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