OmniSciDB  fe05a0c208
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RelAlgTranslatorGeo.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 
18 
19 #include <memory>
20 #include <vector>
21 
22 #include "Geospatial/Compression.h"
23 #include "Geospatial/Types.h"
25 
26 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoColumn(
27  const RexInput* rex_input,
28  SQLTypeInfo& ti,
29  const bool with_bounds,
30  const bool with_render_group,
31  const bool expand_geo_col) const {
32  std::vector<std::shared_ptr<Analyzer::Expr>> args;
33  const auto source = rex_input->getSourceNode();
34  const auto it_rte_idx = input_to_nest_level_.find(source);
35  CHECK(it_rte_idx != input_to_nest_level_.end());
36  const int rte_idx = it_rte_idx->second;
37  const auto& in_metainfo = source->getOutputMetainfo();
38 
39  int32_t table_id{0};
40  int column_id{-1};
41  const auto scan_source = dynamic_cast<const RelScan*>(source);
42  if (scan_source) {
43  // We're at leaf (scan) level and not supposed to have input metadata,
44  // the name and type information come directly from the catalog.
45  CHECK(in_metainfo.empty());
46 
47  const auto td = scan_source->getTableDescriptor();
48  table_id = td->tableId;
49 
50  const auto gcd = cat_.getMetadataForColumnBySpi(table_id, rex_input->getIndex() + 1);
51  CHECK(gcd);
52  ti = gcd->columnType;
53  column_id = gcd->columnId;
54 
55  } else {
56  // Likely backed by a temp table. Read the table ID from the source node and negate it
57  // (see RelAlgTranslator::translateInput)
58  table_id = -source->getId();
59 
60  CHECK(!expand_geo_col);
61  if (with_bounds || with_render_group) {
62  throw QueryNotSupported(
63  "Geospatial columns not yet supported in intermediate results.");
64  }
65 
66  CHECK(!in_metainfo.empty());
67  CHECK_GE(rte_idx, 0);
68  column_id = rex_input->getIndex();
69  CHECK_LT(static_cast<size_t>(column_id), in_metainfo.size());
70  ti = in_metainfo[column_id].get_type_info();
71  }
72  CHECK(IS_GEO(ti.get_type()));
73 
74  // Return geo column reference. The geo column may be expanded if required for extension
75  // function arguments. Otherwise, the geo column reference will be translated into
76  // physical columns as required. Bounds column will be added if present and requested.
77  if (expand_geo_col) {
78  for (auto i = 0; i < ti.get_physical_coord_cols(); i++) {
79  const auto pcd = cat_.getMetadataForColumnBySpi(
80  table_id, SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(), i + 1));
81  auto pcol_ti = pcd->columnType;
82  args.push_back(std::make_shared<Analyzer::ColumnVar>(
83  pcol_ti, table_id, pcd->columnId, rte_idx));
84  }
85  } else {
86  args.push_back(
87  std::make_shared<Analyzer::ColumnVar>(ti, table_id, column_id, rte_idx));
88  }
89  if (with_bounds && ti.has_bounds()) {
90  const auto bounds_cd = cat_.getMetadataForColumnBySpi(
91  table_id,
93  ti.get_physical_coord_cols() + 1));
94  auto bounds_ti = bounds_cd->columnType;
95  args.push_back(std::make_shared<Analyzer::ColumnVar>(
96  bounds_ti, table_id, bounds_cd->columnId, rte_idx));
97  }
98  if (with_render_group && ti.has_render_group()) {
99  const auto render_group_cd = cat_.getMetadataForColumnBySpi(
100  table_id,
101  SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(),
102  ti.get_physical_coord_cols() + 2));
103  auto render_group_ti = render_group_cd->columnType;
104  args.push_back(std::make_shared<Analyzer::ColumnVar>(
105  render_group_ti, table_id, render_group_cd->columnId, rte_idx));
106  }
107  return args;
108 }
109 
110 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoLiteral(
111  const RexLiteral* rex_literal,
112  SQLTypeInfo& ti,
113  bool with_bounds) const {
114  CHECK(rex_literal);
115  if (rex_literal->getType() != kTEXT) {
116  throw std::runtime_error("Geo literals must be strings");
117  }
118 
119  // TODO: use geo conversion here
120  const auto e = translateLiteral(rex_literal);
121  auto wkt = std::dynamic_pointer_cast<Analyzer::Constant>(e);
122  CHECK(wkt);
123  std::vector<double> coords;
124  std::vector<double> bounds;
125  std::vector<int> ring_sizes;
126  std::vector<int> poly_rings;
127  int32_t srid = ti.get_output_srid();
129  *wkt->get_constval().stringval, ti, coords, bounds, ring_sizes, poly_rings)) {
130  throw QueryNotSupported("Could not read geometry from text");
131  }
133  ti.set_input_srid(srid);
134  ti.set_output_srid(srid);
135  // Compress geo literals by default
136  if (srid == 4326) {
138  ti.set_comp_param(32);
139  }
140 
141  std::vector<std::shared_ptr<Analyzer::Expr>> args;
142 
143  std::vector<uint8_t> compressed_coords = Geospatial::compress_coords(coords, ti);
144  std::list<std::shared_ptr<Analyzer::Expr>> compressed_coords_exprs;
145  for (auto cc : compressed_coords) {
146  Datum d;
147  d.tinyintval = cc;
148  auto e = makeExpr<Analyzer::Constant>(kTINYINT, false, d);
149  compressed_coords_exprs.push_back(e);
150  }
151  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
152  arr_ti.set_subtype(kTINYINT);
153  arr_ti.set_size(compressed_coords.size() * sizeof(int8_t));
154  arr_ti.set_compression(ti.get_compression());
155  arr_ti.set_comp_param((ti.get_compression() == kENCODING_GEOINT) ? 32 : 64);
156  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, compressed_coords_exprs));
157 
158  auto lit_type = ti.get_type();
159  if (lit_type == kPOLYGON || lit_type == kMULTIPOLYGON) {
160  // ring sizes
161  std::list<std::shared_ptr<Analyzer::Expr>> ring_size_exprs;
162  for (auto c : ring_sizes) {
163  Datum d;
164  d.intval = c;
165  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
166  ring_size_exprs.push_back(e);
167  }
168  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
169  arr_ti.set_subtype(kINT);
170  arr_ti.set_size(ring_sizes.size() * sizeof(int32_t));
171  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, ring_size_exprs));
172 
173  // poly rings
174  if (lit_type == kMULTIPOLYGON) {
175  std::list<std::shared_ptr<Analyzer::Expr>> poly_rings_exprs;
176  for (auto c : poly_rings) {
177  Datum d;
178  d.intval = c;
179  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
180  poly_rings_exprs.push_back(e);
181  }
182  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
183  arr_ti.set_subtype(kINT);
184  arr_ti.set_size(poly_rings.size() * sizeof(int32_t));
185  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, poly_rings_exprs));
186  }
187  }
188 
189  if (with_bounds && ti.has_bounds()) {
190  // bounds
191  std::list<std::shared_ptr<Analyzer::Expr>> bounds_exprs;
192  for (auto b : bounds) {
193  Datum d;
194  d.doubleval = b;
195  auto e = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
196  bounds_exprs.push_back(e);
197  }
198  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
199  arr_ti.set_subtype(kDOUBLE);
200  arr_ti.set_size(bounds.size() * sizeof(double));
201  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, bounds_exprs));
202  }
203 
204  return args;
205 }
206 
207 namespace {
208 
209 std::string suffix(SQLTypes type) {
210  if (type == kPOINT) {
211  return std::string("_Point");
212  }
213  if (type == kLINESTRING) {
214  return std::string("_LineString");
215  }
216  if (type == kPOLYGON) {
217  return std::string("_Polygon");
218  }
219  if (type == kMULTIPOLYGON) {
220  return std::string("_MultiPolygon");
221  }
222  throw QueryNotSupported("Unsupported argument type");
223 }
224 
226  CHECK(geo);
227  switch (geo->getType()) {
229  return kPOINT;
230  }
232  return kLINESTRING;
233  }
235  return kPOLYGON;
236  }
238  return kMULTIPOLYGON;
239  }
240  default:
241  UNREACHABLE();
242  return kNULLT;
243  }
244 }
245 
246 } // namespace
247 
248 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoFunctionArg(
249  const RexScalar* rex_scalar,
250  SQLTypeInfo& arg_ti,
251  int32_t& lindex,
252  const bool with_bounds,
253  const bool with_render_group,
254  const bool expand_geo_col,
255  const bool is_projection,
256  const bool use_geo_expressions) const {
257  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
258 
259  const auto rex_input = dynamic_cast<const RexInput*>(rex_scalar);
260  if (rex_input) {
261  const auto input = translateInput(rex_input);
262  const auto column = dynamic_cast<const Analyzer::ColumnVar*>(input.get());
263  if (!column || !column->get_type_info().is_geometry()) {
264  throw QueryNotSupported("Geo function is expecting a geo column argument");
265  }
266  if (use_geo_expressions) {
267  return {makeExpr<Analyzer::GeoColumnVar>(column, with_bounds, with_render_group)};
268  }
269  return translateGeoColumn(
270  rex_input, arg_ti, with_bounds, with_render_group, expand_geo_col);
271  }
272  const auto rex_function = dynamic_cast<const RexFunctionOperator*>(rex_scalar);
273  if (rex_function) {
274  if (rex_function->getName() == "ST_Transform"sv) {
275  CHECK_EQ(size_t(2), rex_function->size());
276  const auto rex_scalar0 =
277  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
278  if (!rex_scalar0) {
279  throw QueryNotSupported(rex_function->getName() + ": unexpected first argument");
280  }
281 
282  const auto rex_literal =
283  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
284  if (!rex_literal) {
285  throw QueryNotSupported(rex_function->getName() +
286  ": second argument is expected to be a literal");
287  }
288  const auto e = translateLiteral(rex_literal);
289  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
290  if (!ce || !e->get_type_info().is_integer()) {
291  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
292  }
293  int32_t srid = 0;
294  if (e->get_type_info().get_type() == kSMALLINT) {
295  srid = static_cast<int32_t>(ce->get_constval().smallintval);
296  } else if (e->get_type_info().get_type() == kTINYINT) {
297  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
298  } else if (e->get_type_info().get_type() == kINT) {
299  srid = static_cast<int32_t>(ce->get_constval().intval);
300  } else {
301  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
302  }
303  if (srid != 900913) {
304  throw QueryNotSupported(rex_function->getName() + ": unsupported output SRID " +
305  std::to_string(srid));
306  }
307  arg_ti.set_output_srid(srid); // Forward output srid down to argument translation
308  auto arg0 = translateGeoFunctionArg(rex_scalar0,
309  arg_ti,
310  lindex,
311  with_bounds,
312  with_render_group,
313  expand_geo_col,
314  is_projection,
315  use_geo_expressions);
316 
317  if (arg_ti.get_input_srid() > 0) {
318  if (arg_ti.get_input_srid() != 4326) {
319  throw QueryNotSupported(rex_function->getName() + ": unsupported input SRID " +
320  std::to_string(arg_ti.get_input_srid()));
321  }
322  arg_ti.set_output_srid(
323  srid); // We have a valid input SRID, register the output SRID for transform
324  } else {
325  throw QueryNotSupported(rex_function->getName() +
326  ": unexpected input SRID, unable to transform");
327  }
328  return arg0;
329  } else if (func_resolve(
330  rex_function->getName(), "ST_GeomFromText"sv, "ST_GeogFromText"sv)) {
331  CHECK(rex_function->size() == size_t(1) || rex_function->size() == size_t(2));
332  if (use_geo_expressions) {
333  int32_t srid = 0;
334  if (rex_function->size() == 2) {
335  // user supplied srid
336  const auto rex_literal =
337  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
338  if (!rex_literal) {
339  throw QueryNotSupported(rex_function->getName() +
340  ": second argument is expected to be a literal");
341  }
342  const auto e = translateLiteral(rex_literal);
343  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
344  if (!ce || !e->get_type_info().is_integer()) {
345  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
346  }
347  if (e->get_type_info().get_type() == kSMALLINT) {
348  srid = static_cast<int32_t>(ce->get_constval().smallintval);
349  } else if (e->get_type_info().get_type() == kTINYINT) {
350  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
351  } else if (e->get_type_info().get_type() == kINT) {
352  srid = static_cast<int32_t>(ce->get_constval().intval);
353  } else {
354  throw QueryNotSupported(rex_function->getName() + " expecting integer SRID");
355  }
356  if (srid != 0 && srid != 4326 && srid != 900913) {
357  throw QueryNotSupported(rex_function->getName() + ": unsupported SRID " +
358  std::to_string(srid));
359  }
360  }
361  arg_ti.set_input_srid(srid); // Input SRID
362  // leave the output srid unset in case a transform was above us
363  // arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
364 
365  if (rex_function->getName() == "ST_GeogFromText"sv) {
366  arg_ti.set_subtype(kGEOGRAPHY);
367  }
368 
369  auto func_args = translateGeoFunctionArg(rex_function->getOperand(0),
370  arg_ti,
371  lindex,
372  with_bounds,
373  with_render_group,
374  expand_geo_col,
375  is_projection,
376  use_geo_expressions);
377  // TODO: maybe this can be removed?
378  CHECK_GE(func_args.size(), size_t(1));
379  if (auto geo_const =
380  dynamic_cast<const Analyzer::GeoConstant*>(func_args.front().get())) {
381  arg_ti = geo_const->get_type_info();
382  }
383  return func_args;
384  }
385 
386  // First - register srid, then send it to geo literal translation
387  int32_t srid = 0;
388  if (rex_function->size() == 2) {
389  const auto rex_literal =
390  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
391  if (!rex_literal) {
392  throw QueryNotSupported(rex_function->getName() +
393  ": second argument is expected to be a literal");
394  }
395  const auto e = translateLiteral(rex_literal);
396  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
397  if (!ce || !e->get_type_info().is_integer()) {
398  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
399  }
400  if (e->get_type_info().get_type() == kSMALLINT) {
401  srid = static_cast<int32_t>(ce->get_constval().smallintval);
402  } else if (e->get_type_info().get_type() == kTINYINT) {
403  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
404  } else if (e->get_type_info().get_type() == kINT) {
405  srid = static_cast<int32_t>(ce->get_constval().intval);
406  } else {
407  throw QueryNotSupported(rex_function->getName() + " expecting integer SRID");
408  }
409  if (srid != 0 && srid != 4326 && srid != 900913) {
410  throw QueryNotSupported(rex_function->getName() + ": unsupported SRID " +
411  std::to_string(srid));
412  }
413  }
414  arg_ti.set_input_srid(srid); // Input SRID
415  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
416 
417  const auto rex_literal =
418  dynamic_cast<const RexLiteral*>(rex_function->getOperand(0));
419  if (!rex_literal) {
420  throw QueryNotSupported(rex_function->getName() +
421  " expects a string literal as first argument");
422  }
423  auto arg0 = translateGeoLiteral(rex_literal, arg_ti, with_bounds);
424  arg_ti.set_subtype((rex_function->getName() == "ST_GeogFromText"sv) ? kGEOGRAPHY
425  : kGEOMETRY);
426  return arg0;
427  } else if (rex_function->getName() == "ST_PointN"sv) {
428  if (use_geo_expressions) {
429  std::vector<std::shared_ptr<Analyzer::Expr>> args;
430  for (size_t i = 0; i < rex_function->size(); i++) {
431  // TODO: remove
432  auto constant = dynamic_cast<const RexLiteral*>(rex_function->getOperand(i));
433  if (constant && IS_GEO(constant->getType())) {
434  const auto arg_exprs = translateGeoFunctionArg(constant,
435  arg_ti,
436  lindex,
437  with_bounds,
438  with_render_group,
439  expand_geo_col,
440  is_projection,
441  use_geo_expressions);
442  CHECK_EQ(arg_exprs.size(), size_t(1));
443  args.push_back(arg_exprs.front());
444  } else if (auto function = dynamic_cast<const RexFunctionOperator*>(
445  rex_function->getOperand(i))) {
446  const auto arg_exprs = translateGeoFunctionArg(function,
447  arg_ti,
448  lindex,
449  with_bounds,
450  with_render_group,
451  expand_geo_col,
452  is_projection,
453  use_geo_expressions);
454  CHECK_EQ(arg_exprs.size(), size_t(1));
455  args.push_back(arg_exprs.front());
456  } else {
457  args.push_back(translateScalarRex(rex_function->getOperand(i)));
458  }
459  }
460  arg_ti.set_type(kPOINT);
461  arg_ti.set_notnull(false); // can return null if out of bounds
462  return {makeExpr<Analyzer::GeoOperator>(arg_ti, rex_function->getName(), args)};
463  }
464  CHECK_EQ(size_t(2), rex_function->size());
465  const auto rex_scalar0 =
466  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
467  if (!rex_scalar0) {
468  throw QueryNotSupported(rex_function->getName() +
469  ": expects scalar as first argument");
470  }
471  auto arg0 = translateGeoFunctionArg(
472  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
473  if (arg_ti.get_type() != kLINESTRING) {
474  throw QueryNotSupported(rex_function->getName() +
475  " expects LINESTRING as first argument");
476  }
477  const auto rex_literal =
478  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
479  if (!rex_literal) {
480  throw QueryNotSupported(rex_function->getName() +
481  ": second argument is expected to be a literal");
482  }
483  const auto e = translateLiteral(rex_literal);
484  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
485  if (!ce || !e->get_type_info().is_integer()) {
486  throw QueryNotSupported(rex_function->getName() +
487  ": expecting integer index as second argument");
488  }
489  int32_t index = 0;
490  if (e->get_type_info().get_type() == kSMALLINT) {
491  index = static_cast<int32_t>(ce->get_constval().smallintval);
492  } else if (e->get_type_info().get_type() == kTINYINT) {
493  index = static_cast<int32_t>(ce->get_constval().tinyintval);
494  } else if (e->get_type_info().get_type() == kINT) {
495  index = static_cast<int32_t>(ce->get_constval().intval);
496  } else {
497  throw QueryNotSupported(rex_function->getName() + " expecting integer index");
498  }
499  if (lindex != 0) {
500  throw QueryNotSupported(rex_function->getName() +
501  ": LINESTRING is already indexed");
502  }
503  if (index == 0) {
504  throw QueryNotSupported(rex_function->getName() + ": invalid index");
505  }
506  lindex = index;
507  return arg0;
508  } else if (rex_function->getName() == "ST_StartPoint"sv) {
509  CHECK_EQ(size_t(1), rex_function->size());
510  const auto rex_scalar0 =
511  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
512  if (!rex_scalar0) {
513  throw QueryNotSupported(rex_function->getName() +
514  ": expects scalar as first argument");
515  }
516  auto arg0 = translateGeoFunctionArg(
517  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
518  if (arg_ti.get_type() != kLINESTRING) {
519  throw QueryNotSupported(rex_function->getName() +
520  " expects LINESTRING as first argument");
521  }
522  if (lindex != 0) {
523  throw QueryNotSupported(rex_function->getName() +
524  ": LINESTRING is already indexed");
525  }
526  lindex = 1;
527  return arg0;
528  } else if (rex_function->getName() == "ST_EndPoint"sv) {
529  if (use_geo_expressions) {
530  std::vector<std::shared_ptr<Analyzer::Expr>> args;
531  CHECK_EQ(size_t(1), rex_function->size());
532  // TODO: remove
533  auto constant = dynamic_cast<const RexLiteral*>(rex_function->getOperand(0));
534  if (constant && IS_GEO(constant->getType())) {
535  const auto arg_exprs = translateGeoFunctionArg(constant,
536  arg_ti,
537  lindex,
538  with_bounds,
539  with_render_group,
540  expand_geo_col,
541  is_projection,
542  use_geo_expressions);
543  CHECK_EQ(arg_exprs.size(), size_t(1));
544  args.push_back(arg_exprs.front());
545  } else if (auto function = dynamic_cast<const RexFunctionOperator*>(
546  rex_function->getOperand(0))) {
547  const auto arg_exprs = translateGeoFunctionArg(function,
548  arg_ti,
549  lindex,
550  with_bounds,
551  with_render_group,
552  expand_geo_col,
553  is_projection,
554  use_geo_expressions);
555  CHECK_EQ(arg_exprs.size(), size_t(1));
556  args.push_back(arg_exprs.front());
557  } else {
558  args.push_back(translateScalarRex(rex_function->getOperand(0)));
559  }
560  arg_ti.set_type(kPOINT);
561  return {makeExpr<Analyzer::GeoOperator>(arg_ti, rex_function->getName(), args)};
562  }
563  CHECK_EQ(size_t(1), rex_function->size());
564  const auto rex_scalar0 =
565  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
566  if (!rex_scalar0) {
567  throw QueryNotSupported(rex_function->getName() +
568  ": expects scalar as first argument");
569  }
570  auto arg0 = translateGeoFunctionArg(
571  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
572  if (arg_ti.get_type() != kLINESTRING) {
573  throw QueryNotSupported(rex_function->getName() +
574  " expects LINESTRING as first argument");
575  }
576  if (lindex != 0) {
577  throw QueryNotSupported(rex_function->getName() +
578  ": LINESTRING is already indexed");
579  }
580  lindex = -1;
581  return arg0;
582  } else if (rex_function->getName() == "ST_SRID"sv) {
583  CHECK_EQ(size_t(1), rex_function->size());
584  const auto rex_scalar0 =
585  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
586  if (!rex_scalar0) {
587  throw QueryNotSupported(rex_function->getName() +
588  ": expects scalar as first argument");
589  }
590  auto arg0 = translateGeoFunctionArg(
591  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
592  if (!IS_GEO(arg_ti.get_type())) {
593  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
594  }
595  return arg0;
596  } else if (rex_function->getName() == "ST_SetSRID"sv) {
597  CHECK_EQ(size_t(2), rex_function->size());
598  const auto rex_scalar0 =
599  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
600  if (!rex_scalar0) {
601  throw QueryNotSupported(rex_function->getName() +
602  ": expects scalar as first argument");
603  }
604  auto arg0 = translateGeoFunctionArg(rex_scalar0,
605  arg_ti,
606  lindex,
607  with_bounds,
608  with_render_group,
609  expand_geo_col,
610  is_projection);
611  if (!IS_GEO(arg_ti.get_type())) {
612  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
613  }
614  const auto rex_literal =
615  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
616  if (!rex_literal) {
617  throw QueryNotSupported(rex_function->getName() +
618  ": second argument is expected to be a literal");
619  }
620  const auto e = translateLiteral(rex_literal);
621  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
622  if (!ce || !e->get_type_info().is_integer()) {
623  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
624  }
625  int32_t srid = 0;
626  if (e->get_type_info().get_type() == kSMALLINT) {
627  srid = static_cast<int32_t>(ce->get_constval().smallintval);
628  } else if (e->get_type_info().get_type() == kTINYINT) {
629  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
630  } else if (e->get_type_info().get_type() == kINT) {
631  srid = static_cast<int32_t>(ce->get_constval().intval);
632  } else {
633  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
634  }
635  arg_ti.set_input_srid(srid); // Input SRID
636  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
637  return arg0;
638  } else if (rex_function->getName() == "CastToGeography"sv) {
639  CHECK_EQ(size_t(1), rex_function->size());
640  const auto rex_scalar0 =
641  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
642  if (!rex_scalar0) {
643  throw QueryNotSupported(rex_function->getName() +
644  ": expects scalar as first argument");
645  }
646  auto arg0 = translateGeoFunctionArg(
647  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
648  if (!IS_GEO(arg_ti.get_type())) {
649  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
650  }
651  if (arg_ti.get_output_srid() != 4326) {
652  throw QueryNotSupported(rex_function->getName() +
653  " expects geometry with SRID=4326");
654  }
655  arg_ti.set_subtype(kGEOGRAPHY);
656  return arg0;
657  } else if (rex_function->getName() == "ST_Point"sv) {
658  CHECK_EQ(size_t(2), rex_function->size());
659  arg_ti.set_type(kPOINT);
660  arg_ti.set_subtype(kGEOMETRY);
661  arg_ti.set_input_srid(0);
662  arg_ti.set_output_srid(0);
664 
665  auto coord1 = translateScalarRex(rex_function->getOperand(0));
666  auto coord2 = translateScalarRex(rex_function->getOperand(1));
667  auto d_ti = SQLTypeInfo(kDOUBLE, true);
668  auto cast_coord1 = coord1->add_cast(d_ti);
669  auto cast_coord2 = coord2->add_cast(d_ti);
670  // First try to fold to geo literal
671  auto fold1 = fold_expr(cast_coord1.get());
672  auto fold2 = fold_expr(cast_coord2.get());
673  auto const_coord1 = std::dynamic_pointer_cast<Analyzer::Constant>(fold1);
674  auto const_coord2 = std::dynamic_pointer_cast<Analyzer::Constant>(fold2);
675  if (const_coord1 && const_coord2) {
676  CHECK(const_coord1->get_type_info().get_type() == kDOUBLE);
677  CHECK(const_coord2->get_type_info().get_type() == kDOUBLE);
678  std::string wkt = "POINT(" +
679  std::to_string(const_coord1->get_constval().doubleval) + " " +
680  std::to_string(const_coord2->get_constval().doubleval) + ")";
681  RexLiteral rex_literal{wkt, kTEXT, kNULLT, 0, 0, 0, 0};
682  auto args = translateGeoLiteral(&rex_literal, arg_ti, false);
683  CHECK(arg_ti.get_type() == kPOINT);
684  return args;
685  }
686  // Couldn't fold to geo literal, construct on the fly
687  auto da_ti = SQLTypeInfo(kARRAY, true);
688  da_ti.set_subtype(kDOUBLE);
689  da_ti.set_size(16);
690  auto cast_coords = {cast_coord1, cast_coord2};
691  auto is_local_alloca = !is_projection;
692  auto ae = makeExpr<Analyzer::ArrayExpr>(da_ti, cast_coords, false, is_local_alloca);
693  // cast it to tinyint[16]
694  SQLTypeInfo tia_ti = SQLTypeInfo(kARRAY, true);
695  tia_ti.set_subtype(kTINYINT);
696  tia_ti.set_size(16);
697  return {makeExpr<Analyzer::UOper>(tia_ti, false, kCAST, ae)};
698  } else if (rex_function->getName() == "ST_Centroid"sv) {
699  CHECK_EQ(size_t(1), rex_function->size());
700  arg_ti.set_type(kPOINT);
701  arg_ti.set_subtype(kGEOMETRY);
702  arg_ti.set_input_srid(0);
703  arg_ti.set_output_srid(0);
705 
706  SQLTypeInfo geo_ti;
707  bool with_bounds = false;
708  auto geoargs = translateGeoFunctionArg(
709  rex_function->getOperand(0), geo_ti, lindex, with_bounds, false, false);
710 
711  auto specialized_geofunc = rex_function->getName() + suffix(geo_ti.get_type());
712  if (lindex != 0) {
713  throw QueryNotSupported(rex_function->getName() +
714  " doesn't support indexed LINESTRINGs");
715  }
716  Datum input_compression;
717  input_compression.intval = Geospatial::get_compression_scheme(geo_ti);
718  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
719  Datum input_srid;
720  input_srid.intval = geo_ti.get_input_srid();
721  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
722  Datum output_srid;
723  output_srid.intval = geo_ti.get_output_srid();
724  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
725 
726  // TODO: find a better way to get 2 coords from the centroid functions
727  Datum coord_selector;
728  coord_selector.boolval = false;
729  geoargs.push_back(makeExpr<Analyzer::Constant>(kBOOLEAN, false, coord_selector));
730  std::shared_ptr<Analyzer::Expr> coord1 = makeExpr<Analyzer::FunctionOper>(
731  rex_function->getType(), specialized_geofunc, geoargs);
732  geoargs.pop_back();
733  coord_selector.boolval = true;
734  geoargs.push_back(makeExpr<Analyzer::Constant>(kBOOLEAN, false, coord_selector));
735  std::shared_ptr<Analyzer::Expr> coord2 = makeExpr<Analyzer::FunctionOper>(
736  rex_function->getType(), specialized_geofunc, geoargs);
737 
738  auto da_ti = SQLTypeInfo(kARRAY, true);
739  da_ti.set_subtype(kDOUBLE);
740  da_ti.set_size(16);
741  auto centroid_coords = {coord1, coord2};
742  auto is_local_alloca = !is_projection;
743  auto ae =
744  makeExpr<Analyzer::ArrayExpr>(da_ti, centroid_coords, false, is_local_alloca);
745  // cast it to tinyint[16]
746  SQLTypeInfo tia_ti = SQLTypeInfo(kARRAY, true);
747  tia_ti.set_subtype(kTINYINT);
748  tia_ti.set_size(16);
749  return {makeExpr<Analyzer::UOper>(tia_ti, false, kCAST, ae)};
750  } else if (func_resolve(rex_function->getName(),
751  "ST_Intersection"sv,
752  "ST_Difference"sv,
753  "ST_Union"sv,
754  "ST_Buffer"sv)) {
755  CHECK_EQ(size_t(2), rex_function->size());
756  // What geo type will the constructor return? Could be anything.
757  return {translateGeoBinaryConstructor(rex_function, arg_ti, with_bounds)};
758  } else if (func_resolve(rex_function->getName(), "ST_IsEmpty"sv, "ST_IsValid"sv)) {
759  CHECK_EQ(size_t(1), rex_function->size());
760  return {translateGeoPredicate(rex_function, arg_ti, with_bounds)};
761  } else {
762  throw QueryNotSupported("Unsupported argument: " + rex_function->getName());
763  }
764  }
765  const auto rex_literal = dynamic_cast<const RexLiteral*>(rex_scalar);
766  if (rex_literal) {
767  if (use_geo_expressions) {
768  const auto translated_literal = translateLiteral(rex_literal);
769  const auto constant_expr =
770  dynamic_cast<const Analyzer::Constant*>(translated_literal.get());
771  CHECK(constant_expr);
772  if (constant_expr->get_is_null()) {
773  // TODO: we could lift this limitation by assuming a minimum type per function
774  throw QueryNotSupported("Geospatial functions require typed nulls.");
775  }
776  const auto& datum = constant_expr->get_constval();
777  CHECK(datum.stringval);
778  auto geospatial_base = Geospatial::GeoTypesFactory::createGeoType(*datum.stringval);
779  CHECK(geospatial_base);
780  SQLTypeInfo ti;
781  ti.set_type(get_ti_from_geo(geospatial_base.get()));
782  if (arg_ti.get_subtype() == kGEOGRAPHY) {
784  } else {
786  }
787  ti.set_input_srid(arg_ti.get_input_srid());
788  ti.set_output_srid(arg_ti.get_output_srid() == 0 ? arg_ti.get_input_srid()
789  : arg_ti.get_output_srid());
790  if (ti.get_output_srid() == 4326) {
792  ti.set_comp_param(32);
793  }
794  ti.set_notnull(true);
795  return {makeExpr<Analyzer::GeoConstant>(std::move(geospatial_base), ti)};
796  }
797  return translateGeoLiteral(rex_literal, arg_ti, with_bounds);
798  }
799  throw QueryNotSupported("Geo function argument not supported");
800 }
801 
802 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoProjection(
803  const RexFunctionOperator* rex_function,
804  SQLTypeInfo& ti,
805  const bool with_bounds) const {
806  int32_t lindex = 0;
807  auto geoargs =
808  translateGeoFunctionArg(rex_function, ti, lindex, false, false, true, true);
809  if (lindex != 0) {
810  throw QueryNotSupported(
811  "Indexed LINESTRING geometries not supported in this context");
812  }
813  return makeExpr<Analyzer::GeoUOper>(
814  Geospatial::GeoBase::GeoOp::kPROJECTION, ti, ti, geoargs);
815 }
816 
817 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoBinaryConstructor(
818  const RexFunctionOperator* rex_function,
819  SQLTypeInfo& ti,
820  const bool with_bounds) const {
821 #ifndef ENABLE_GEOS
822  throw QueryNotSupported(rex_function->getName() +
823  " geo constructor requires enabled GEOS support");
824 #endif
826  if (rex_function->getName() == "ST_Difference"sv) {
828  } else if (rex_function->getName() == "ST_Union"sv) {
830  } else if (rex_function->getName() == "ST_Buffer"sv) {
832  }
833 
836  SQLTypeInfo arg0_ti;
837  SQLTypeInfo arg1_ti;
838  if (func_resolve(rex_function->getName(),
839  "ST_Intersection"sv,
840  "ST_Difference"sv,
841  "ST_Union"sv,
842  "ST_Buffer"sv)) {
843  // First arg: geometry
844  int32_t lindex0 = 0;
845  geoargs0 = translateGeoFunctionArg(
846  rex_function->getOperand(0), arg0_ti, lindex0, false, false, true, true);
847  if (arg0_ti.get_type() == kLINESTRING) {
848  if (lindex0 != 0) {
849  throw QueryNotSupported(
850  "Indexed LINESTRING geometries not supported in this context");
851  }
852  }
853  }
854  if (func_resolve(rex_function->getName(),
855  "ST_Intersection"sv,
856  "ST_Difference"sv,
857  "ST_Union"sv)) {
858  // Second arg: geometry
859  int32_t lindex1 = 0;
860  geoargs1 = translateGeoFunctionArg(
861  rex_function->getOperand(1), arg1_ti, lindex1, false, false, true, true);
862  if (arg1_ti.get_type() == kLINESTRING) {
863  if (lindex1 != 0) {
864  throw QueryNotSupported(
865  "Indexed LINESTRING geometries not supported in this context");
866  }
867  }
868  } else if (func_resolve(rex_function->getName(), "ST_Buffer"sv)) {
869  // Second arg: double scalar
870  auto param_expr = translateScalarRex(rex_function->getOperand(1));
871  arg1_ti = SQLTypeInfo(kDOUBLE, false);
872  if (param_expr->get_type_info().get_type() != kDOUBLE) {
873  param_expr = param_expr->add_cast(arg1_ti);
874  }
875  geoargs1 = {param_expr};
876  }
877 
878  auto srid = ti.get_output_srid();
879  ti = arg0_ti;
881  ti.set_compression(kENCODING_NONE); // Constructed geometries are not compressed
882  ti.set_comp_param(0);
883  if (srid > 0) {
884  ti.set_output_srid(srid); // Requested SRID sent from above
885  }
886  return makeExpr<Analyzer::GeoBinOper>(op, ti, arg0_ti, arg1_ti, geoargs0, geoargs1);
887 }
888 
889 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoPredicate(
890  const RexFunctionOperator* rex_function,
891  SQLTypeInfo& ti,
892  const bool with_bounds) const {
893 #ifndef ENABLE_GEOS
894  throw QueryNotSupported(rex_function->getName() +
895  " geo predicate requires enabled GEOS support");
896 #endif
897  int32_t lindex = 0;
898  SQLTypeInfo arg_ti;
899  auto geoargs = translateGeoFunctionArg(
900  rex_function->getOperand(0), arg_ti, lindex, false, false, true, true);
901  if (lindex != 0) {
902  throw QueryNotSupported(
903  "Indexed LINESTRING geometries not supported in this context");
904  }
905  ti = SQLTypeInfo(kBOOLEAN, false);
906  auto op = (rex_function->getName() == "ST_IsEmpty"sv)
909  return makeExpr<Analyzer::GeoUOper>(op, ti, arg_ti, geoargs);
910 }
911 
912 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateUnaryGeoFunction(
913  const RexFunctionOperator* rex_function) const {
914  CHECK_EQ(size_t(1), rex_function->size());
915 
916  int32_t lindex = 0;
917  std::string specialized_geofunc{rex_function->getName()};
918 
919  // Geo function calls which do not need the coords col but do need cols associated
920  // with physical coords (e.g. ring_sizes / poly_rings)
921  if (rex_function->getName() == "ST_NRings"sv) {
922  SQLTypeInfo arg_ti;
923  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
924  arg_ti,
925  lindex,
926  /*with_bounds=*/false,
927  /*with_render_group=*/false,
928  /*expand_geo_col=*/true,
929  /*is_projection=*/false,
930  /*use_geo_expressions=*/true);
931  CHECK_EQ(geoargs.size(), size_t(1));
932  return makeExpr<Analyzer::GeoOperator>(
933  rex_function->getType(),
934  rex_function->getName(),
935  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
936  } else if (rex_function->getName() == "ST_NPoints"sv) {
937  SQLTypeInfo arg_ti;
938  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
939  arg_ti,
940  lindex,
941  /*with_bounds=*/false,
942  /*with_render_group=*/false,
943  /*expand_geo_col=*/true,
944  /*is_projection=*/false,
945  /*use_geo_expressions=*/true);
946  CHECK_EQ(geoargs.size(), size_t(1));
947  return makeExpr<Analyzer::GeoOperator>(
948  rex_function->getType(),
949  rex_function->getName(),
950  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
951  } else if (func_resolve(rex_function->getName(), "ST_Perimeter"sv, "ST_Area"sv)) {
952  SQLTypeInfo arg_ti;
953  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
954  arg_ti,
955  lindex,
956  /*with_bounds=*/false,
957  /*with_render_group=*/false,
958  /*expand_geo_col=*/true,
959  /*is_projection=*/false,
960  /*use_geo_expressions=*/true);
961  CHECK_EQ(geoargs.size(), size_t(1));
962  arg_ti = geoargs.front()->get_type_info();
963  if (arg_ti.get_type() != kPOLYGON && arg_ti.get_type() != kMULTIPOLYGON) {
964  throw QueryNotSupported(rex_function->getName() +
965  " expects a POLYGON or MULTIPOLYGON");
966  }
967 
968  return makeExpr<Analyzer::GeoFunctionOperator>(
969  rex_function->getType(),
970  rex_function->getName(),
971  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
972  }
973 
974  // Accessors for poly bounds and render group for in-situ poly render queries
975  if (func_resolve(rex_function->getName(),
976  "MapD_GeoPolyBoundsPtr"sv /* deprecated */,
977  "OmniSci_Geo_PolyBoundsPtr"sv)) {
978  SQLTypeInfo arg_ti;
979  // get geo column plus bounds only (not expanded)
980  auto geoargs = translateGeoFunctionArg(
981  rex_function->getOperand(0), arg_ti, lindex, true, false, false);
982  // this function only works on polys
983  if (!IS_GEO_POLY(arg_ti.get_type())) {
984  throw QueryNotSupported(rex_function->getName() +
985  " expects a POLYGON or MULTIPOLYGON");
986  }
987  // only need the bounds argument (last), discard the rest
988  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
989  // done
990  return makeExpr<Analyzer::FunctionOper>(
991  rex_function->getType(), specialized_geofunc, geoargs);
992  } else if (func_resolve(rex_function->getName(),
993  "MapD_GeoPolyRenderGroup"sv /* deprecated */,
994  "OmniSci_Geo_PolyRenderGroup"sv)) {
995  SQLTypeInfo arg_ti;
996  // get geo column plus render_group only (not expanded)
997  auto geoargs = translateGeoFunctionArg(
998  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
999  // this function only works on polys
1000  if (!IS_GEO_POLY(arg_ti.get_type())) {
1001  throw QueryNotSupported(rex_function->getName() +
1002  " expects a POLYGON or MULTIPOLYGON");
1003  }
1004  // only need the render_group argument (last), discard the rest
1005  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
1006  // done
1007  return makeExpr<Analyzer::FunctionOper>(
1008  rex_function->getType(), specialized_geofunc, geoargs);
1009  }
1010 
1011  // All functions below use geo col as reference and expand it as necessary
1012  SQLTypeInfo arg_ti;
1013  bool with_bounds = true;
1014  auto geoargs = translateGeoFunctionArg(
1015  rex_function->getOperand(0), arg_ti, lindex, with_bounds, false, false);
1016 
1017  if (rex_function->getName() == "ST_SRID"sv) {
1018  Datum output_srid;
1019  output_srid.intval = arg_ti.get_output_srid();
1020  return makeExpr<Analyzer::Constant>(kINT, false, output_srid);
1021  }
1022 
1023  if (func_resolve(
1024  rex_function->getName(), "ST_XMin"sv, "ST_YMin"sv, "ST_XMax"sv, "ST_YMax"sv)) {
1025  // If type has bounds - use them, otherwise look at coords
1026  if (arg_ti.has_bounds()) {
1027  if (lindex != 0) {
1028  throw QueryNotSupported(rex_function->getName() +
1029  " doesn't support indexed LINESTRINGs");
1030  }
1031  // Only need the bounds argument, discard the rest
1032  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
1033 
1034  // Supply srids too - transformed geo would have a transformed bounding box
1035  Datum input_srid;
1036  input_srid.intval = arg_ti.get_input_srid();
1037  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1038  Datum output_srid;
1039  output_srid.intval = arg_ti.get_output_srid();
1040  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1041 
1042  specialized_geofunc += "_Bounds"s;
1043  return makeExpr<Analyzer::FunctionOper>(
1044  rex_function->getType(), specialized_geofunc, geoargs);
1045  }
1046  }
1047 
1048  // All geo function calls translated below only need the coords, extras e.g.
1049  // ring_sizes are dropped. Specialize for other/new functions if needed.
1050  geoargs.erase(geoargs.begin() + 1, geoargs.end());
1051 
1052  if (func_resolve(rex_function->getName(), "ST_X"sv, "ST_Y"sv)) {
1053  int32_t lineindex_new = 0;
1054  auto new_geoargs = translateGeoFunctionArg(
1055  rex_function->getOperand(0),
1056  arg_ti,
1057  /*lindex=*/lineindex_new, // TODO: don't care about the line index we
1058  // already translated above
1059  /*with_bounds=*/false,
1060  /*with_render_group=*/false,
1061  /*expand_geo_col=*/true,
1062  /*is_projection=*/false,
1063  /*use_geo_expressions=*/true);
1064  if (arg_ti.get_type() != kPOINT) {
1065  throw QueryNotSupported(rex_function->getName() + " expects a POINT");
1066  }
1067  CHECK_EQ(new_geoargs.size(), size_t(1));
1068  auto function_ti = rex_function->getType();
1069  if (std::dynamic_pointer_cast<Analyzer::GeoOperator>(new_geoargs.front())) {
1070  function_ti.set_notnull(false);
1071  }
1072  return makeExpr<Analyzer::GeoOperator>(
1073  function_ti,
1074  rex_function->getName(),
1075  std::vector<std::shared_ptr<Analyzer::Expr>>{new_geoargs.front()});
1076  } else if (rex_function->getName() == "ST_Length"sv) {
1077  if (arg_ti.get_type() != kLINESTRING || lindex != 0) {
1078  throw QueryNotSupported(rex_function->getName() + " expects unindexed LINESTRING");
1079  }
1080  specialized_geofunc += suffix(arg_ti.get_type());
1081  if (arg_ti.get_subtype() == kGEOGRAPHY && arg_ti.get_output_srid() == 4326) {
1082  specialized_geofunc += "_Geodesic"s;
1083  }
1084  }
1085 
1086  // Add input compression mode and SRID args to enable on-the-fly
1087  // decompression/transforms
1088  Datum input_compression;
1089  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1090  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1091  Datum input_srid;
1092  input_srid.intval = arg_ti.get_input_srid();
1093  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1094 
1095  // Add output SRID arg to enable on-the-fly transforms
1096  Datum output_srid;
1097  output_srid.intval = arg_ti.get_output_srid();
1098  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1099 
1100  return makeExpr<Analyzer::FunctionOper>(
1101  rex_function->getType(), specialized_geofunc, geoargs);
1102 }
1103 
1104 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateBinaryGeoFunction(
1105  const RexFunctionOperator* rex_function) const {
1106  auto function_name = rex_function->getName();
1107  auto return_type = rex_function->getType();
1108 
1109  if (function_name == "ST_Overlaps"sv) {
1110  // Overlaps join is the only implementation supported for now, only translate bounds
1111  CHECK_EQ(size_t(2), rex_function->size());
1112  auto extract_geo_bounds_from_input =
1113  [this, &rex_function](const size_t index) -> std::shared_ptr<Analyzer::Expr> {
1114  const auto rex_input =
1115  dynamic_cast<const RexInput*>(rex_function->getOperand(index));
1116  if (rex_input) {
1117  SQLTypeInfo ti;
1118  const auto exprs = translateGeoColumn(rex_input, ti, true, false, false);
1119  CHECK_GT(exprs.size(), size_t(0));
1120  if (ti.get_type() == kPOINT) {
1121  throw std::runtime_error("ST_Overlaps is not supported for point arguments.");
1122  } else {
1123  return exprs.back();
1124  }
1125  } else {
1126  throw std::runtime_error(
1127  "Only inputs are supported as arguments to ST_Overlaps for now.");
1128  }
1129  };
1130  std::vector<std::shared_ptr<Analyzer::Expr>> geo_args;
1131  geo_args.push_back(extract_geo_bounds_from_input(0));
1132  geo_args.push_back(extract_geo_bounds_from_input(1));
1133 
1134  return makeExpr<Analyzer::FunctionOper>(return_type, function_name, geo_args);
1135  }
1136 
1137  bool swap_args = false;
1138  bool with_bounds = false;
1139  bool negate_result = false;
1140  Analyzer::ExpressionPtr threshold_expr = nullptr;
1141  if (function_name == "ST_DWithin"sv) {
1142  CHECK_EQ(size_t(3), rex_function->size());
1143  function_name = "ST_Distance";
1144  return_type = SQLTypeInfo(kDOUBLE, false);
1145  // Inject ST_DWithin's short-circuiting threshold into ST_MaxDistance
1146  threshold_expr = translateScalarRex(rex_function->getOperand(2));
1147  } else if (function_name == "ST_DFullyWithin"sv) {
1148  CHECK_EQ(size_t(3), rex_function->size());
1149  function_name = "ST_MaxDistance";
1150  return_type = SQLTypeInfo(kDOUBLE, false);
1151  // TODO: inject ST_DFullyWithin's short-circuiting threshold into ST_MaxDistance
1152  threshold_expr = nullptr;
1153  } else if (function_name == "ST_Distance"sv) {
1154  // TODO: pick up an outside short-circuiting threshold and inject into ST_Distance
1155  threshold_expr = nullptr;
1156  } else if (function_name == "ST_MaxDistance"sv) {
1157  // TODO: pick up an outside short-circuiting threshold and inject into
1158  // ST_MaxDistance
1159  threshold_expr = nullptr;
1160  } else {
1161  CHECK_EQ(size_t(2), rex_function->size());
1162  }
1163  if (function_name == "ST_Within"sv) {
1164  function_name = "ST_Contains";
1165  swap_args = true;
1166  } else if (function_name == "ST_Disjoint"sv) {
1167  function_name = "ST_Intersects";
1168  negate_result = true;
1169  }
1170  if (func_resolve(
1171  function_name, "ST_Contains"sv, "ST_Intersects"sv, "ST_Approx_Overlaps"sv)) {
1172  with_bounds = true;
1173  }
1174 
1175  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
1176  SQLTypeInfo arg0_ti;
1177  SQLTypeInfo arg1_ti;
1178  int32_t lindex0 = 0;
1179  int32_t lindex1 = 0;
1180 
1181  auto geoargs0 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 1 : 0),
1182  arg0_ti,
1183  lindex0,
1184  with_bounds,
1185  false,
1186  false);
1187  if (arg0_ti.get_type() == kLINESTRING) {
1188  Datum index;
1189  index.intval = lindex0;
1190  geoargs0.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
1191  }
1192  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1193  auto geoargs1 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 0 : 1),
1194  arg1_ti,
1195  lindex1,
1196  with_bounds,
1197  false,
1198  false);
1199  if (arg1_ti.get_type() == kLINESTRING) {
1200  Datum index;
1201  index.intval = lindex1;
1202  geoargs1.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
1203  }
1204  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1205 
1206  if (arg0_ti.get_subtype() != kNULLT && arg0_ti.get_subtype() != arg1_ti.get_subtype()) {
1207  throw QueryNotSupported(rex_function->getName() +
1208  " accepts either two GEOGRAPHY or two GEOMETRY arguments");
1209  }
1210  // Check SRID match if at least one is set/valid
1211  if ((arg0_ti.get_output_srid() > 0 || arg1_ti.get_output_srid() > 0) &&
1212  arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
1213  throw QueryNotSupported(rex_function->getName() + " cannot accept different SRIDs");
1214  }
1215 
1216  if (function_name == "ST_Contains"sv) {
1217  const bool lhs_is_literal =
1218  geoargs1.size() == 1 &&
1219  std::dynamic_pointer_cast<const Analyzer::Constant>(geoargs1.front()) != nullptr;
1220  const bool lhs_is_point = arg1_ti.get_type() == kPOINT;
1221  if (!lhs_is_literal && lhs_is_point &&
1222  arg0_ti.get_compression() == kENCODING_GEOINT &&
1223  arg0_ti.get_input_srid() == arg0_ti.get_output_srid() &&
1224  arg0_ti.get_compression() == arg1_ti.get_compression() &&
1225  arg1_ti.get_input_srid() == arg1_ti.get_output_srid()) {
1226  // use the compressed version of ST_Contains
1227  function_name = "ST_cContains";
1228  }
1229  }
1230 
1231  std::string specialized_geofunc{function_name + suffix(arg0_ti.get_type()) +
1232  suffix(arg1_ti.get_type())};
1233 
1234  if (arg0_ti.get_subtype() == kGEOGRAPHY && arg0_ti.get_output_srid() == 4326) {
1235  // Need to call geodesic runtime functions
1236  if (function_name == "ST_Distance"sv) {
1237  if ((arg0_ti.get_type() == kPOINT ||
1238  (arg0_ti.get_type() == kLINESTRING && lindex0 != 0)) &&
1239  (arg1_ti.get_type() == kPOINT ||
1240  (arg1_ti.get_type() == kLINESTRING && lindex1 != 0))) {
1241  // Geodesic distance between points (or indexed linestrings)
1242  specialized_geofunc += "_Geodesic"s;
1243  } else {
1244  throw QueryNotSupported(function_name +
1245  " currently doesn't accept non-POINT geographies");
1246  }
1247  } else if (rex_function->getName() == "ST_Contains"sv) {
1248  // We currently don't have a geodesic implementation of ST_Contains,
1249  // allowing calls to a [less precise] cartesian implementation.
1250  } else {
1251  throw QueryNotSupported(function_name + " doesn't accept geographies");
1252  }
1253  } else if (function_name == "ST_Distance"sv && rex_function->size() == 3) {
1254  if (arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kPOINT) {
1255  // Cartesian distance between points used by ST_DWithin - switch to faster Squared
1256  specialized_geofunc += "_Squared"s;
1257  }
1258  }
1259 
1260  // Add first input's compression mode and SRID args to enable on-the-fly
1261  // decompression/transforms
1262  Datum input_compression0;
1263  input_compression0.intval = Geospatial::get_compression_scheme(arg0_ti);
1264  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1265  Datum input_srid0;
1266  input_srid0.intval = arg0_ti.get_input_srid();
1267  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1268 
1269  // Add second input's compression mode and SRID args to enable on-the-fly
1270  // decompression/transforms
1271  Datum input_compression1;
1272  input_compression1.intval = Geospatial::get_compression_scheme(arg1_ti);
1273  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1274  Datum input_srid1;
1275  input_srid1.intval = arg1_ti.get_input_srid();
1276  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1277 
1278  // Add output SRID arg to enable on-the-fly transforms
1279  Datum output_srid;
1280  output_srid.intval = arg0_ti.get_output_srid();
1281  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1282 
1283  // Some geo distance functions will be injected with a short-circuit threshold.
1284  // Threshold value would come from Geo comparison operations or from other outer
1285  // geo operations, e.g. ST_DWithin
1286  // At this point, only ST_Distance_LineString_LineString requires a threshold arg.
1287  // TODO: Other combinations that involve LINESTRING, POLYGON and MULTIPOLYGON args
1288  // TODO: Inject threshold into ST_MaxDistance
1289  if (function_name == "ST_Distance"sv && arg0_ti.get_subtype() != kGEOGRAPHY &&
1290  (arg0_ti.get_type() != kPOINT || arg1_ti.get_type() != kPOINT)) {
1291  if (threshold_expr) {
1292  if (threshold_expr->get_type_info().get_type() != kDOUBLE) {
1293  const auto& threshold_ti = SQLTypeInfo(kDOUBLE, false);
1294  threshold_expr = threshold_expr->add_cast(threshold_ti);
1295  }
1296  threshold_expr = fold_expr(threshold_expr.get());
1297  } else {
1298  Datum d;
1299  d.doubleval = 0.0;
1300  threshold_expr = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
1301  }
1302  geoargs.push_back(threshold_expr);
1303  }
1304 
1305  auto result =
1306  makeExpr<Analyzer::FunctionOper>(return_type, specialized_geofunc, geoargs);
1307  if (negate_result) {
1308  return makeExpr<Analyzer::UOper>(kBOOLEAN, kNOT, result);
1309  }
1310  return result;
1311 }
1312 
1313 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateTernaryGeoFunction(
1314  const RexFunctionOperator* rex_function) const {
1315  CHECK_EQ(size_t(3), rex_function->size());
1316 
1317  auto distance_expr = translateScalarRex(rex_function->getOperand(2));
1318  const auto& distance_ti = SQLTypeInfo(kDOUBLE, false);
1319  if (distance_expr->get_type_info().get_type() != kDOUBLE) {
1320  distance_expr = distance_expr->add_cast(distance_ti);
1321  }
1322 
1323  auto function_name = rex_function->getName();
1324  if (function_name == "ST_DWithin"sv) {
1325  auto return_type = rex_function->getType();
1326  bool swap_args = false;
1327  bool with_bounds = true;
1328  SQLTypeInfo arg0_ti;
1329  SQLTypeInfo arg1_ti;
1330  int32_t lindex0 = 0;
1331  int32_t lindex1 = 0;
1332 
1333  auto geoargs0 = translateGeoFunctionArg(
1334  rex_function->getOperand(0), arg0_ti, lindex0, with_bounds, false, false);
1335  if (arg0_ti.get_type() == kLINESTRING) {
1336  Datum index;
1337  index.intval = lindex0;
1338  geoargs0.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
1339  }
1340  auto geoargs1 = translateGeoFunctionArg(
1341  rex_function->getOperand(1), arg1_ti, lindex1, with_bounds, false, false);
1342  if (arg1_ti.get_type() == kLINESTRING) {
1343  Datum index;
1344  index.intval = lindex1;
1345  geoargs1.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
1346  }
1347  if (arg0_ti.get_subtype() == kGEOMETRY &&
1348  arg0_ti.get_subtype() != arg1_ti.get_subtype()) {
1349  throw QueryNotSupported(rex_function->getName() +
1350  " accepts only accepts GEOMETRY arguments");
1351  }
1352  // Check SRID match if at least one is set/valid
1353  if ((arg0_ti.get_output_srid() > 0 || arg1_ti.get_output_srid() > 0) &&
1354  arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
1355  throw QueryNotSupported(rex_function->getName() + " cannot accept different SRIDs");
1356  }
1357 
1358  if ((arg1_ti.get_type() == kPOINT && arg0_ti.get_type() != kPOINT) ||
1359  (arg1_ti.get_type() == kLINESTRING && arg0_ti.get_type() == kPOLYGON) ||
1360  (arg1_ti.get_type() == kPOLYGON && arg0_ti.get_type() == kMULTIPOLYGON)) {
1361  // Swap arguments and use single implementation per arg pair
1362  swap_args = true;
1363  }
1364 
1365  // First input's compression mode and SRID args to enable on-the-fly
1366  // decompression/transforms
1367  Datum input_compression0;
1368  input_compression0.intval = Geospatial::get_compression_scheme(arg0_ti);
1369  Datum input_srid0;
1370  input_srid0.intval = arg0_ti.get_input_srid();
1371 
1372  // Second input's compression mode and SRID args to enable on-the-fly
1373  // decompression/transforms
1374  Datum input_compression1;
1375  input_compression1.intval = Geospatial::get_compression_scheme(arg1_ti);
1376  Datum input_srid1;
1377  input_srid1.intval = arg1_ti.get_input_srid();
1378 
1379  // Output SRID arg to enable on-the-fly transforms
1380  Datum output_srid;
1381  output_srid.intval = arg0_ti.get_output_srid();
1382 
1383  std::string specialized_geofunc{function_name};
1384  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
1385  if (swap_args) {
1386  specialized_geofunc += suffix(arg1_ti.get_type()) + suffix(arg0_ti.get_type());
1387  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1388  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1389  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1390  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1391  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1392  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1393  } else {
1394  specialized_geofunc += suffix(arg0_ti.get_type()) + suffix(arg1_ti.get_type());
1395  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1396  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1397  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1398  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1399  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1400  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1401  }
1402  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1403  // Also add the within distance
1404  geoargs.push_back(distance_expr);
1405 
1406  auto result =
1407  makeExpr<Analyzer::FunctionOper>(return_type, specialized_geofunc, geoargs);
1408  return result;
1409  }
1410 
1411  // Otherwise translate function as binary geo to get distance,
1412  // with optional short-circuiting threshold held in the third operand
1413  const auto geo_distance = translateBinaryGeoFunction(rex_function);
1414  // and generate the comparison
1415  return makeExpr<Analyzer::BinOper>(kBOOLEAN, kLE, kONE, geo_distance, distance_expr);
1416 }
1417 
1418 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoComparison(
1419  const RexOperator* rex_operator) const {
1420  if (rex_operator->size() != size_t(2)) {
1421  return nullptr;
1422  }
1423 
1424  const auto rex_operand = rex_operator->getOperand(0);
1425  const auto rex_function = dynamic_cast<const RexFunctionOperator*>(rex_operand);
1426  if (!rex_function) {
1427  return nullptr;
1428  }
1429  if (rex_function->getName() == "ST_Distance"sv && rex_operator->getOperator() == kLE) {
1430  // TODO: fixup
1431  return nullptr;
1432  /*
1433  auto ti = rex_operator->getType();
1434  std::vector<std::unique_ptr<const RexScalar>> st_dwithin_operands;
1435  st_dwithin_operands.emplace_back(rex_function->getOperandAndRelease(0));
1436  st_dwithin_operands.emplace_back(rex_function->getOperandAndRelease(1));
1437  st_dwithin_operands.emplace_back(rex_operator->getOperandAndRelease(1));
1438  std::unique_ptr<RexFunctionOperator> st_dwithin(
1439  new RexFunctionOperator("ST_DWithin", st_dwithin_operands, ti));
1440  return translateTernaryGeoFunction(st_dwithin.get());
1441  */
1442  }
1443 
1444  return nullptr;
1445 }
1446 
1447 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateFunctionWithGeoArg(
1448  const RexFunctionOperator* rex_function) const {
1449  int32_t lindex = 0;
1450  std::string specialized_geofunc{rex_function->getName()};
1451  if (func_resolve(rex_function->getName(),
1452  "convert_meters_to_pixel_width"sv,
1453  "convert_meters_to_pixel_height"sv)) {
1454  CHECK_EQ(rex_function->size(), 6u);
1455  SQLTypeInfo arg_ti;
1456  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1457  args.push_back(translateScalarRex(rex_function->getOperand(0)));
1458  auto geoargs = translateGeoFunctionArg(
1459  rex_function->getOperand(1), arg_ti, lindex, false, true, false);
1460  // only works on points
1461  if (arg_ti.get_type() != kPOINT) {
1462  throw QueryNotSupported(rex_function->getName() +
1463  " expects a point for the second argument");
1464  }
1465 
1466  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1467 
1468  // Add compression information
1469  Datum input_compression;
1470  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1471  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1472  if (arg_ti.get_input_srid() != 4326) {
1473  throw QueryNotSupported(
1474  rex_function->getName() +
1475  " currently only supports points of with SRID WGS84/EPSG:4326");
1476  }
1477  Datum input_srid;
1478  input_srid.intval = arg_ti.get_input_srid();
1479  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1480  Datum output_srid;
1481  // Forcing web-mercator projection for now
1482  // TODO(croot): check that the input-to-output conversion routines exist?
1483  output_srid.intval =
1484  arg_ti.get_output_srid() != 900913 ? 900913 : arg_ti.get_output_srid();
1485  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1486 
1487  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1488  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1489  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1490  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1491  return makeExpr<Analyzer::FunctionOper>(
1492  rex_function->getType(), specialized_geofunc, args);
1493  } else if (rex_function->getName() == "is_point_in_view"sv) {
1494  CHECK_EQ(rex_function->size(), 5u);
1495  SQLTypeInfo arg_ti;
1496  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1497  auto geoargs = translateGeoFunctionArg(
1498  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
1499  // only works on points
1500  if (arg_ti.get_type() != kPOINT) {
1501  throw QueryNotSupported(rex_function->getName() +
1502  " expects a point for the second argument");
1503  }
1504 
1505  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1506 
1507  // Add compression information
1508  Datum input_compression;
1509  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1510  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1511  if (arg_ti.get_input_srid() != 4326) {
1512  throw QueryNotSupported(
1513  rex_function->getName() +
1514  " currently only supports points of with SRID WGS84/EPSG:4326");
1515  }
1516  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1517  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1518  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1519  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1520  return makeExpr<Analyzer::FunctionOper>(
1521  rex_function->getType(), specialized_geofunc, args);
1522  } else if (rex_function->getName() == "is_point_size_in_view"sv) {
1523  CHECK_EQ(rex_function->size(), 6u);
1524  SQLTypeInfo arg_ti;
1525  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1526  auto geoargs = translateGeoFunctionArg(
1527  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
1528  // only works on points
1529  if (arg_ti.get_type() != kPOINT) {
1530  throw QueryNotSupported(rex_function->getName() +
1531  " expects a point for the second argument");
1532  }
1533 
1534  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1535 
1536  // Add compression information
1537  Datum input_compression;
1538  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1539  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1540  if (arg_ti.get_input_srid() != 4326) {
1541  throw QueryNotSupported(
1542  rex_function->getName() +
1543  " currently only supports points of with SRID WGS84/EPSG:4326");
1544  }
1545  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1546  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1547  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1548  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1549  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1550  return makeExpr<Analyzer::FunctionOper>(
1551  rex_function->getType(), specialized_geofunc, args);
1552  }
1553  CHECK(false);
1554  return nullptr;
1555 }
1556 
1557 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoOverlapsOper(
1558  const RexOperator* rex_operator) const {
1559  CHECK_EQ(rex_operator->size(), 2u);
1560 
1561  auto translate_input =
1562  [&](const RexScalar* operand) -> std::shared_ptr<Analyzer::Expr> {
1563  const auto input = dynamic_cast<const RexInput*>(operand);
1564  CHECK(input);
1565 
1566  SQLTypeInfo ti;
1567  const auto exprs = translateGeoColumn(input, ti, true, false, false);
1568  CHECK_GT(exprs.size(), 0u);
1569  if (ti.get_type() == kPOINT) {
1570  return exprs.front();
1571  } else {
1572  return exprs.back();
1573  }
1574  };
1575 
1576  SQLQualifier sql_qual{kONE};
1577  SQLOps sql_op{kOVERLAPS};
1578  return makeExpr<Analyzer::BinOper>(SQLTypeInfo(kBOOLEAN, false),
1579  false,
1580  sql_op,
1581  sql_qual,
1582  translate_input(rex_operator->getOperand(1)),
1583  translate_input(rex_operator->getOperand(0)));
1584 }
int8_t tinyintval
Definition: sqltypes.h:206
std::vector< std::shared_ptr< Analyzer::Expr > > translateGeoFunctionArg(const RexScalar *rex_scalar, SQLTypeInfo &arg_ti, int32_t &lindex, const bool with_bounds, const bool with_render_group, const bool expand_geo_col, const bool is_projection=false, const bool use_geo_expressions=false) const
HOST DEVICE SQLTypes get_subtype() const
Definition: sqltypes.h:315
void set_compression(EncodingType c)
Definition: sqltypes.h:414
void set_size(int s)
Definition: sqltypes.h:412
#define CHECK_EQ(x, y)
Definition: Logger.h:211
static std::unique_ptr< GeoBase > createGeoType(const std::string &wkt_or_wkb_hex)
Definition: Types.cpp:919
auto func_resolve
SQLTypes
Definition: sqltypes.h:37
tuple d
Definition: test_fsi.py:9
#define SPIMAP_GEO_PHYSICAL_INPUT(c, i)
Definition: Catalog.h:76
SQLQualifier
Definition: sqldefs.h:69
std::shared_ptr< Analyzer::Expr > translateScalarRex(const RexScalar *rex) const
const SQLTypeInfo & getType() const
bool boolval
Definition: sqltypes.h:205
size_t size() const
const RexScalar * getOperand(const size_t idx) const
SQLOps
Definition: sqldefs.h:29
Definition: sqldefs.h:35
int32_t get_compression_scheme(const SQLTypeInfo &ti)
Definition: Compression.cpp:23
#define UNREACHABLE()
Definition: Logger.h:247
HOST DEVICE void set_subtype(SQLTypes st)
Definition: sqltypes.h:405
#define CHECK_GE(x, y)
Definition: Logger.h:216
Definition: sqldefs.h:49
std::shared_ptr< Analyzer::Expr > ExpressionPtr
Definition: Analyzer.h:180
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:314
#define CHECK_GT(x, y)
Definition: Logger.h:215
std::shared_ptr< Analyzer::Expr > translateGeoProjection(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
int32_t intval
Definition: sqltypes.h:208
std::string to_string(char const *&&v)
std::shared_ptr< Analyzer::Expr > translateInput(const RexInput *) const
std::shared_ptr< Analyzer::Expr > translateUnaryGeoFunction(const RexFunctionOperator *) const
bool has_render_group() const
Definition: sqltypes.h:394
std::shared_ptr< Analyzer::Expr > translateGeoOverlapsOper(const RexOperator *) const
std::vector< std::shared_ptr< Analyzer::Expr > > translateGeoLiteral(const RexLiteral *, SQLTypeInfo &, bool) const
void set_input_srid(int d)
Definition: sqltypes.h:408
unsigned getIndex() const
std::vector< uint8_t > compress_coords(const std::vector< double > &coords, const SQLTypeInfo &ti)
Definition: Compression.cpp:52
static std::shared_ptr< Analyzer::Expr > translateLiteral(const RexLiteral *)
SQLOps getOperator() const
bool has_bounds() const
Definition: sqltypes.h:383
const ColumnDescriptor * getMetadataForColumnBySpi(const int tableId, const size_t spi) const
Definition: Catalog.cpp:1598
void set_output_srid(int s)
Definition: sqltypes.h:410
const std::unordered_map< const RelAlgNode *, int > input_to_nest_level_
void set_comp_param(int p)
Definition: sqltypes.h:415
static bool getGeoColumns(const std::string &wkt_or_wkb_hex, SQLTypeInfo &ti, std::vector< double > &coords, std::vector< double > &bounds, std::vector< int > &ring_sizes, std::vector< int > &poly_rings, const bool promote_poly_to_mpoly=false)
Definition: Types.cpp:937
#define CHECK_LT(x, y)
Definition: Logger.h:213
Definition: sqltypes.h:51
Definition: sqldefs.h:69
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:322
const RelAlgNode * getSourceNode() const
std::shared_ptr< Analyzer::Expr > translateGeoBinaryConstructor(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
HOST DEVICE int get_input_srid() const
Definition: sqltypes.h:318
void set_notnull(bool n)
Definition: sqltypes.h:411
#define CHECK(condition)
Definition: Logger.h:203
std::shared_ptr< Analyzer::Expr > translateTernaryGeoFunction(const RexFunctionOperator *) const
std::shared_ptr< Analyzer::Expr > translateBinaryGeoFunction(const RexFunctionOperator *) const
std::vector< ExpressionPtr > ExpressionPtrVector
Definition: Analyzer.h:182
std::shared_ptr< Analyzer::Expr > translateFunctionWithGeoArg(const RexFunctionOperator *) const
Definition: sqltypes.h:44
SQLTypeInfo columnType
const std::string & getName() const
std::string suffix(SQLTypes type)
Definition: GeoIR.cpp:419
std::shared_ptr< Analyzer::Expr > translateGeoComparison(const RexOperator *) const
int get_physical_coord_cols() const
Definition: sqltypes.h:350
std::vector< std::shared_ptr< Analyzer::Expr > > translateGeoColumn(const RexInput *, SQLTypeInfo &, const bool with_bounds, const bool with_render_group, const bool expand_geo_col) const
#define IS_GEO(T)
Definition: sqltypes.h:245
Definition: sqldefs.h:39
SQLTypes get_ti_from_geo(const Geospatial::GeoBase *geo)
Definition: Analyzer.cpp:3299
std::shared_ptr< Analyzer::Expr > fold_expr(const Analyzer::Expr *expr)
double doubleval
Definition: sqltypes.h:211
const Catalog_Namespace::Catalog & cat_
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:320
std::shared_ptr< Analyzer::Expr > translateGeoPredicate(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
virtual GeoType getType() const =0
#define IS_GEO_POLY(T)
Definition: sqltypes.h:249
HOST DEVICE void set_type(SQLTypes t)
Definition: sqltypes.h:404