OmniSciDB  06b3bd477c
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CalciteAdapter.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, 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 
17 #include "CalciteAdapter.h"
18 
19 #include <boost/algorithm/string/predicate.hpp>
20 #include <boost/algorithm/string/replace.hpp>
21 #include <boost/regex.hpp>
22 
23 #include "Shared/StringTransform.h"
24 
25 namespace {
26 
27 std::string pg_shim_impl(const std::string& query) {
28  auto result = query;
29  {
30  static const boost::regex unnest_expr{R"((\s+|,)(unnest)\s*\()",
31  boost::regex::extended | boost::regex::icase};
32  apply_shim(result, unnest_expr, [](std::string& result, const boost::smatch& what) {
33  result.replace(what.position(), what.length(), what[1] + "PG_UNNEST(");
34  });
35  }
36  {
37  static const boost::regex cast_true_expr{
38  R"(CAST\s*\(\s*'t'\s+AS\s+boolean\s*\))",
39  boost::regex::extended | boost::regex::icase};
40  apply_shim(
41  result, cast_true_expr, [](std::string& result, const boost::smatch& what) {
42  result.replace(what.position(), what.length(), "true");
43  });
44  }
45  {
46  static const boost::regex cast_false_expr{
47  R"(CAST\s*\(\s*'f'\s+AS\s+boolean\s*\))",
48  boost::regex::extended | boost::regex::icase};
49  apply_shim(
50  result, cast_false_expr, [](std::string& result, const boost::smatch& what) {
51  result.replace(what.position(), what.length(), "false");
52  });
53  }
54  {
55  static const boost::regex ilike_expr{
56  R"((\s+|\()((?!\()[^\s]+)\s+ilike\s+('(?:[^']+|'')+')(\s+escape(\s+('[^']+')))?)",
57  boost::regex::perl | boost::regex::icase};
58  apply_shim(result, ilike_expr, [](std::string& result, const boost::smatch& what) {
59  std::string esc = what[6];
60  result.replace(what.position(),
61  what.length(),
62  what[1] + "PG_ILIKE(" + what[2] + ", " + what[3] +
63  (esc.empty() ? "" : ", " + esc) + ")");
64  });
65  }
66  {
67  static const boost::regex regexp_expr{
68  R"((\s+)([^\s]+)\s+REGEXP\s+('(?:[^']+|'')+')(\s+escape(\s+('[^']+')))?)",
69  boost::regex::perl | boost::regex::icase};
70  apply_shim(result, regexp_expr, [](std::string& result, const boost::smatch& what) {
71  std::string esc = what[6];
72  result.replace(what.position(),
73  what.length(),
74  what[1] + "REGEXP_LIKE(" + what[2] + ", " + what[3] +
75  (esc.empty() ? "" : ", " + esc) + ")");
76  });
77  }
78  {
79  // Comparison operator needed to distinguish from other uses of ALL (e.g. UNION ALL)
80  static const boost::regex quant_expr{R"(([<=>]\s*)(any|all)\s+([^(\s|;)]+))",
81  boost::regex::extended | boost::regex::icase};
82  apply_shim(result, quant_expr, [](std::string& result, const boost::smatch& what) {
83  auto const quant_fname = boost::iequals(what[2], "any") ? "PG_ANY(" : "PG_ALL(";
84  result.replace(
85  what.position(), what.length(), what[1] + quant_fname + what[3] + ')');
86  });
87  }
88  {
89  static const boost::regex immediate_cast_expr{
90  R"(TIMESTAMP\(([0369])\)\s+('[^']+'))",
91  boost::regex::extended | boost::regex::icase};
92  apply_shim(
93  result, immediate_cast_expr, [](std::string& result, const boost::smatch& what) {
94  result.replace(what.position(),
95  what.length(),
96  "CAST(" + what[2] + " AS TIMESTAMP(" + what[1] + "))");
97  });
98  }
99  {
100  static const boost::regex timestampadd_expr{
101  R"(DATE(ADD|DIFF|PART|_TRUNC)\s*\(\s*(\w+)\s*,)",
102  boost::regex::extended | boost::regex::icase};
103  apply_shim(
104  result, timestampadd_expr, [](std::string& result, const boost::smatch& what) {
105  result.replace(
106  what.position(), what.length(), "DATE" + what[1] + "('" + what[2] + "',");
107  });
108  }
109 
110  {
111  static const boost::regex pg_extract_expr{
112  R"(PG_EXTRACT\s*\(\s*(\w+)\s*,)", boost::regex::extended | boost::regex::icase};
113  apply_shim(
114  result, pg_extract_expr, [](std::string& result, const boost::smatch& what) {
115  result.replace(what.position(), what.length(), "PG_EXTRACT('" + what[1] + "',");
116  });
117 
118  static const boost::regex extract_expr_quoted{
119  R"(extract\s*\(\s*'(\w+)'\s+from\s+(.+)\))",
120  boost::regex::extended | boost::regex::icase};
121  apply_shim(
122  result, extract_expr_quoted, [](std::string& result, const boost::smatch& what) {
123  result.replace(what.position(),
124  what.length(),
125  "PG_EXTRACT('" + what[1] + "', " + what[2] + ")");
126  });
127 
128  static const boost::regex extract_expr{R"(extract\s*\(\s*(\w+)\s+from\s+(.+)\))",
129  boost::regex::extended | boost::regex::icase};
130  apply_shim(result, extract_expr, [](std::string& result, const boost::smatch& what) {
131  result.replace(what.position(),
132  what.length(),
133  "PG_EXTRACT('" + what[1] + "', " + what[2] + ")");
134  });
135  }
136 
137  {
138  static const boost::regex date_trunc_expr{
139  R"(([^_])date_trunc\s*)", boost::regex::extended | boost::regex::icase};
140  apply_shim(
141  result, date_trunc_expr, [](std::string& result, const boost::smatch& what) {
142  result.replace(what.position(), what.length(), what[1] + "PG_DATE_TRUNC");
143  });
144  }
145  {
146  static const boost::regex timestampadd_expr_quoted{
147  R"(TIMESTAMP(ADD|DIFF)\s*\(\s*'(\w+)'\s*,)",
148  boost::regex::extended | boost::regex::icase};
150  timestampadd_expr_quoted,
151  [](std::string& result, const boost::smatch& what) {
152  result.replace(what.position(),
153  what.length(),
154  "DATE" + what[1] + "('" + what[2] + "',");
155  });
156  static const boost::regex timestampadd_expr{
157  R"(TIMESTAMP(ADD|DIFF)\s*\(\s*(\w+)\s*,)",
158  boost::regex::extended | boost::regex::icase};
159  apply_shim(
160  result, timestampadd_expr, [](std::string& result, const boost::smatch& what) {
161  result.replace(
162  what.position(), what.length(), "DATE" + what[1] + "('" + what[2] + "',");
163  });
164  }
165  {
166  static const boost::regex us_timestamp_cast_expr{
167  R"(CAST\s*\(\s*('[^']+')\s*AS\s*TIMESTAMP\(6\)\s*\))",
168  boost::regex::extended | boost::regex::icase};
170  us_timestamp_cast_expr,
171  [](std::string& result, const boost::smatch& what) {
172  result.replace(
173  what.position(), what.length(), "usTIMESTAMP(" + what[1] + ")");
174  });
175  }
176  {
177  static const boost::regex ns_timestamp_cast_expr{
178  R"(CAST\s*\(\s*('[^']+')\s*AS\s*TIMESTAMP\(9\)\s*\))",
179  boost::regex::extended | boost::regex::icase};
181  ns_timestamp_cast_expr,
182  [](std::string& result, const boost::smatch& what) {
183  result.replace(
184  what.position(), what.length(), "nsTIMESTAMP(" + what[1] + ")");
185  });
186  }
187  {
188  static const boost::regex corr_expr{R"((\s+|,|\()(corr)\s*\()",
189  boost::regex::extended | boost::regex::icase};
190  apply_shim(result, corr_expr, [](std::string& result, const boost::smatch& what) {
191  result.replace(what.position(), what.length(), what[1] + "CORRELATION(");
192  });
193  }
194  {
195  try {
196  // the geography regex pattern is expensive and can sometimes run out of stack space
197  // on long queries. Treat it separately from the other shims.
198  static const boost::regex cast_to_geography_expr{
199  R"(CAST\s*\(\s*(((?!geography).)+)\s+AS\s+geography\s*\))",
200  boost::regex::perl | boost::regex::icase};
202  cast_to_geography_expr,
203  [](std::string& result, const boost::smatch& what) {
204  result.replace(what.position(),
205  what.length(),
206  "CastToGeography(" + what[1] + ")");
207  });
208  } catch (const std::exception& e) {
209  LOG(WARNING) << "Error apply geography cast shim: " << e.what()
210  << "\nContinuing query parse...";
211  }
212  }
213  return result;
214 }
215 
216 } // namespace
217 
218 std::string pg_shim(const std::string& query) {
219  try {
220  return pg_shim_impl(query);
221  } catch (const std::exception& e) {
222  LOG(WARNING) << "Error applying shim: " << e.what() << "\nContinuing query parse...";
223  // boost::regex throws an exception about the complexity of matching when
224  // the wrong type of quotes are used or they're mismatched. Let the query
225  // through unmodified, the parser will throw a much more informative error.
226  }
227  return query;
228 }
#define LOG(tag)
Definition: Logger.h:188
std::string pg_shim_impl(const std::string &query)
void apply_shim(std::string &result, const boost::regex &reg_expr, const std::function< void(std::string &, const boost::smatch &)> &shim_fn)
std::string pg_shim(const std::string &query)