OmniSciDB  b28c0d5765
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ParserWrapper.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
23 #include "ParserWrapper.h"
24 #include "Shared/measure.h"
25 
26 #include <boost/algorithm/string.hpp>
27 
28 using namespace std;
29 
30 namespace { // anonymous namespace
31 
32 const std::string explain_str = {"explain"};
33 const std::string calcite_explain_str = {"explain calcite"};
34 const std::string optimized_explain_str = {"explain optimized"};
35 const std::string plan_explain_str = {"explain plan"};
36 
37 } // namespace
38 
39 ExplainInfo::ExplainInfo(std::string query_string) {
40  // sets explain_type_ and caches a trimmed actual_query_
41  explain_type_ = ExplainType::None;
42  actual_query_ = "";
43 
44  if (boost::istarts_with(query_string, explain_str)) {
45  if (boost::istarts_with(query_string, calcite_explain_str)) {
46  actual_query_ = boost::trim_copy(query_string.substr(calcite_explain_str.size()));
47  explain_type_ = ExplainType::Calcite;
48  } else if (boost::istarts_with(query_string, optimized_explain_str)) {
49  actual_query_ = boost::trim_copy(query_string.substr(optimized_explain_str.size()));
50  explain_type_ = ExplainType::OptimizedIR;
51  } else if (boost::istarts_with(query_string, plan_explain_str)) {
52  actual_query_ = boost::trim_copy(query_string.substr(plan_explain_str.size()));
53  explain_type_ = ExplainType::ExecutionPlan;
54  } else {
55  actual_query_ = boost::trim_copy(query_string.substr(explain_str.size()));
56  explain_type_ = ExplainType::IR;
57  }
58  // override condition: ddl, update_dml
59  ParserWrapper inner{actual_query_};
60  if (inner.is_ddl || inner.is_update_dml) {
61  explain_type_ = ExplainType::Other;
62  }
63  }
64 }
65 
66 const std::vector<std::string> ParserWrapper::ddl_cmd = {"ARCHIVE",
67  "ALTER",
68  "COPY",
69  "CREATE",
70  "DROP",
71  "DUMP",
72  "GRANT",
73  "KILL",
74  "OPTIMIZE",
75  "REFRESH",
76  "RENAME",
77  "RESTORE",
78  "REVOKE",
79  "SHOW",
80  "TRUNCATE",
81  "REASSIGN",
82  "VALIDATE",
83  "CLEAR"};
84 
85 const std::vector<std::string> ParserWrapper::update_dml_cmd = {"INSERT",
86  "DELETE",
87  "UPDATE"};
88 
89 namespace {
90 
91 void validate_no_leading_comments(const std::string& query_str) {
92  if (boost::starts_with(query_str, "--") || boost::starts_with(query_str, "//") ||
93  boost::starts_with(query_str, "/*")) {
94  throw std::runtime_error(
95  "SQL statements starting with comments are currently not allowed.");
96  }
97 }
98 
99 const std::string optimize_str = {"optimize"};
100 const std::string validate_str = {"validate"};
101 
102 } // namespace
103 
104 ParserWrapper::ParserWrapper(std::string query_string) {
105  validate_no_leading_comments(query_string);
106 
107  is_other_explain = ExplainInfo(query_string).isOtherExplain();
108 
109  query_type_ = QueryType::Read;
110  for (std::string ddl : ddl_cmd) {
111  if (boost::istarts_with(query_string, ddl)) {
112  query_type_ = QueryType::SchemaWrite;
113  is_ddl = true;
114 
115  if (ddl == "CREATE") {
116  boost::regex ctas_regex{
117  R"(CREATE\s+(TEMPORARY\s+|\s*)+TABLE.*(\"|\s)AS(\(|\s)+(SELECT|WITH).*)",
118  boost::regex::extended | boost::regex::icase};
119  if (boost::regex_match(query_string, ctas_regex)) {
120  is_ctas = true;
121  }
122  } else if (ddl == "COPY") {
123  is_copy = true;
124  // now check if it is COPY TO
125  boost::regex copy_to{R"(COPY\s*\(([^#])(.+)\)\s+TO\s+.*)",
126  boost::regex::extended | boost::regex::icase};
127  if (boost::regex_match(query_string, copy_to)) {
128  query_type_ = QueryType::Read;
129  is_copy_to = true;
130  } else {
131  query_type_ = QueryType::Write;
132  }
133  } else if (ddl == "SHOW") {
134  query_type_ = QueryType::SchemaRead;
135  } else if (ddl == "KILL") {
136  query_type_ = QueryType::Unknown;
137  } else if (ddl == "VALIDATE") {
138  query_type_ = QueryType::Unknown;
139  // needs to execute in a different context from other DDL
140  is_validate = true;
141  } else if (ddl == "ALTER") {
142  boost::regex alter_system_regex{R"(ALTER\s+(SYSTEM|SESSION).*)",
143  boost::regex::extended | boost::regex::icase};
144  if (boost::regex_match(query_string, alter_system_regex)) {
145  query_type_ = QueryType::Unknown;
146  }
147  } else if (ddl == "ARCHIVE" || ddl == "DUMP") {
148  query_type_ = QueryType::SchemaRead;
149  } else if (ddl == "REFRESH") {
150  is_refresh = true;
151  }
152  return;
153  }
154  }
155 
156  for (int i = 0; i < update_dml_cmd.size(); i++) {
157  is_update_dml = boost::istarts_with(query_string, ParserWrapper::update_dml_cmd[i]);
158  if (is_update_dml) {
159  query_type_ = QueryType::Write;
160  dml_type_ = (DMLType)(i);
161  break;
162  }
163  }
164 
165  if (dml_type_ == DMLType::Insert) {
166  boost::regex itas_regex{R"(INSERT\s+INTO\s+.*(\s+|\(|\")SELECT(\s|\(|\").*)",
167  boost::regex::extended | boost::regex::icase};
168  if (boost::regex_match(query_string, itas_regex)) {
169  is_itas = true;
170  return;
171  }
172  }
173 }
174 
Classes used to wrap parser calls for calcite redirection.
virtual ~ParserWrapper()
void validate_no_leading_comments(const std::string &query_str)
bool isOtherExplain() const
Definition: ParserWrapper.h:60
static const std::vector< std::string > ddl_cmd
static const std::vector< std::string > update_dml_cmd
ParserWrapper(std::string query_string)