OmniSciDB  a5dc49c757
 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 calcite_explain_detailed_str = {"explain calcite detailed"};
35 const std::string optimized_explain_str = {"explain optimized"};
36 const std::string plan_explain_str = {"explain plan"};
37 const std::string plan_explain_detailed_str = {"explain plan detailed"};
38 
39 } // namespace
40 
41 ExplainInfo::ExplainInfo(std::string query_string) {
42  // sets explain_type_ and caches a trimmed actual_query_
43  explain_type_ = ExplainType::None;
44  actual_query_ = "";
45 
46  if (boost::istarts_with(query_string, explain_str)) {
47  if (boost::istarts_with(query_string, calcite_explain_str)) {
48  if (boost::istarts_with(query_string, calcite_explain_detailed_str)) {
49  explain_type_ = ExplainType::CalciteDetail;
50  actual_query_ =
51  boost::trim_copy(query_string.substr(calcite_explain_detailed_str.size()));
52  } else {
53  actual_query_ = boost::trim_copy(query_string.substr(calcite_explain_str.size()));
54  explain_type_ = ExplainType::Calcite;
55  }
56  } else if (boost::istarts_with(query_string, optimized_explain_str)) {
57  actual_query_ = boost::trim_copy(query_string.substr(optimized_explain_str.size()));
58  explain_type_ = ExplainType::OptimizedIR;
59  } else if (boost::istarts_with(query_string, plan_explain_str)) {
60  if (boost::istarts_with(query_string, plan_explain_detailed_str)) {
61  actual_query_ =
62  boost::trim_copy(query_string.substr(plan_explain_detailed_str.size()));
63  verbose_ = true;
64  } else {
65  actual_query_ = boost::trim_copy(query_string.substr(plan_explain_str.size()));
66  }
67  explain_type_ = ExplainType::ExecutionPlan;
68  } else {
69  actual_query_ = boost::trim_copy(query_string.substr(explain_str.size()));
70  explain_type_ = ExplainType::IR;
71  }
72  // override condition: ddl, update_dml
73  ParserWrapper inner{actual_query_};
74  if (inner.is_ddl || inner.is_update_dml) {
75  explain_type_ = ExplainType::Other;
76  }
77  }
78 }
79 
80 const std::vector<std::string> ParserWrapper::ddl_cmd = {
81  "ARCHIVE", "ALTER", "COPY", "CREATE", "DROP", "DUMP", "EVALUATE",
82  "GRANT", "KILL", "OPTIMIZE", "REFRESH", "RENAME", "RESTORE", "REVOKE",
83  "SHOW", "TRUNCATE", "REASSIGN", "VALIDATE", "CLEAR", "PAUSE", "RESUME"};
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:79
static const std::vector< std::string > ddl_cmd
static const std::vector< std::string > update_dml_cmd
ParserWrapper(std::string query_string)