OmniSciDB  a987f07e93
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RelAlgTranslatorGeo.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 
17 #include <memory>
18 #include <vector>
19 
20 #include "Geospatial/Compression.h"
21 #include "Geospatial/Types.h"
25 
27 
28 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoColumn(
29  const RexInput* rex_input,
30  SQLTypeInfo& ti,
31  const bool with_bounds,
32  const bool with_render_group,
33  const bool expand_geo_col) const {
34  std::vector<std::shared_ptr<Analyzer::Expr>> args;
35  const auto source = rex_input->getSourceNode();
36  const auto it_rte_idx = input_to_nest_level_.find(source);
37  CHECK(it_rte_idx != input_to_nest_level_.end());
38  const int rte_idx = it_rte_idx->second;
39  const auto& in_metainfo = source->getOutputMetainfo();
40 
41  int32_t table_id{0};
42  int column_id{-1};
43  const auto scan_source = dynamic_cast<const RelScan*>(source);
44  if (scan_source) {
45  // We're at leaf (scan) level and not supposed to have input metadata,
46  // the name and type information come directly from the catalog.
47  CHECK(in_metainfo.empty());
48 
49  const auto td = scan_source->getTableDescriptor();
50  table_id = td->tableId;
51 
52  const auto gcd = cat_.getMetadataForColumnBySpi(table_id, rex_input->getIndex() + 1);
53  CHECK(gcd);
54  ti = gcd->columnType;
55  column_id = gcd->columnId;
56 
57  } else {
58  // Likely backed by a temp table. Read the table ID from the source node and negate it
59  // (see RelAlgTranslator::translateInput)
60  table_id = -source->getId();
61 
62  if (with_bounds || with_render_group) {
63  throw QueryNotSupported(
64  "Geospatial columns not yet supported in intermediate results.");
65  }
66 
67  CHECK(!in_metainfo.empty());
68  CHECK_GE(rte_idx, 0);
69  column_id = rex_input->getIndex();
70  CHECK_LT(static_cast<size_t>(column_id), in_metainfo.size());
71  ti = in_metainfo[column_id].get_type_info();
72  if (expand_geo_col && ti.is_geometry()) {
73  throw QueryNotSupported(
74  "Geospatial columns not yet supported in this temporary table context.");
75  }
76  }
77 
78  if (!IS_GEO(ti.get_type())) {
79  throw QueryNotSupported(
80  "Geospatial expression and operator require geospatial column as their input "
81  "argument(s)");
82  }
83 
84  // Return geo column reference. The geo column may be expanded if required for extension
85  // function arguments. Otherwise, the geo column reference will be translated into
86  // physical columns as required. Bounds column will be added if present and requested.
87  if (expand_geo_col) {
88  for (auto i = 0; i < ti.get_physical_coord_cols(); i++) {
89  const auto pcd = cat_.getMetadataForColumnBySpi(
90  table_id, SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(), i + 1));
91  auto pcol_ti = pcd->columnType;
92  args.push_back(std::make_shared<Analyzer::ColumnVar>(
93  pcol_ti, table_id, pcd->columnId, rte_idx));
94  }
95  } else {
96  args.push_back(
97  std::make_shared<Analyzer::ColumnVar>(ti, table_id, column_id, rte_idx));
98  }
99  if (with_bounds && ti.has_bounds()) {
100  const auto bounds_cd = cat_.getMetadataForColumnBySpi(
101  table_id,
102  SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(),
103  ti.get_physical_coord_cols() + 1));
104  auto bounds_ti = bounds_cd->columnType;
105  args.push_back(std::make_shared<Analyzer::ColumnVar>(
106  bounds_ti, table_id, bounds_cd->columnId, rte_idx));
107  }
108  if (with_render_group && ti.has_render_group()) {
109  const auto render_group_cd = cat_.getMetadataForColumnBySpi(
110  table_id,
111  SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(),
112  ti.get_physical_coord_cols() + 2));
113  auto render_group_ti = render_group_cd->columnType;
114  args.push_back(std::make_shared<Analyzer::ColumnVar>(
115  render_group_ti, table_id, render_group_cd->columnId, rte_idx));
116  }
117  return args;
118 }
119 
120 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoLiteral(
121  const RexLiteral* rex_literal,
122  SQLTypeInfo& ti,
123  bool with_bounds) const {
124  CHECK(rex_literal);
125  if (rex_literal->getType() != kTEXT) {
126  throw std::runtime_error("Geo literals must be strings");
127  }
128 
129  // TODO: use geo conversion here
130  const auto e = translateLiteral(rex_literal);
131  auto wkt = std::dynamic_pointer_cast<Analyzer::Constant>(e);
132  CHECK(wkt);
133  std::vector<double> coords;
134  std::vector<double> bounds;
135  std::vector<int> ring_sizes;
136  std::vector<int> poly_rings;
137  int32_t srid = ti.get_output_srid();
139  *wkt->get_constval().stringval, ti, coords, bounds, ring_sizes, poly_rings)) {
140  throw QueryNotSupported("Could not read geometry from text");
141  }
143  ti.set_input_srid(srid);
144  ti.set_output_srid(srid);
145  // Compress geo literals by default
146  if (srid == 4326) {
148  ti.set_comp_param(32);
149  }
150 
151  std::vector<std::shared_ptr<Analyzer::Expr>> args;
152 
153  std::vector<uint8_t> compressed_coords = Geospatial::compress_coords(coords, ti);
154  std::list<std::shared_ptr<Analyzer::Expr>> compressed_coords_exprs;
155  for (auto cc : compressed_coords) {
156  Datum d;
157  d.tinyintval = cc;
158  auto e = makeExpr<Analyzer::Constant>(kTINYINT, false, d);
159  compressed_coords_exprs.push_back(e);
160  }
161  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
162  arr_ti.set_subtype(kTINYINT);
163  arr_ti.set_size(compressed_coords.size() * sizeof(int8_t));
164  arr_ti.set_compression(ti.get_compression());
165  arr_ti.set_comp_param((ti.get_compression() == kENCODING_GEOINT) ? 32 : 64);
166  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, compressed_coords_exprs));
167 
168  auto lit_type = ti.get_type();
169  if (lit_type == kMULTILINESTRING || lit_type == kPOLYGON || lit_type == kMULTIPOLYGON) {
170  // [linest]ring sizes
171  std::list<std::shared_ptr<Analyzer::Expr>> ring_size_exprs;
172  for (auto c : ring_sizes) {
173  Datum d;
174  d.intval = c;
175  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
176  ring_size_exprs.push_back(e);
177  }
178  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
179  arr_ti.set_subtype(kINT);
180  arr_ti.set_size(ring_sizes.size() * sizeof(int32_t));
181  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, ring_size_exprs));
182 
183  // poly rings
184  if (lit_type == kMULTIPOLYGON) {
185  std::list<std::shared_ptr<Analyzer::Expr>> poly_rings_exprs;
186  for (auto c : poly_rings) {
187  Datum d;
188  d.intval = c;
189  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
190  poly_rings_exprs.push_back(e);
191  }
192  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
193  arr_ti.set_subtype(kINT);
194  arr_ti.set_size(poly_rings.size() * sizeof(int32_t));
195  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, poly_rings_exprs));
196  }
197  }
198 
199  if (with_bounds && ti.has_bounds()) {
200  // bounds
201  std::list<std::shared_ptr<Analyzer::Expr>> bounds_exprs;
202  for (auto b : bounds) {
203  Datum d;
204  d.doubleval = b;
205  auto e = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
206  bounds_exprs.push_back(e);
207  }
208  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
209  arr_ti.set_subtype(kDOUBLE);
210  arr_ti.set_size(bounds.size() * sizeof(double));
211  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, bounds_exprs));
212  }
213 
214  return args;
215 }
216 
217 namespace {
218 
219 std::string suffix(SQLTypes type) {
220  if (type == kPOINT) {
221  return std::string("_Point");
222  }
223  if (type == kMULTIPOINT) {
224  return std::string("_MultiPoint");
225  }
226  if (type == kLINESTRING) {
227  return std::string("_LineString");
228  }
229  if (type == kMULTILINESTRING) {
230  return std::string("_MultiLineString");
231  }
232  if (type == kPOLYGON) {
233  return std::string("_Polygon");
234  }
235  if (type == kMULTIPOLYGON) {
236  return std::string("_MultiPolygon");
237  }
238  throw QueryNotSupported("Unsupported argument type");
239 }
240 
242  CHECK(geo);
243  switch (geo->getType()) {
245  return kPOINT;
246  }
248  return kMULTIPOINT;
249  }
251  return kLINESTRING;
252  }
254  return kMULTILINESTRING;
255  }
257  return kPOLYGON;
258  }
260  return kMULTIPOLYGON;
261  }
262  default:
263  UNREACHABLE();
264  return kNULLT;
265  }
266 }
267 
268 } // namespace
269 
270 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoFunctionArg(
271  const RexScalar* rex_scalar,
272  SQLTypeInfo& arg_ti,
273  const bool with_bounds,
274  const bool with_render_group,
275  const bool expand_geo_col,
276  const bool is_projection,
277  const bool use_geo_expressions,
278  const bool try_to_compress,
279  const bool allow_gdal_transforms) const {
280  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
281 
282  const auto rex_input = dynamic_cast<const RexInput*>(rex_scalar);
283  if (rex_input) {
284  const auto input = translateInput(rex_input);
285  const auto column = dynamic_cast<const Analyzer::ColumnVar*>(input.get());
286  if (!column || !column->get_type_info().is_geometry()) {
287  throw QueryNotSupported("Geo function is expecting a geo column argument");
288  }
289  if (use_geo_expressions) {
290  arg_ti = column->get_type_info();
291  return {makeExpr<Analyzer::GeoColumnVar>(column, with_bounds, with_render_group)};
292  }
293  return translateGeoColumn(
294  rex_input, arg_ti, with_bounds, with_render_group, expand_geo_col);
295  }
296  const auto rex_function = dynamic_cast<const RexFunctionOperator*>(rex_scalar);
297  if (rex_function) {
298  if (rex_function->getName() == "ST_Transform"sv) {
299  CHECK_EQ(size_t(2), rex_function->size());
300  const auto rex_scalar0 =
301  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
302  if (!rex_scalar0) {
303  throw QueryNotSupported(rex_function->getName() + ": unexpected first argument");
304  }
305 
306  const auto rex_literal =
307  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
308  if (!rex_literal) {
309  throw QueryNotSupported(rex_function->getName() +
310  ": second argument is expected to be a literal");
311  }
312  const auto e = translateLiteral(rex_literal);
313  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
314  if (!ce || !e->get_type_info().is_integer()) {
315  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
316  }
317  int32_t srid = 0;
318  if (e->get_type_info().get_type() == kSMALLINT) {
319  srid = static_cast<int32_t>(ce->get_constval().smallintval);
320  } else if (e->get_type_info().get_type() == kTINYINT) {
321  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
322  } else if (e->get_type_info().get_type() == kINT) {
323  srid = static_cast<int32_t>(ce->get_constval().intval);
324  } else {
325  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
326  }
327  bool allow_result_gdal_transform = false;
328  const auto rex_function0 = dynamic_cast<const RexFunctionOperator*>(rex_scalar0);
329  if (rex_function0 && func_resolve(rex_function0->getName(),
330  "ST_Intersection"sv,
331  "ST_Difference"sv,
332  "ST_Union"sv,
333  "ST_Buffer"sv,
334  "ST_ConcaveHull"sv,
335  "ST_ConvexHull"sv)) {
336  // TODO: the design of geo operators currently doesn't allow input srid overrides.
337  // For example, in case of ST_Area(ST_Transform(ST_Buffer(geo_column,0), 900913))
338  // we can ask geos runtime to transform ST_Buffer's output from 4326 to 900913,
339  // however, ST_Area geo operator would still rely on the first arg's typeinfo
340  // to codegen srid arg values in the ST_Area_ extension function call. And it will
341  // still pick up that transform so the coords will be transformed to 900913 twice.
342 
343  // Sink result transform into geos runtime
344  // allow_result_gdal_transform = true;
345  }
346  if (!allow_gdal_transforms && !allow_result_gdal_transform) {
347  if (srid != 900913 && ((use_geo_expressions || is_projection) && srid != 4326 &&
349  throw QueryNotSupported(rex_function->getName() + ": unsupported output SRID " +
350  std::to_string(srid));
351  }
352  }
353  arg_ti.set_output_srid(srid); // Forward output srid down to argument translation
354  bool arg0_use_geo_expressions = is_projection ? true : use_geo_expressions;
355  if (allow_gdal_transforms) {
356  arg0_use_geo_expressions = false;
357  }
358  auto arg0 = translateGeoFunctionArg(rex_scalar0,
359  arg_ti,
360  with_bounds,
361  with_render_group,
362  expand_geo_col,
363  is_projection,
364  arg0_use_geo_expressions);
365 
366  if (use_geo_expressions) {
367  CHECK_EQ(arg0.size(), size_t(1));
368  auto arg0_ti = arg0.front()->get_type_info(); // make a copy so we can override
369  arg0_ti.set_output_srid(srid);
370  if (arg0_ti.get_type() == kPOINT) {
371  // the output type is going to be fully transformed, so set the input srid to
372  // the output srid
373  const auto input_srid = arg0_ti.get_input_srid();
374  arg0_ti.set_input_srid(srid);
375  // geo transforms projections leave the result decompressed in a register
376  arg0_ti.set_compression(kENCODING_NONE);
377  arg0_ti.set_comp_param(0);
378  // reset recursive arg_ti, as the output type of transform will be properly
379  // transformed to the desired SRID
380  arg_ti.set_output_srid(srid);
381  arg_ti.set_input_srid(srid);
382  return {makeExpr<Analyzer::GeoTransformOperator>(
383  arg0_ti, rex_function->getName(), arg0, input_srid, srid)};
384  } else {
385  if (auto geo_constant =
386  std::dynamic_pointer_cast<Analyzer::GeoConstant>(arg0.front())) {
387  // fold transform
388  auto cast_geo_constant = geo_constant->add_cast(arg0_ti);
389  // update return type info
390  arg_ti = cast_geo_constant->get_type_info();
391  return {cast_geo_constant};
392  } else if (auto col_var =
393  std::dynamic_pointer_cast<Analyzer::ColumnVar>(arg0.front())) {
394  const auto& col_ti = col_var->get_type_info();
395  CHECK(col_ti.is_geometry());
396  if (col_ti.get_type() != kPOINT) {
397  arg_ti.set_input_srid(col_ti.get_input_srid());
398  // fall through to transform code below
399  }
400  } else {
401  if (!allow_gdal_transforms && !allow_result_gdal_transform) {
402  throw std::runtime_error(
403  "Transform on non-POINT geospatial types not yet supported in this "
404  "context.");
405  }
406  }
407  }
408  }
409 
410  if (arg_ti.get_input_srid() > 0) {
411  if (!allow_gdal_transforms && !allow_result_gdal_transform) {
412  if (arg_ti.get_input_srid() != 4326) {
413  throw QueryNotSupported(rex_function->getName() +
414  ": unsupported input SRID " +
415  std::to_string(arg_ti.get_input_srid()));
416  }
417  }
418  // Established that the input SRID is valid
419  if (allow_result_gdal_transform) {
420  // If gdal transform has been allowed, then it has been sunk into geos runtime.
421  // The returning geometry has already been transformed, de-register transform.
422  if (arg_ti.get_input_srid() != srid) {
423  arg_ti.set_input_srid(srid);
424  }
425  }
426  arg_ti.set_output_srid(srid);
427  } else {
428  throw QueryNotSupported(rex_function->getName() +
429  ": unexpected input SRID, unable to transform");
430  }
431  return arg0;
432  } else if (func_resolve(
433  rex_function->getName(), "ST_GeomFromText"sv, "ST_GeogFromText"sv)) {
434  CHECK(rex_function->size() == size_t(1) || rex_function->size() == size_t(2));
435  if (use_geo_expressions) {
436  int32_t srid = 0;
437  if (rex_function->size() == 2) {
438  // user supplied srid
439  const auto rex_literal =
440  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
441  if (!rex_literal) {
442  throw QueryNotSupported(rex_function->getName() +
443  ": second argument is expected to be a literal");
444  }
445  const auto e = translateLiteral(rex_literal);
446  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
447  if (!ce || !e->get_type_info().is_integer()) {
448  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
449  }
450  if (e->get_type_info().get_type() == kSMALLINT) {
451  srid = static_cast<int32_t>(ce->get_constval().smallintval);
452  } else if (e->get_type_info().get_type() == kTINYINT) {
453  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
454  } else if (e->get_type_info().get_type() == kINT) {
455  srid = static_cast<int32_t>(ce->get_constval().intval);
456  } else {
457  throw QueryNotSupported(rex_function->getName() + " expecting integer SRID");
458  }
459  if (srid != 0 && srid != 4326 && srid != 900913) {
460  throw QueryNotSupported(rex_function->getName() + ": unsupported SRID " +
461  std::to_string(srid));
462  }
463  }
464  arg_ti.set_input_srid(srid); // Input SRID
465  // leave the output srid unset in case a transform was above us
466 
467  if (rex_function->getName() == "ST_GeogFromText"sv) {
468  arg_ti.set_subtype(kGEOGRAPHY);
469  } else {
470  arg_ti.set_subtype(kGEOMETRY);
471  }
472 
473  auto func_args = translateGeoFunctionArg(rex_function->getOperand(0),
474  arg_ti,
475  with_bounds,
476  with_render_group,
477  expand_geo_col,
479  use_geo_expressions);
480  CHECK_GE(func_args.size(), size_t(1));
481  return func_args;
482  }
483 
484  // First - register srid, then send it to geo literal translation
485  int32_t srid = 0;
486  if (rex_function->size() == 2) {
487  const auto rex_literal =
488  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
489  if (!rex_literal) {
490  throw QueryNotSupported(rex_function->getName() +
491  ": second argument is expected to be a literal");
492  }
493  const auto e = translateLiteral(rex_literal);
494  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
495  if (!ce || !e->get_type_info().is_integer()) {
496  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
497  }
498  if (e->get_type_info().get_type() == kSMALLINT) {
499  srid = static_cast<int32_t>(ce->get_constval().smallintval);
500  } else if (e->get_type_info().get_type() == kTINYINT) {
501  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
502  } else if (e->get_type_info().get_type() == kINT) {
503  srid = static_cast<int32_t>(ce->get_constval().intval);
504  } else {
505  throw QueryNotSupported(rex_function->getName() + " expecting integer SRID");
506  }
507  if (srid != 0 && srid != 4326 && srid != 900913) {
508  throw QueryNotSupported(rex_function->getName() + ": unsupported SRID " +
509  std::to_string(srid));
510  }
511  }
512  arg_ti.set_input_srid(srid); // Input SRID
513  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
514 
515  const auto rex_literal =
516  dynamic_cast<const RexLiteral*>(rex_function->getOperand(0));
517  if (!rex_literal) {
518  throw QueryNotSupported(rex_function->getName() +
519  " expects a string literal as first argument");
520  }
521  auto arg0 = translateGeoLiteral(rex_literal, arg_ti, with_bounds);
522  arg_ti.set_subtype((rex_function->getName() == "ST_GeogFromText"sv) ? kGEOGRAPHY
523  : kGEOMETRY);
524  return arg0;
525  } else if (rex_function->getName() == "ST_PointN"sv) {
526  // uses geo expressions
527  const auto rex_scalar0 =
528  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
529  if (!rex_scalar0) {
530  throw QueryNotSupported(rex_function->getName() +
531  ": expects scalar as first argument");
532  }
533  auto arg0 = translateGeoFunctionArg(rex_scalar0,
534  arg_ti,
535  with_bounds,
536  with_render_group,
537  expand_geo_col,
538  /*is_projection=*/false,
539  /*use_geo_expressions=*/true);
540  CHECK_EQ(arg0.size(), size_t(1));
541  CHECK(arg0.front());
542  if (arg0.front()->get_type_info().get_type() != kLINESTRING) {
543  throw QueryNotSupported(rex_function->getName() +
544  " expects LINESTRING as first argument");
545  }
546  const auto rex_literal =
547  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
548  if (!rex_literal) {
549  throw QueryNotSupported(rex_function->getName() +
550  ": second argument is expected to be a literal");
551  }
552  const auto e = translateLiteral(rex_literal);
553  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
554  if (!ce || !e->get_type_info().is_integer()) {
555  throw QueryNotSupported(rex_function->getName() +
556  ": expecting integer index as second argument");
557  }
558  int32_t index = 0;
559  if (e->get_type_info().get_type() == kSMALLINT) {
560  index = static_cast<int32_t>(ce->get_constval().smallintval);
561  } else if (e->get_type_info().get_type() == kTINYINT) {
562  index = static_cast<int32_t>(ce->get_constval().tinyintval);
563  } else if (e->get_type_info().get_type() == kINT) {
564  index = static_cast<int32_t>(ce->get_constval().intval);
565  } else {
566  throw QueryNotSupported(rex_function->getName() + " expecting integer index");
567  }
568  if (index == 0) {
569  // maybe we will just return NULL here?
570  throw QueryNotSupported(rex_function->getName() + ": invalid index");
571  }
572  arg0.push_back(e);
573  auto oper_ti =
574  arg0.front()->get_type_info(); // make a copy so we can reset nullness and type
575  oper_ti.set_type(kPOINT);
576  oper_ti.set_notnull(false);
577 
578  arg_ti = oper_ti; // TODO: remove
579 
580  return {makeExpr<Analyzer::GeoOperator>(oper_ti, rex_function->getName(), arg0)};
581 
582  } else if (rex_function->getName() == "ST_StartPoint"sv ||
583  rex_function->getName() == "ST_EndPoint"sv) {
584  std::vector<std::shared_ptr<Analyzer::Expr>> args;
585  CHECK_EQ(size_t(1), rex_function->size());
586  const auto arg_exprs = translateGeoFunctionArg(rex_function->getOperand(0),
587  arg_ti,
588  with_bounds,
589  with_render_group,
590  expand_geo_col,
592  /*use_geo_expressions=*/true);
593  CHECK_EQ(arg_exprs.size(), size_t(1));
594  CHECK(arg_exprs.front());
595  const auto arg_expr_ti = arg_exprs.front()->get_type_info();
596  if (arg_expr_ti.get_type() != kLINESTRING) {
597  throw QueryNotSupported(rex_function->getName() +
598  " expected LINESTRING argument. Received " +
599  arg_expr_ti.toString());
600  }
601  args.push_back(arg_exprs.front());
602 
603  auto oper_ti = args.back()->get_type_info(); // make a copy so we can override type
604  oper_ti.set_type(kPOINT);
605 
606  arg_ti = oper_ti; // TODO: remove
607 
608  return {makeExpr<Analyzer::GeoOperator>(oper_ti, rex_function->getName(), args)};
609  } else if (rex_function->getName() == "ST_SRID"sv) {
610  CHECK_EQ(size_t(1), rex_function->size());
611  const auto rex_scalar0 =
612  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
613  if (!rex_scalar0) {
614  throw QueryNotSupported(rex_function->getName() +
615  ": expects scalar as first argument");
616  }
617  auto arg0 = translateGeoFunctionArg(
618  rex_scalar0, arg_ti, with_bounds, with_render_group, expand_geo_col);
619  if (!IS_GEO(arg_ti.get_type())) {
620  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
621  }
622  return arg0;
623  } else if (rex_function->getName() == "ST_SetSRID"sv) {
624  CHECK_EQ(size_t(2), rex_function->size());
625  const auto rex_literal =
626  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
627  if (!rex_literal) {
628  throw QueryNotSupported(rex_function->getName() +
629  ": second argument is expected to be a literal");
630  }
631  const auto e = translateLiteral(rex_literal);
632  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
633  if (!ce || !e->get_type_info().is_integer()) {
634  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
635  }
636  int32_t srid = 0;
637  if (e->get_type_info().get_type() == kSMALLINT) {
638  srid = static_cast<int32_t>(ce->get_constval().smallintval);
639  } else if (e->get_type_info().get_type() == kTINYINT) {
640  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
641  } else if (e->get_type_info().get_type() == kINT) {
642  srid = static_cast<int32_t>(ce->get_constval().intval);
643  } else {
644  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
645  }
646 
647  const auto rex_scalar0 =
648  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
649  if (!rex_scalar0) {
650  throw QueryNotSupported(rex_function->getName() +
651  ": expects scalar as first argument");
652  }
653 
654  // Only convey the request to compress if dealing with 4326 geo
655  auto arg0 = translateGeoFunctionArg(rex_scalar0,
656  arg_ti,
657  with_bounds,
658  with_render_group,
659  expand_geo_col,
660  is_projection,
661  use_geo_expressions,
662  (try_to_compress && (srid == 4326)));
663 
664  CHECK(!arg0.empty() && arg0.front());
665  if (!IS_GEO(arg_ti.get_type()) && !use_geo_expressions) {
666  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
667  }
668  arg_ti.set_input_srid(srid); // Input SRID
669  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
670  if (auto geo_expr = std::dynamic_pointer_cast<Analyzer::GeoExpr>(arg0.front())) {
671  CHECK_EQ(arg0.size(), size_t(1));
672  auto ti = geo_expr->get_type_info();
673  ti.set_input_srid(srid);
674  ti.set_output_srid(srid);
675  return {geo_expr->add_cast(ti)};
676  }
677  return arg0;
678  } else if (rex_function->getName() == "CastToGeography"sv) {
679  CHECK_EQ(size_t(1), rex_function->size());
680  const auto rex_scalar0 =
681  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
682  if (!rex_scalar0) {
683  throw QueryNotSupported(rex_function->getName() +
684  ": expects scalar as first argument");
685  }
686  auto arg0 = translateGeoFunctionArg(rex_scalar0,
687  arg_ti,
688  with_bounds,
689  with_render_group,
690  expand_geo_col,
691  /*is_projection=*/false,
692  use_geo_expressions);
693  CHECK(!arg0.empty());
694  if (auto geo_expr = std::dynamic_pointer_cast<Analyzer::GeoExpr>(arg0.front())) {
695  auto arg_ti = geo_expr->get_type_info(); // make a copy
696  arg_ti.set_subtype(kGEOGRAPHY);
697  return {geo_expr->add_cast(arg_ti)};
698  }
699  if (use_geo_expressions) {
700  arg_ti = arg0.front()->get_type_info();
701  arg_ti.set_subtype(kGEOGRAPHY);
702  arg0.front()->set_type_info(arg_ti);
703  }
704  if (!IS_GEO(arg_ti.get_type())) {
705  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
706  }
707  if (arg_ti.get_output_srid() != 4326) {
708  throw QueryNotSupported(rex_function->getName() +
709  " expects geometry with SRID=4326");
710  }
711  arg_ti.set_subtype(kGEOGRAPHY);
712  return arg0;
713  } else if (rex_function->getName() == "ST_Point"sv) {
714  CHECK_EQ(size_t(2), rex_function->size());
715  arg_ti.set_type(kPOINT);
716  arg_ti.set_subtype(kGEOMETRY);
717  arg_ti.set_input_srid(0);
718  arg_ti.set_output_srid(0);
720 
721  auto coord1 = translateScalarRex(rex_function->getOperand(0));
722  auto coord2 = translateScalarRex(rex_function->getOperand(1));
723  auto d_ti = SQLTypeInfo(kDOUBLE, false);
724  auto cast_coord1 = coord1->add_cast(d_ti);
725  auto cast_coord2 = coord2->add_cast(d_ti);
726  // First try to fold to geo literal
727  auto folded_coord1 = fold_expr(cast_coord1.get());
728  auto folded_coord2 = fold_expr(cast_coord2.get());
729  auto const_coord1 = std::dynamic_pointer_cast<Analyzer::Constant>(folded_coord1);
730  auto const_coord2 = std::dynamic_pointer_cast<Analyzer::Constant>(folded_coord2);
731  if (const_coord1 && const_coord2 && !use_geo_expressions) {
732  CHECK(const_coord1->get_type_info().get_type() == kDOUBLE);
733  CHECK(const_coord2->get_type_info().get_type() == kDOUBLE);
734  std::string wkt = "POINT(" +
735  std::to_string(const_coord1->get_constval().doubleval) + " " +
736  std::to_string(const_coord2->get_constval().doubleval) + ")";
737  RexLiteral rex_literal{wkt, kTEXT, kNULLT, 0, 0, 0, 0};
738  auto args = translateGeoLiteral(&rex_literal, arg_ti, false);
739  CHECK(arg_ti.get_type() == kPOINT);
740  return args;
741  }
742  const auto is_local_alloca = !is_projection;
743  if (!is_local_alloca || use_geo_expressions) {
744  if (try_to_compress) {
745  arg_ti.set_input_srid(4326);
746  arg_ti.set_output_srid(4326);
747  }
748  return {makeExpr<Analyzer::GeoOperator>(
749  arg_ti,
750  rex_function->getName(),
751  std::vector<std::shared_ptr<Analyzer::Expr>>{folded_coord1, folded_coord2})};
752  }
753  // Couldn't fold to geo literal, construct [and compress] on the fly
754  auto da_ti = SQLTypeInfo(kARRAY, true);
755  da_ti.set_subtype(kDOUBLE);
756  da_ti.set_size(16);
757  if (try_to_compress) {
758  // Switch to compressed coord array
759  da_ti.set_subtype(kINT);
760  da_ti.set_size(8);
761  da_ti.set_input_srid(4326);
762  da_ti.set_output_srid(4326);
763  da_ti.set_compression(kENCODING_GEOINT);
764  da_ti.set_comp_param(32);
765  // Register point compression
766  arg_ti.set_input_srid(4326);
767  arg_ti.set_output_srid(4326);
769  arg_ti.set_comp_param(32);
770  }
771  auto cast_coords = {folded_coord1, folded_coord2};
772  auto ae = makeExpr<Analyzer::ArrayExpr>(da_ti, cast_coords, false, is_local_alloca);
773  SQLTypeInfo tia_ti = da_ti;
774  tia_ti.set_subtype(kTINYINT);
775  return {makeExpr<Analyzer::UOper>(tia_ti, false, kCAST, ae)};
776  } else if (rex_function->getName() == "ST_Centroid"sv) {
777  CHECK_EQ(size_t(1), rex_function->size());
778  arg_ti.set_type(kPOINT);
779  arg_ti.set_subtype(kGEOMETRY);
780  arg_ti.set_input_srid(0);
781  arg_ti.set_output_srid(0);
783 
784  SQLTypeInfo geo_ti;
785  int legacy_transform_srid = 0; // discard
786  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
787  geo_ti,
788  /*with_bounds=*/false,
789  /*with_render_group=*/false,
790  /*expand_geo_col=*/true,
791  /*is_projection=*/false,
792  /*use_geo_expressions=*/true);
793  CHECK_EQ(geoargs.size(), size_t(1));
794  if (geo_ti.get_output_srid() > 0) {
795  // Pick up the arg's srid
796  arg_ti.set_input_srid(geo_ti.get_output_srid());
797  arg_ti.set_output_srid(geo_ti.get_output_srid());
798  }
799  if (try_to_compress) {
800  // Point compression is requested by a higher level [4326] operation
801  if (geo_ti.get_output_srid() == 0) {
802  // srid-less geo is considered and is forced to be 4326
803  arg_ti.set_input_srid(4326);
804  arg_ti.set_output_srid(4326);
805  } else {
806  CHECK_EQ(arg_ti.get_output_srid(), 4326);
807  }
809  arg_ti.set_comp_param(32);
810  }
811  if (geo_ti.get_input_srid() != geo_ti.get_output_srid() &&
812  geo_ti.get_output_srid() > 0 &&
813  std::dynamic_pointer_cast<Analyzer::ColumnVar>(geoargs.front())) {
814  // Centroid argument is transformed before use,
815  // pass the transform to the geo operator
816  legacy_transform_srid = geo_ti.get_output_srid();
817  }
818  return {makeExpr<Analyzer::GeoOperator>(
819  arg_ti,
820  rex_function->getName(),
821  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()},
822  legacy_transform_srid > 0 ? std::make_optional<int>(legacy_transform_srid)
823  : std::nullopt)};
824  } else if (func_resolve(rex_function->getName(), "ST_ConvexHull"sv)) {
825  CHECK_EQ(size_t(1), rex_function->size());
826  // What geo type will the constructor return? Could be anything.
827  return {translateUnaryGeoConstructor(rex_function, arg_ti, with_bounds)};
828  } else if (func_resolve(rex_function->getName(),
829  "ST_Intersection"sv,
830  "ST_Difference"sv,
831  "ST_Union"sv,
832  "ST_Buffer"sv,
833  "ST_ConcaveHull"sv)) {
834  CHECK_EQ(size_t(2), rex_function->size());
835  // What geo type will the constructor return? Could be anything.
836  return {translateBinaryGeoConstructor(rex_function, arg_ti, with_bounds)};
837  } else if (func_resolve(rex_function->getName(), "ST_IsEmpty"sv, "ST_IsValid"sv)) {
838  CHECK_EQ(size_t(1), rex_function->size());
839  return {translateUnaryGeoPredicate(rex_function, arg_ti, with_bounds)};
840  } else if (func_resolve(rex_function->getName(), "ST_Equals"sv)) {
841  CHECK_EQ(size_t(2), rex_function->size());
842  return {translateBinaryGeoPredicate(rex_function, arg_ti, with_bounds)};
843  } else {
844  throw QueryNotSupported("Unsupported argument: " + rex_function->getName());
845  }
846  }
847  const auto rex_literal = dynamic_cast<const RexLiteral*>(rex_scalar);
848  if (rex_literal) {
849  if (use_geo_expressions) {
850  const auto translated_literal = translateLiteral(rex_literal);
851  auto const translated_literal_type = translated_literal->get_type_info().get_type();
852  if (!IS_STRING(translated_literal_type) && !IS_GEO(translated_literal_type)) {
853  // This stops crashes in the createGeoType call below due to datum.stringval
854  // being uninitialized when the datum isn't even a string, let alone a geo string
855  // There needs to be specific handling for ST_NumGeometries in the code above
856  // but I don't know what category it would fall over (it's not GEOS, and it
857  // returns an INT, not a BOOL or other geo)
858  // simon.eves 8/15/22
859  throw QueryNotSupported("Geospatial function requires geo literal.");
860  }
861  const auto constant_expr =
862  dynamic_cast<const Analyzer::Constant*>(translated_literal.get());
863  CHECK(constant_expr);
864  if (constant_expr->get_is_null()) {
865  // TODO: we could lift this limitation by assuming a minimum type per function
866  throw QueryNotSupported("Geospatial functions require typed nulls.");
867  }
868  const auto& datum = constant_expr->get_constval();
869  CHECK(datum.stringval);
870  auto geospatial_base = Geospatial::GeoTypesFactory::createGeoType(*datum.stringval);
871  CHECK(geospatial_base);
872  SQLTypeInfo ti;
873  ti.set_type(get_ti_from_geo(geospatial_base.get()));
874  if (arg_ti.get_subtype() == kGEOGRAPHY) {
876  } else {
878  }
879  ti.set_input_srid(arg_ti.get_input_srid());
880  ti.set_output_srid(arg_ti.get_output_srid() == 0 ? arg_ti.get_input_srid()
881  : arg_ti.get_output_srid());
882  // TODO: remove dependence on arg_ti
883  if (ti.get_output_srid() == 4326 || arg_ti.get_compression() == kENCODING_GEOINT) {
885  ti.set_comp_param(32);
886  }
887  ti.set_notnull(true);
888  // Before removing dependence on arg_ti need to note that ST_Transform uses it
889  // as a vehicle to pass transform output SRID to its args.
890  // arg_ti is also expected to be filled with relevant data, which wasn't done here.
891  // Not filling arg_ti with the geo constant data (which went to ti instead)
892  // resulted in GeoConstant::add_cast adopting a corrupt type info,
893  // which later killed codegen. Need to complete arg_ti composition:
894  arg_ti = ti;
895  return {makeExpr<Analyzer::GeoConstant>(std::move(geospatial_base), ti)};
896  }
897  return translateGeoLiteral(rex_literal, arg_ti, with_bounds);
898  }
899  throw QueryNotSupported("Geo function argument not supported");
900 }
901 
902 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoProjection(
903  const RexFunctionOperator* rex_function,
904  SQLTypeInfo& ti,
905  const bool with_bounds) const {
906  // note that this is a bit of a misnomer, as ST_SetSRID embedded in a transform will
907  // eventually use geo expressions -- just not here
908  const bool use_geo_projections = !(rex_function->getName() == "ST_GeomFromText" ||
909  rex_function->getName() == "ST_GeogFromText" ||
910  rex_function->getName() == "ST_SetSRID");
911  auto geoargs = translateGeoFunctionArg(rex_function,
912  ti,
913  /*with_bounds=*/false,
914  /*with_render_group=*/false,
915  /*expand_geo_col=*/true,
916  /*is_projection=*/true,
917  /*use_geo_expressions=*/use_geo_projections);
918  CHECK(!geoargs.empty());
919  if (std::dynamic_pointer_cast<const Analyzer::GeoExpr>(geoargs.front()) &&
920  !geoargs.front()->get_type_info().is_array()) {
921  if (rex_function->getName() == "ST_Transform" &&
922  std::dynamic_pointer_cast<const Analyzer::GeoConstant>(geoargs.front())) {
923  return makeExpr<Analyzer::GeoUOper>(
924  Geospatial::GeoBase::GeoOp::kPROJECTION, ti, ti, geoargs);
925  }
926  // GeoExpression
927  return geoargs.front();
928  }
929  bool allow_gdal_transform = false;
930  if (rex_function->getName() == "ST_Transform") {
931  const auto rex_scalar0 = dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
932  const auto rex_function0 = dynamic_cast<const RexFunctionOperator*>(rex_scalar0);
933  if (rex_function0 && func_resolve(rex_function0->getName(),
934  "ST_Intersection"sv,
935  "ST_Difference"sv,
936  "ST_Union"sv,
937  "ST_Buffer"sv,
938  "ST_ConcaveHull"sv,
939  "ST_ConvexHull"sv)) {
940  // Allow projection of gdal-transformed geos outputs
941  allow_gdal_transform = true;
942  }
943  }
944  if (use_geo_projections && !allow_gdal_transform) {
945  throw std::runtime_error("Geospatial projection for function " +
946  rex_function->toString(RelRexToStringConfig::defaults()) +
947  " not yet supported in this context");
948  }
949  return makeExpr<Analyzer::GeoUOper>(
950  Geospatial::GeoBase::GeoOp::kPROJECTION, ti, ti, geoargs);
951 }
952 
953 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateBinaryGeoConstructor(
954  const RexFunctionOperator* rex_function,
955  SQLTypeInfo& ti,
956  const bool with_bounds) const {
957 #ifndef ENABLE_GEOS
958  throw QueryNotSupported(rex_function->getName() +
959  " geo constructor requires enabled GEOS support");
960 #endif
962  if (rex_function->getName() == "ST_Difference"sv) {
964  } else if (rex_function->getName() == "ST_Union"sv) {
966  } else if (rex_function->getName() == "ST_Buffer"sv) {
968  } else if (rex_function->getName() == "ST_ConcaveHull"sv) {
970  }
971 
974  SQLTypeInfo arg0_ti;
975  SQLTypeInfo arg1_ti;
976  if (func_resolve(rex_function->getName(),
977  "ST_Intersection"sv,
978  "ST_Difference"sv,
979  "ST_Union"sv,
980  "ST_Buffer"sv,
981  "ST_ConcaveHull"sv)) {
982  // First arg: geometry
983  geoargs0 = translateGeoFunctionArg(rex_function->getOperand(0),
984  arg0_ti,
985  false,
986  false,
987  true,
988  true,
989  false,
990  false,
991  /* allow_gdal_transforms = */ true);
992  }
993  if (func_resolve(rex_function->getName(),
994  "ST_Intersection"sv,
995  "ST_Difference"sv,
996  "ST_Union"sv)) {
997  // Second arg: geometry
998  geoargs1 = translateGeoFunctionArg(rex_function->getOperand(1),
999  arg1_ti,
1000  false,
1001  false,
1002  true,
1003  true,
1004  false,
1005  false,
1006  /* allow_gdal_transforms = */ true);
1007  if (arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
1008  throw QueryNotSupported(rex_function->getName() +
1009  " geo constructor requires arguments with matching srids");
1010  }
1011  } else if (func_resolve(rex_function->getName(), "ST_Buffer"sv, "ST_ConcaveHull"sv)) {
1012  // Second arg: double scalar
1013  auto param_expr = translateScalarRex(rex_function->getOperand(1));
1014  arg1_ti = SQLTypeInfo(kDOUBLE, false);
1015  if (param_expr->get_type_info().get_type() != kDOUBLE) {
1016  param_expr = param_expr->add_cast(arg1_ti);
1017  }
1018  geoargs1 = {param_expr};
1019  }
1020 
1021  // Record the optional transform request that can be sent by an ecompassing TRANSFORM
1022  auto srid = ti.get_output_srid();
1023  // Build the typeinfo of the constructed geometry
1024  SQLTypeInfo arg_ti = arg0_ti;
1025  arg_ti.set_type(kMULTIPOLYGON);
1026  arg_ti.set_subtype(kGEOMETRY);
1027  arg_ti.set_compression(kENCODING_NONE); // Constructed geometries are not compressed
1028  arg_ti.set_comp_param(0);
1029  arg_ti.set_input_srid(arg0_ti.get_output_srid());
1030  if (srid > 0) {
1031  if (arg_ti.get_input_srid() > 0) {
1032  // Constructed geometry to be transformed to srid given by encompassing transform
1033  arg_ti.set_output_srid(srid);
1034  } else {
1035  throw QueryNotSupported("Transform of geo constructor " + rex_function->getName() +
1036  " requires its argument(s) to have a valid srid");
1037  }
1038  } else {
1039  arg_ti.set_output_srid(arg_ti.get_input_srid()); // No encompassing transform
1040  }
1041  // If there was an output transform, it's now embedded into arg_ti and the geo operator.
1042  // Now de-register the transform from the return typeinfo:
1043  ti = arg_ti;
1045  return makeExpr<Analyzer::GeoBinOper>(op, arg_ti, arg0_ti, arg1_ti, geoargs0, geoargs1);
1046 }
1047 
1048 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateUnaryGeoPredicate(
1049  const RexFunctionOperator* rex_function,
1050  SQLTypeInfo& ti,
1051  const bool with_bounds) const {
1052 #ifndef ENABLE_GEOS
1053  throw QueryNotSupported(rex_function->getName() +
1054  " geo predicate requires enabled GEOS support");
1055 #endif
1056  SQLTypeInfo arg_ti;
1057  auto geoargs = translateGeoFunctionArg(
1058  rex_function->getOperand(0), arg_ti, false, false, true, true);
1059  ti = SQLTypeInfo(kBOOLEAN, false);
1060  auto op = (rex_function->getName() == "ST_IsEmpty"sv)
1063  return makeExpr<Analyzer::GeoUOper>(op, ti, arg_ti, geoargs);
1064 }
1065 
1066 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateBinaryGeoPredicate(
1067  const RexFunctionOperator* rex_function,
1068  SQLTypeInfo& ti,
1069  const bool with_bounds) const {
1070  if (rex_function->getName() != "ST_Equals"sv) {
1071  throw QueryNotSupported(rex_function->getName() + " geo predicate is not supported");
1072  }
1073 #ifndef ENABLE_GEOS
1074  throw QueryNotSupported(rex_function->getName() +
1075  " geo predicate requires enabled GEOS support");
1076 #endif
1077  SQLTypeInfo arg0_ti;
1078  auto geoargs0 = translateGeoFunctionArg(
1079  rex_function->getOperand(0), arg0_ti, false, false, true, true);
1080  SQLTypeInfo arg1_ti;
1081  auto geoargs1 = translateGeoFunctionArg(
1082  rex_function->getOperand(1), arg1_ti, false, false, true, true);
1083  ti = SQLTypeInfo(kBOOLEAN, false);
1085  return makeExpr<Analyzer::GeoBinOper>(op, ti, arg0_ti, arg1_ti, geoargs0, geoargs1);
1086 }
1087 
1088 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateUnaryGeoConstructor(
1089  const RexFunctionOperator* rex_function,
1090  SQLTypeInfo& ti,
1091  const bool with_bounds) const {
1092 #ifndef ENABLE_GEOS
1093  throw QueryNotSupported(rex_function->getName() +
1094  " geo constructor requires enabled GEOS support");
1095 #endif
1097 
1098  Analyzer::ExpressionPtrVector geoargs0{};
1099  SQLTypeInfo arg0_ti;
1100  if (func_resolve(rex_function->getName(), "ST_ConvexHull"sv)) {
1101  // First arg: geometry
1102  geoargs0 = translateGeoFunctionArg(rex_function->getOperand(0),
1103  arg0_ti,
1104  false,
1105  false,
1106  true,
1107  true,
1108  false,
1109  false,
1110  /* allow_gdal_transforms = */ true);
1111  }
1112 
1113  // Record the optional transform request that can be sent by an ecompassing TRANSFORM
1114  auto srid = ti.get_output_srid();
1115  // Build the typeinfo of the constructed geometry
1116  SQLTypeInfo arg_ti = arg0_ti;
1117  arg_ti.set_type(kMULTIPOLYGON);
1118  arg_ti.set_subtype(kGEOMETRY);
1119  arg_ti.set_compression(kENCODING_NONE); // Constructed geometries are not compressed
1120  arg_ti.set_comp_param(0);
1121  arg_ti.set_input_srid(arg0_ti.get_output_srid());
1122  if (srid > 0) {
1123  if (arg_ti.get_input_srid() > 0) {
1124  // Constructed geometry to be transformed to srid given by encompassing transform
1125  arg_ti.set_output_srid(srid);
1126  } else {
1127  throw QueryNotSupported("Transform of geo constructor " + rex_function->getName() +
1128  " requires its argument(s) to have a valid srid");
1129  }
1130  } else {
1131  arg_ti.set_output_srid(arg_ti.get_input_srid()); // No encompassing transform
1132  }
1133  // If there was an output transform, it's now embedded into arg_ti and the geo operator.
1134  // Now de-register the transform from the return typeinfo:
1135  ti = arg_ti;
1137  return makeExpr<Analyzer::GeoUOper>(op, arg_ti, arg0_ti, geoargs0);
1138 }
1139 
1140 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateUnaryGeoFunction(
1141  const RexFunctionOperator* rex_function) const {
1142  CHECK_EQ(size_t(1), rex_function->size());
1143 
1144  std::string specialized_geofunc{rex_function->getName()};
1145 
1146  // Geo function calls which do not need the coords col but do need cols associated
1147  // with physical coords (e.g. ring_sizes / poly_rings)
1148  if (rex_function->getName() == "ST_NRings"sv) {
1149  SQLTypeInfo arg_ti;
1150  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
1151  arg_ti,
1152  /*with_bounds=*/false,
1153  /*with_render_group=*/false,
1154  /*expand_geo_col=*/true,
1155  /*is_projection=*/false,
1156  /*use_geo_expressions=*/true);
1157  if (!IS_GEO_POLY(arg_ti.get_type())) {
1158  throw QueryNotSupported(rex_function->getName() +
1159  " expects a POLYGON or MULTIPOLYGON");
1160  }
1161  CHECK_EQ(geoargs.size(), size_t(1));
1162  arg_ti = rex_function->getType(); // TODO: remove
1163  return makeExpr<Analyzer::GeoOperator>(
1164  rex_function->getType(),
1165  rex_function->getName(),
1166  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
1167  } else if (rex_function->getName() == "ST_NumGeometries"sv) {
1168  SQLTypeInfo arg_ti;
1169  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
1170  arg_ti,
1171  /*with_bounds=*/false,
1172  /*with_render_group=*/false,
1173  /*expand_geo_col=*/true,
1174  /*is_projection=*/false,
1175  /*use_geo_expressions=*/true);
1176  if (!IS_GEO(arg_ti.get_type())) {
1177  throw QueryNotSupported(rex_function->getName() + " expects a geo parameter");
1178  }
1179  CHECK_EQ(geoargs.size(), size_t(1));
1180  arg_ti = rex_function->getType(); // TODO: remove
1181  return makeExpr<Analyzer::GeoOperator>(
1182  rex_function->getType(),
1183  rex_function->getName(),
1184  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
1185  } else if (rex_function->getName() == "ST_NPoints"sv) {
1186  SQLTypeInfo arg_ti;
1187  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
1188  arg_ti,
1189  /*with_bounds=*/false,
1190  /*with_render_group=*/false,
1191  /*expand_geo_col=*/true,
1192  /*is_projection=*/false,
1193  /*use_geo_expressions=*/true);
1194  CHECK_EQ(geoargs.size(), size_t(1));
1195  return makeExpr<Analyzer::GeoOperator>(
1196  rex_function->getType(),
1197  rex_function->getName(),
1198  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()});
1199  } else if (func_resolve(rex_function->getName(), "ST_Perimeter"sv, "ST_Area"sv)) {
1200  SQLTypeInfo arg_ti;
1201  int legacy_transform_srid = 0; // discard
1202  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
1203  arg_ti,
1204  /*with_bounds=*/false,
1205  /*with_render_group=*/false,
1206  /*expand_geo_col=*/true,
1207  /*is_projection=*/false,
1208  /*use_geo_expressions=*/true);
1209  CHECK_EQ(geoargs.size(), size_t(1));
1210  if (arg_ti.get_input_srid() != arg_ti.get_output_srid() &&
1211  arg_ti.get_output_srid() > 0 &&
1212  std::dynamic_pointer_cast<Analyzer::ColumnVar>(geoargs.front())) {
1213  // legacy transform
1214  legacy_transform_srid = arg_ti.get_output_srid();
1215  // Reset the transform, transform will be given to the operator as an override
1216  arg_ti = geoargs.front()->get_type_info();
1217  }
1218  if (!IS_GEO_POLY(arg_ti.get_type())) {
1219  throw QueryNotSupported(rex_function->getName() +
1220  " expects a POLYGON or MULTIPOLYGON");
1221  }
1222  return makeExpr<Analyzer::GeoOperator>(
1223  rex_function->getType(),
1224  rex_function->getName(),
1225  std::vector<std::shared_ptr<Analyzer::Expr>>{geoargs.front()},
1226  legacy_transform_srid > 0 ? std::make_optional<int>(legacy_transform_srid)
1227  : std::nullopt);
1228  }
1229 
1230  // Accessors for poly bounds and render group for in-situ poly render queries
1231  if (func_resolve(rex_function->getName(), "HeavyDB_Geo_PolyBoundsPtr"sv)) {
1232  SQLTypeInfo arg_ti;
1233  // get geo column plus bounds only (not expanded)
1234  auto geoargs =
1235  translateGeoFunctionArg(rex_function->getOperand(0), arg_ti, true, false, false);
1236  // this function only works on polys
1237  if (!IS_GEO_POLY(arg_ti.get_type())) {
1238  throw QueryNotSupported(rex_function->getName() +
1239  " expects a POLYGON or MULTIPOLYGON");
1240  }
1241  // only need the bounds argument (last), discard the rest
1242  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
1243  // done
1244  return makeExpr<Analyzer::FunctionOper>(
1245  rex_function->getType(), specialized_geofunc, geoargs);
1246  } else if (func_resolve(rex_function->getName(), "HeavyDB_Geo_PolyRenderGroup"sv)) {
1247  SQLTypeInfo arg_ti;
1248  // get geo column plus render_group only (not expanded)
1249  auto geoargs =
1250  translateGeoFunctionArg(rex_function->getOperand(0), arg_ti, false, true, false);
1251  // this function only works on polys
1252  if (!IS_GEO_POLY(arg_ti.get_type())) {
1253  throw QueryNotSupported(rex_function->getName() +
1254  " expects a POLYGON or MULTIPOLYGON");
1255  }
1256  // only need the render_group argument (last), discard the rest
1257  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
1258  // done
1259  return makeExpr<Analyzer::FunctionOper>(
1260  rex_function->getType(), specialized_geofunc, geoargs);
1261  }
1262 
1263  // start to move geo expressions above the generic translation call, as geo expression
1264  // error handling can differ
1265  if (func_resolve(rex_function->getName(), "ST_X"sv, "ST_Y"sv)) {
1266  SQLTypeInfo arg_ti;
1267  auto new_geoargs = translateGeoFunctionArg(rex_function->getOperand(0),
1268  arg_ti,
1269  /*with_bounds=*/false,
1270  /*with_render_group=*/false,
1271  /*expand_geo_col=*/true,
1272  /*is_projection=*/true,
1273  /*use_geo_expressions=*/true);
1274  CHECK_EQ(new_geoargs.size(), size_t(1));
1275  CHECK(new_geoargs.front());
1276  const auto& arg_expr_ti = new_geoargs.front()->get_type_info();
1277  if (arg_expr_ti.get_type() != kPOINT) {
1278  throw QueryNotSupported(rex_function->getName() + " expects a POINT");
1279  }
1280  auto function_ti = rex_function->getType();
1281  if (std::dynamic_pointer_cast<Analyzer::GeoOperator>(new_geoargs.front())) {
1282  function_ti.set_notnull(false);
1283  }
1284  if (std::dynamic_pointer_cast<Analyzer::GeoConstant>(new_geoargs.front())) {
1285  // TODO(adb): fixup null handling
1286  function_ti.set_notnull(true);
1287  }
1288  return makeExpr<Analyzer::GeoOperator>(
1289  function_ti,
1290  rex_function->getName(),
1291  std::vector<std::shared_ptr<Analyzer::Expr>>{new_geoargs.front()});
1292  }
1293 
1294  // All functions below use geo col as reference and expand it as necessary
1295  SQLTypeInfo arg_ti;
1296  bool with_bounds = true;
1297  auto geoargs = translateGeoFunctionArg(
1298  rex_function->getOperand(0), arg_ti, with_bounds, false, false);
1299 
1300  if (rex_function->getName() == "ST_SRID"sv) {
1301  Datum output_srid;
1302  output_srid.intval = arg_ti.get_output_srid();
1303  return makeExpr<Analyzer::Constant>(kINT, false, output_srid);
1304  }
1305 
1306  if (func_resolve(
1307  rex_function->getName(), "ST_XMin"sv, "ST_YMin"sv, "ST_XMax"sv, "ST_YMax"sv)) {
1308  // If type has bounds - use them, otherwise look at coords
1309  if (arg_ti.has_bounds()) {
1310  // Only need the bounds argument, discard the rest
1311  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
1312 
1313  // Supply srids too - transformed geo would have a transformed bounding box
1314  Datum input_srid;
1315  input_srid.intval = arg_ti.get_input_srid();
1316  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1317  Datum output_srid;
1318  output_srid.intval = arg_ti.get_output_srid();
1319  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1320 
1321  specialized_geofunc += "_Bounds"s;
1322  return makeExpr<Analyzer::FunctionOper>(
1323  rex_function->getType(), specialized_geofunc, geoargs);
1324  }
1325  }
1326 
1327  // Unless overriden, function is assumed to be interested in the first geoarg only,
1328  // which may be a geo object (e.g. geo column), or a coord array (e.g. geo literal)
1329  auto discard_after_arg = 1;
1330 
1331  if (rex_function->getName() == "ST_Length"sv) {
1332  if (arg_ti.get_type() != kLINESTRING && arg_ti.get_type() != kMULTILINESTRING) {
1333  throw QueryNotSupported(rex_function->getName() +
1334  " expects LINESTRING or MULTILINESTRING");
1335  }
1336  if (arg_ti.get_type() == kMULTILINESTRING) {
1337  auto ti0 = geoargs[0]->get_type_info();
1338  if (ti0.get_type() == kARRAY && ti0.get_subtype() == kTINYINT) {
1339  // Received expanded geo: widen the reach to grab linestring size array as well
1340  discard_after_arg = 2;
1341  }
1342  }
1343  specialized_geofunc += suffix(arg_ti.get_type());
1344  if (arg_ti.get_subtype() == kGEOGRAPHY && arg_ti.get_output_srid() == 4326) {
1345  if (arg_ti.get_type() == kMULTILINESTRING) {
1346  throw QueryNotSupported(rex_function->getName() +
1347  " Geodesic is not supported for MULTILINESTRING");
1348  }
1349  specialized_geofunc += "_Geodesic"s;
1350  }
1351  }
1352 
1353  geoargs.erase(geoargs.begin() + discard_after_arg, geoargs.end());
1354 
1355  // Add input compression mode and SRID args to enable on-the-fly
1356  // decompression/transforms
1357  Datum input_compression;
1358  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1359  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1360  Datum input_srid;
1361  input_srid.intval = arg_ti.get_input_srid();
1362  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1363 
1364  // Add output SRID arg to enable on-the-fly transforms
1365  Datum output_srid;
1366  output_srid.intval = arg_ti.get_output_srid();
1367  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1368 
1369  return makeExpr<Analyzer::FunctionOper>(
1370  rex_function->getType(), specialized_geofunc, geoargs);
1371 }
1372 
1373 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateBinaryGeoFunction(
1374  const RexFunctionOperator* rex_function) const {
1375  auto function_name = rex_function->getName();
1376  auto return_type = rex_function->getType();
1377 
1378  if (function_name == "ST_Overlaps"sv) {
1379  // Overlaps join is the only implementation supported for now, only translate bounds
1380  CHECK_EQ(size_t(2), rex_function->size());
1381  auto extract_geo_bounds_from_input =
1382  [this, &rex_function](const size_t index) -> std::shared_ptr<Analyzer::Expr> {
1383  const auto rex_input =
1384  dynamic_cast<const RexInput*>(rex_function->getOperand(index));
1385  if (rex_input) {
1386  SQLTypeInfo ti;
1387  const auto exprs = translateGeoColumn(rex_input, ti, true, false, false);
1388  CHECK_GT(exprs.size(), size_t(0));
1389  if (ti.get_type() == kPOINT) {
1390  throw std::runtime_error("ST_Overlaps is not supported for point arguments.");
1391  } else {
1392  return exprs.back();
1393  }
1394  } else {
1395  throw std::runtime_error(
1396  "Only inputs are supported as arguments to ST_Overlaps for now.");
1397  }
1398  };
1399  std::vector<std::shared_ptr<Analyzer::Expr>> geo_args;
1400  geo_args.push_back(extract_geo_bounds_from_input(0));
1401  geo_args.push_back(extract_geo_bounds_from_input(1));
1402 
1403  return makeExpr<Analyzer::FunctionOper>(return_type, function_name, geo_args);
1404  }
1405 
1406  if (function_name == "ST_Distance"sv || function_name == "ST_MaxDistance"sv) {
1407  CHECK_EQ(size_t(2), rex_function->size());
1408  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1409  int legacy_transform_srid = 0;
1410  for (size_t i = 0; i < rex_function->size(); i++) {
1411  SQLTypeInfo arg0_ti; // discard
1412  auto geoargs = translateGeoFunctionArg(rex_function->getOperand(i),
1413  arg0_ti,
1414  /*with_bounds=*/false, // TODO
1415  /*with_render_group=*/false,
1416  /*expand_geo_col=*/false,
1417  /*is_projection = */ false,
1418  /*use_geo_expressions=*/true);
1419  if (arg0_ti.get_input_srid() != arg0_ti.get_output_srid() &&
1420  arg0_ti.get_output_srid() > 0 &&
1421  std::dynamic_pointer_cast<Analyzer::ColumnVar>(geoargs.front())) {
1422  // legacy transform
1423  CHECK(legacy_transform_srid == 0 ||
1424  legacy_transform_srid == arg0_ti.get_output_srid());
1425  legacy_transform_srid = arg0_ti.get_output_srid();
1426  }
1427  args.insert(args.end(), geoargs.begin(), geoargs.end());
1428  }
1429  return makeExpr<Analyzer::GeoOperator>(
1430  SQLTypeInfo(kDOUBLE, /*not_null=*/false),
1431  function_name,
1432  args,
1433  legacy_transform_srid > 0 ? std::make_optional<int>(legacy_transform_srid)
1434  : std::nullopt);
1435  }
1436 
1437  bool swap_args = false;
1438  bool with_bounds = false;
1439  bool negate_result = false;
1440  Analyzer::ExpressionPtr threshold_expr = nullptr;
1441  Analyzer::ExpressionPtr compare_expr = nullptr;
1442  if (function_name == "ST_DWithin"sv) {
1443  CHECK_EQ(size_t(3), rex_function->size());
1444  function_name = "ST_Distance";
1445  return_type = SQLTypeInfo(kDOUBLE, false);
1446  // Inject ST_DWithin's short-circuiting threshold into ST_MaxDistance
1447  threshold_expr = translateScalarRex(rex_function->getOperand(2));
1448  } else if (function_name == "ST_Equals"sv) {
1449  // Translate ST_Equals(g1,g2) to ST_Distance(g1,g2)<=0.0
1450  CHECK_EQ(size_t(2), rex_function->size());
1451  function_name = "ST_Distance";
1452  return_type = SQLTypeInfo(kDOUBLE, false);
1453  threshold_expr = nullptr;
1454  Datum d;
1455  d.doubleval = 0.0;
1456  compare_expr = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
1457  } else if (function_name == "ST_DFullyWithin"sv) {
1458  CHECK_EQ(size_t(3), rex_function->size());
1459  function_name = "ST_MaxDistance";
1460  return_type = SQLTypeInfo(kDOUBLE, false);
1461  // TODO: inject ST_DFullyWithin's short-circuiting threshold into ST_MaxDistance
1462  threshold_expr = nullptr;
1463  } else if (function_name == "ST_Distance"sv) {
1464  // TODO: pick up an outside short-circuiting threshold and inject into ST_Distance
1465  threshold_expr = nullptr;
1466  } else if (function_name == "ST_MaxDistance"sv) {
1467  // TODO: pick up an outside short-circuiting threshold and inject into
1468  // ST_MaxDistance
1469  threshold_expr = nullptr;
1470  } else {
1471  CHECK_EQ(size_t(2), rex_function->size());
1472  }
1473  if (function_name == "ST_Within"sv) {
1474  function_name = "ST_Contains";
1475  swap_args = true;
1476  } else if (function_name == "ST_Disjoint"sv) {
1477  function_name = "ST_Intersects";
1478  negate_result = true;
1479  }
1480  if (func_resolve(
1481  function_name, "ST_Contains"sv, "ST_Intersects"sv, "ST_Approx_Overlaps"sv)) {
1482  with_bounds = true;
1483  }
1484 
1485  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
1486  SQLTypeInfo arg0_ti;
1487  SQLTypeInfo arg1_ti;
1488 
1489  // Proactively try to compress the first arg of ST_Intersects to preempt arg swap
1490  bool try_to_compress_arg0 = g_enable_geo_ops_on_uncompressed_coords &&
1491  func_resolve(function_name, "ST_Intersects"sv);
1492 
1493  auto geoargs0 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 1 : 0),
1494  arg0_ti,
1495  with_bounds,
1496  false,
1497  false,
1498  false,
1499  false,
1500  try_to_compress_arg0);
1501  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1502 
1503  // If first arg is compressed, try to compress the second one to be able to
1504  // switch to faster implementations working directly on uncompressed coords
1505  bool try_to_compress_arg1 =
1507  func_resolve(function_name, "ST_Contains"sv, "ST_Intersects"sv) &&
1508  arg0_ti.get_compression() == kENCODING_GEOINT &&
1509  arg0_ti.get_output_srid() == 4326);
1510 
1511  auto geoargs1 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 0 : 1),
1512  arg1_ti,
1513  with_bounds,
1514  false,
1515  false,
1516  false,
1517  false,
1518  try_to_compress_arg1);
1519  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1520 
1521  if (arg0_ti.get_subtype() != kNULLT && arg0_ti.get_subtype() != arg1_ti.get_subtype()) {
1522  throw QueryNotSupported(rex_function->getName() +
1523  " accepts either two GEOGRAPHY or two GEOMETRY arguments");
1524  }
1525  // Check SRID match if at least one is set/valid
1526  if ((arg0_ti.get_output_srid() > 0 || arg1_ti.get_output_srid() > 0) &&
1527  arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
1528  throw QueryNotSupported(rex_function->getName() + " cannot accept different SRIDs");
1529  }
1530  if (compare_expr) {
1531  // We could fold the check to false here if argument geo types are different, e.g.
1532  // POLYGON vs POINT. However, tiny POLYGON could be "spatially" equal to a POINT.
1533  if (arg0_ti.get_type() != kPOINT || arg1_ti.get_type() != kPOINT) {
1534  // ST_Equals is translated to a simple distance check for POINTs,
1535  // otherwise geometries are passed to GEOS's Equals
1536  return nullptr;
1537  }
1538  // Look at POINT compression modes.
1539  if (arg0_ti.get_compression() != arg1_ti.get_compression()) {
1540  if ((arg0_ti.get_compression() == kENCODING_GEOINT &&
1541  arg0_ti.get_comp_param() == 32 &&
1542  arg1_ti.get_compression() == kENCODING_NONE) ||
1543  (arg0_ti.get_compression() == kENCODING_NONE &&
1544  arg1_ti.get_compression() == kENCODING_GEOINT &&
1545  arg0_ti.get_comp_param() == 32)) {
1546  // Spatial equality comparison of a compressed point vs uncompressed point.
1547  // Introduce tolerance into distance calculation and comparison, translate
1548  // ST_Equals(g1,g2) to ST_Distance(g1,g2,thereshold=tolerance)<=tolerance
1549  Datum tolerance;
1550  // Tolerance representing 0.44" to cover shifts due to GEOINT(32) compression
1551  tolerance.doubleval = TOLERANCE_GEOINT32;
1552  threshold_expr = makeExpr<Analyzer::Constant>(kDOUBLE, false, tolerance);
1553  compare_expr = threshold_expr;
1554  } else {
1555  throw QueryNotSupported(
1556  rex_function->getName() +
1557  " unable to calculate compression tolerance for arguments");
1558  }
1559  }
1560  }
1561  if (arg0_ti.get_type() == kMULTILINESTRING || arg1_ti.get_type() == kMULTILINESTRING) {
1562  throw QueryNotSupported(rex_function->getName() +
1563  " currently doesn't support this argument combination");
1564  }
1565 
1566  auto can_use_compressed_coords = [](const SQLTypeInfo& i0_ti,
1567  const Analyzer::ExpressionPtrVector& i0_operands,
1568  const SQLTypeInfo& i1_ti,
1569  const Analyzer::ExpressionPtrVector& i1_operands) {
1570  const bool i0_is_poly =
1571  i0_ti.get_type() == kPOLYGON || i0_ti.get_type() == kMULTIPOLYGON;
1572  const bool i1_is_point = i1_ti.get_type() == kPOINT;
1573  const bool i1_is_literal =
1574  i1_operands.size() == 1 && std::dynamic_pointer_cast<const Analyzer::Constant>(
1575  i1_operands.front()) != nullptr;
1576  return (i0_is_poly && !i1_is_literal && i1_is_point &&
1577  i0_ti.get_compression() == kENCODING_GEOINT &&
1578  i0_ti.get_input_srid() == i0_ti.get_output_srid() &&
1579  i0_ti.get_compression() == i1_ti.get_compression() &&
1580  i1_ti.get_input_srid() == i1_ti.get_output_srid());
1581  };
1582  if (g_enable_geo_ops_on_uncompressed_coords && function_name == "ST_Contains"sv) {
1583  if (can_use_compressed_coords(arg0_ti, geoargs0, arg1_ti, geoargs1)) {
1584  // Switch to Contains implementation working directly on uncompressed coords
1585  function_name = "ST_cContains";
1586  }
1587  }
1588  if (g_enable_geo_ops_on_uncompressed_coords && function_name == "ST_Intersects"sv) {
1589  if (can_use_compressed_coords(arg0_ti, geoargs0, arg1_ti, geoargs1)) {
1590  // Switch to Intersects implementation working directly on uncompressed coords
1591  function_name = "ST_cIntersects";
1592  } else if (can_use_compressed_coords(arg1_ti, geoargs1, arg0_ti, geoargs0)) {
1593  // Switch to Intersects implementation working on uncompressed coords, swapped args
1594  function_name = "ST_cIntersects";
1595  geoargs.clear();
1596  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1597  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1598  auto tmp_ti = arg0_ti;
1599  arg0_ti = arg1_ti;
1600  arg1_ti = tmp_ti;
1601  }
1602  }
1603 
1604  std::string specialized_geofunc{function_name + suffix(arg0_ti.get_type()) +
1605  suffix(arg1_ti.get_type())};
1606 
1607  if (arg0_ti.get_subtype() == kGEOGRAPHY && arg0_ti.get_output_srid() == 4326) {
1608  // Need to call geodesic runtime functions
1609  if (function_name == "ST_Distance"sv) {
1610  if ((arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kPOINT) ||
1611  (arg0_ti.get_type() == kLINESTRING && arg1_ti.get_type() == kPOINT) ||
1612  (arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kLINESTRING)) {
1613  // Geodesic distance between points
1614  specialized_geofunc += "_Geodesic"s;
1615  } else {
1616  throw QueryNotSupported(function_name +
1617  " currently doesn't accept non-POINT geographies");
1618  }
1619  } else if (rex_function->getName() == "ST_Contains"sv) {
1620  // We currently don't have a geodesic implementation of ST_Contains,
1621  // allowing calls to a [less precise] cartesian implementation.
1622  } else {
1623  throw QueryNotSupported(function_name + " doesn't accept geographies");
1624  }
1625  } else if (function_name == "ST_Distance"sv && rex_function->size() == 3) {
1626  if (arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kPOINT) {
1627  // Cartesian distance between points used by ST_DWithin - switch to faster Squared
1628  specialized_geofunc += "_Squared"s;
1629  }
1630  }
1631 
1632  // Add first input's compression mode and SRID args to enable on-the-fly
1633  // decompression/transforms
1634  Datum input_compression0;
1635  input_compression0.intval = Geospatial::get_compression_scheme(arg0_ti);
1636  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1637  Datum input_srid0;
1638  input_srid0.intval = arg0_ti.get_input_srid();
1639  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1640 
1641  // Add second input's compression mode and SRID args to enable on-the-fly
1642  // decompression/transforms
1643  Datum input_compression1;
1644  input_compression1.intval = Geospatial::get_compression_scheme(arg1_ti);
1645  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1646  Datum input_srid1;
1647  input_srid1.intval = arg1_ti.get_input_srid();
1648  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1649 
1650  // Add output SRID arg to enable on-the-fly transforms
1651  Datum output_srid;
1652  output_srid.intval = arg0_ti.get_output_srid();
1653  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1654 
1655  // Some geo distance functions will be injected with a short-circuit threshold.
1656  // Threshold value would come from Geo comparison operations or from other outer
1657  // geo operations, e.g. ST_DWithin
1658  // At this point, only ST_Distance_LineString_LineString requires a threshold arg.
1659  // TODO: Other combinations that involve LINESTRING, POLYGON and MULTIPOLYGON args
1660  // TODO: Inject threshold into ST_MaxDistance
1661  if (function_name == "ST_Distance"sv && arg0_ti.get_subtype() != kGEOGRAPHY &&
1662  (arg0_ti.get_type() != kPOINT || arg1_ti.get_type() != kPOINT)) {
1663  if (threshold_expr) {
1664  if (threshold_expr->get_type_info().get_type() != kDOUBLE) {
1665  const auto& threshold_ti = SQLTypeInfo(kDOUBLE, false);
1666  threshold_expr = threshold_expr->add_cast(threshold_ti);
1667  }
1668  threshold_expr = fold_expr(threshold_expr.get());
1669  } else {
1670  Datum d;
1671  d.doubleval = 0.0;
1672  threshold_expr = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
1673  }
1674  geoargs.push_back(threshold_expr);
1675  }
1676 
1677  auto result =
1678  makeExpr<Analyzer::FunctionOper>(return_type, specialized_geofunc, geoargs);
1679  if (negate_result) {
1680  return makeExpr<Analyzer::UOper>(kBOOLEAN, kNOT, result);
1681  }
1682  if (compare_expr) {
1683  return makeExpr<Analyzer::BinOper>(kBOOLEAN, kLE, kONE, result, compare_expr);
1684  }
1685  return result;
1686 }
1687 
1688 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateTernaryGeoFunction(
1689  const RexFunctionOperator* rex_function) const {
1690  CHECK_EQ(size_t(3), rex_function->size());
1691 
1692  auto distance_expr = translateScalarRex(rex_function->getOperand(2));
1693  const auto& distance_ti = SQLTypeInfo(kDOUBLE, false);
1694  if (distance_expr->get_type_info().get_type() != kDOUBLE) {
1695  distance_expr = distance_expr->add_cast(distance_ti);
1696  }
1697 
1698  auto function_name = rex_function->getName();
1699  if (function_name == "ST_DWithin"sv) {
1700  auto return_type = rex_function->getType();
1701  bool swap_args = false;
1702  bool with_bounds = true;
1703  SQLTypeInfo arg0_ti;
1704  SQLTypeInfo arg1_ti;
1705 
1706  auto geoargs0 = translateGeoFunctionArg(
1707  rex_function->getOperand(0), arg0_ti, with_bounds, false, false);
1708  auto geoargs1 = translateGeoFunctionArg(
1709  rex_function->getOperand(1), arg1_ti, with_bounds, false, false);
1710  if (arg0_ti.get_subtype() != arg1_ti.get_subtype()) {
1711  throw QueryNotSupported(rex_function->getName() +
1712  " cannot accept mixed GEOMETRY/GEOGRAPHY arguments");
1713  }
1714  auto is_geodesic = false;
1715  if (arg0_ti.get_subtype() == kGEOGRAPHY) {
1716  if (arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kPOINT) {
1717  is_geodesic = true;
1718  } else {
1719  throw QueryNotSupported(
1720  rex_function->getName() +
1721  " in geodesic form can only accept POINT GEOGRAPHY arguments");
1722  }
1723  }
1724  // Check SRID match if at least one is set/valid
1725  if ((arg0_ti.get_output_srid() > 0 || arg1_ti.get_output_srid() > 0) &&
1726  arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
1727  throw QueryNotSupported(rex_function->getName() + " cannot accept different SRIDs");
1728  }
1729 
1730  if ((arg1_ti.get_type() == kPOINT && arg0_ti.get_type() != kPOINT) ||
1731  (arg1_ti.get_type() == kLINESTRING && arg0_ti.get_type() == kPOLYGON) ||
1732  (arg1_ti.get_type() == kPOLYGON && arg0_ti.get_type() == kMULTIPOLYGON)) {
1733  // Swap arguments and use single implementation per arg pair
1734  swap_args = true;
1735  }
1736 
1737  // First input's compression mode and SRID args to enable on-the-fly
1738  // decompression/transforms
1739  Datum input_compression0;
1740  input_compression0.intval = Geospatial::get_compression_scheme(arg0_ti);
1741  Datum input_srid0;
1742  input_srid0.intval = arg0_ti.get_input_srid();
1743 
1744  // Second input's compression mode and SRID args to enable on-the-fly
1745  // decompression/transforms
1746  Datum input_compression1;
1747  input_compression1.intval = Geospatial::get_compression_scheme(arg1_ti);
1748  Datum input_srid1;
1749  input_srid1.intval = arg1_ti.get_input_srid();
1750 
1751  // Output SRID arg to enable on-the-fly transforms
1752  Datum output_srid;
1753  output_srid.intval = arg0_ti.get_output_srid();
1754 
1755  std::string specialized_geofunc{function_name};
1756  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
1757  if (swap_args) {
1758  specialized_geofunc += suffix(arg1_ti.get_type()) + suffix(arg0_ti.get_type());
1759  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1760  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1761  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1762  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1763  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1764  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1765  } else {
1766  specialized_geofunc += suffix(arg0_ti.get_type()) + suffix(arg1_ti.get_type());
1767  if (is_geodesic) {
1768  specialized_geofunc += "_Geodesic"s;
1769  }
1770  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
1771  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
1772  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
1773  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
1774  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1775  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1776  }
1777  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1778  // Also add the within distance
1779  geoargs.push_back(distance_expr);
1780 
1781  auto result =
1782  makeExpr<Analyzer::FunctionOper>(return_type, specialized_geofunc, geoargs);
1783  return result;
1784  }
1785 
1786  // Otherwise translate function as binary geo to get distance,
1787  // with optional short-circuiting threshold held in the third operand
1788  const auto geo_distance = translateBinaryGeoFunction(rex_function);
1789  // and generate the comparison
1790  return makeExpr<Analyzer::BinOper>(kBOOLEAN, kLE, kONE, geo_distance, distance_expr);
1791 }
1792 
1793 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoComparison(
1794  const RexOperator* rex_operator) const {
1795  if (rex_operator->size() != size_t(2)) {
1796  return nullptr;
1797  }
1798 
1799  auto geo_distance_expr = translateScalarRex(rex_operator->getOperand(0));
1800  auto func_oper = dynamic_cast<Analyzer::GeoOperator*>(geo_distance_expr.get());
1801  if (func_oper && func_oper->getName() == "ST_Distance"sv) {
1802  const auto& distance_ti = SQLTypeInfo(kDOUBLE, false);
1803  auto distance_expr = translateScalarRex(rex_operator->getOperand(1));
1804  if (distance_expr->get_type_info().get_type() != kDOUBLE) {
1805  distance_expr = distance_expr->add_cast(distance_ti);
1806  }
1807  distance_expr = fold_expr(distance_expr.get());
1808  return makeExpr<Analyzer::BinOper>(
1809  kBOOLEAN, rex_operator->getOperator(), kONE, geo_distance_expr, distance_expr);
1810  }
1811  return nullptr;
1812 }
1813 
1814 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateFunctionWithGeoArg(
1815  const RexFunctionOperator* rex_function) const {
1816  std::string specialized_geofunc{rex_function->getName()};
1817  if (func_resolve(rex_function->getName(),
1818  "convert_meters_to_pixel_width"sv,
1819  "convert_meters_to_pixel_height"sv)) {
1820  CHECK_EQ(rex_function->size(), 6u);
1821  SQLTypeInfo arg_ti;
1822  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1823  args.push_back(translateScalarRex(rex_function->getOperand(0)));
1824  auto geoargs =
1825  translateGeoFunctionArg(rex_function->getOperand(1), arg_ti, false, true, false);
1826  // only works on points
1827  if (arg_ti.get_type() != kPOINT) {
1828  throw QueryNotSupported(rex_function->getName() +
1829  " expects a point for the second argument");
1830  }
1831 
1832  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1833 
1834  // Add compression information
1835  Datum input_compression;
1836  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1837  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1838  if (arg_ti.get_input_srid() != 4326) {
1839  throw QueryNotSupported(
1840  rex_function->getName() +
1841  " currently only supports points of with SRID WGS84/EPSG:4326");
1842  }
1843  Datum input_srid;
1844  input_srid.intval = arg_ti.get_input_srid();
1845  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1846  Datum output_srid;
1847  // Forcing web-mercator projection for now
1848  // TODO(croot): check that the input-to-output conversion routines exist?
1849  output_srid.intval =
1850  arg_ti.get_output_srid() != 900913 ? 900913 : arg_ti.get_output_srid();
1851  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1852 
1853  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1854  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1855  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1856  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1857  return makeExpr<Analyzer::FunctionOper>(
1858  rex_function->getType(), specialized_geofunc, args);
1859  } else if (rex_function->getName() == "is_point_in_view"sv) {
1860  CHECK_EQ(rex_function->size(), 5u);
1861  SQLTypeInfo arg_ti;
1862  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1863  auto geoargs =
1864  translateGeoFunctionArg(rex_function->getOperand(0), arg_ti, false, true, false);
1865  // only works on points
1866  if (arg_ti.get_type() != kPOINT) {
1867  throw QueryNotSupported(rex_function->getName() +
1868  " expects a point for the second argument");
1869  }
1870 
1871  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1872 
1873  // Add compression information
1874  Datum input_compression;
1875  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1876  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1877  if (arg_ti.get_input_srid() != 4326) {
1878  throw QueryNotSupported(
1879  rex_function->getName() +
1880  " currently only supports points of with SRID WGS84/EPSG:4326");
1881  }
1882  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1883  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1884  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1885  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1886  return makeExpr<Analyzer::FunctionOper>(
1887  rex_function->getType(), specialized_geofunc, args);
1888  } else if (rex_function->getName() == "is_point_size_in_view"sv) {
1889  CHECK_EQ(rex_function->size(), 6u);
1890  SQLTypeInfo arg_ti;
1891  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1892  auto geoargs =
1893  translateGeoFunctionArg(rex_function->getOperand(0), arg_ti, false, true, false);
1894  // only works on points
1895  if (arg_ti.get_type() != kPOINT) {
1896  throw QueryNotSupported(rex_function->getName() +
1897  " expects a point for the second argument");
1898  }
1899 
1900  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1901 
1902  // Add compression information
1903  Datum input_compression;
1904  input_compression.intval = Geospatial::get_compression_scheme(arg_ti);
1905  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1906  if (arg_ti.get_input_srid() != 4326) {
1907  throw QueryNotSupported(
1908  rex_function->getName() +
1909  " currently only supports points of with SRID WGS84/EPSG:4326");
1910  }
1911  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1912  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1913  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1914  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1915  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1916  return makeExpr<Analyzer::FunctionOper>(
1917  rex_function->getType(), specialized_geofunc, args);
1918  }
1919  CHECK(false);
1920  return nullptr;
1921 }
1922 
1923 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoOverlapsOper(
1924  const RexOperator* rex_operator) const {
1925  CHECK_EQ(rex_operator->size(), 2u);
1926 
1927  auto translate_input =
1928  [&](const RexScalar* operand) -> std::shared_ptr<Analyzer::Expr> {
1929  const auto input = dynamic_cast<const RexInput*>(operand);
1930  CHECK(input);
1931 
1932  SQLTypeInfo ti;
1933  const auto exprs = translateGeoColumn(input, ti, true, false, false);
1934  CHECK_GT(exprs.size(), 0u);
1935  if (ti.get_type() == kPOINT) {
1936  return exprs.front();
1937  } else {
1938  return exprs.back();
1939  }
1940  };
1941 
1942  SQLQualifier sql_qual{kONE};
1943  SQLOps sql_op{kOVERLAPS};
1944  return makeExpr<Analyzer::BinOper>(SQLTypeInfo(kBOOLEAN, false),
1945  false,
1946  sql_op,
1947  sql_qual,
1948  translate_input(rex_operator->getOperand(1)),
1949  translate_input(rex_operator->getOperand(0)));
1950 }
int8_t tinyintval
Definition: Datum.h:69
HOST DEVICE SQLTypes get_subtype() const
Definition: sqltypes.h:381
void set_compression(EncodingType c)
Definition: sqltypes.h:501
void set_size(int s)
Definition: sqltypes.h:498
#define CHECK_EQ(x, y)
Definition: Logger.h:297
static std::unique_ptr< GeoBase > createGeoType(const std::string &wkt_or_wkb_hex)
Definition: Types.cpp:1061
auto func_resolve
std::shared_ptr< Analyzer::Expr > translateBinaryGeoPredicate(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
SQLTypes
Definition: sqltypes.h:53
std::vector< std::shared_ptr< Analyzer::Expr > > translateGeoFunctionArg(const RexScalar *rex_scalar, SQLTypeInfo &arg_ti, 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 bool try_to_compress=false, const bool allow_gdal_transforms=false) const
#define SPIMAP_GEO_PHYSICAL_INPUT(c, i)
Definition: Catalog.h:84
SQLQualifier
Definition: sqldefs.h:71
std::shared_ptr< Analyzer::Expr > translateScalarRex(const RexScalar *rex) const
const SQLTypeInfo & getType() const
Definition: RelAlgDag.h:284
size_t size() const
Definition: RelAlgDag.h:270
const RexScalar * getOperand(const size_t idx) const
Definition: RelAlgDag.h:272
SQLOps
Definition: sqldefs.h:28
Definition: sqldefs.h:34
int32_t get_compression_scheme(const SQLTypeInfo &ti)
Definition: Compression.cpp:23
#define UNREACHABLE()
Definition: Logger.h:333
HOST DEVICE void set_subtype(SQLTypes st)
Definition: sqltypes.h:491
#define CHECK_GE(x, y)
Definition: Logger.h:302
Definition: sqldefs.h:48
std::shared_ptr< Analyzer::Expr > ExpressionPtr
Definition: Analyzer.h:188
bool g_enable_geo_ops_on_uncompressed_coords
Definition: Execute.cpp:114
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:380
#define CHECK_GT(x, y)
Definition: Logger.h:301
std::shared_ptr< Analyzer::Expr > translateGeoProjection(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
int32_t intval
Definition: Datum.h:71
#define TOLERANCE_GEOINT32
std::string suffix(SQLTypes type)
Definition: Codegen.cpp:70
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:480
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:494
unsigned getIndex() const
Definition: RelAlgDag.h:77
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
Definition: RelAlgDag.h:282
bool has_bounds() const
Definition: sqltypes.h:467
const ColumnDescriptor * getMetadataForColumnBySpi(const int tableId, const size_t spi) const
Definition: Catalog.cpp:2083
void set_output_srid(int s)
Definition: sqltypes.h:496
const std::unordered_map< const RelAlgNode *, int > input_to_nest_level_
void set_comp_param(int p)
Definition: sqltypes.h:502
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:1079
#define CHECK_LT(x, y)
Definition: Logger.h:299
Definition: sqltypes.h:67
std::shared_ptr< Analyzer::Expr > translateUnaryGeoPredicate(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
Definition: sqldefs.h:71
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:388
std::shared_ptr< Analyzer::Expr > translateUnaryGeoConstructor(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
bool is_projection(const RelAlgExecutionUnit &ra_exe_unit)
static RelRexToStringConfig defaults()
Definition: RelAlgDag.h:49
const RelAlgNode * getSourceNode() const
Definition: RelAlgDag.h:389
static bool isUtm(unsigned const srid)
Definition: Transform.h:43
#define IS_STRING(T)
Definition: sqltypes.h:297
HOST DEVICE int get_comp_param() const
Definition: sqltypes.h:389
HOST DEVICE int get_input_srid() const
Definition: sqltypes.h:384
void set_notnull(bool n)
Definition: sqltypes.h:497
#define CHECK(condition)
Definition: Logger.h:289
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:190
std::string toString(RelRexToStringConfig config=RelRexToStringConfig::defaults()) const override
Definition: RelAlgDag.h:502
std::shared_ptr< Analyzer::Expr > translateFunctionWithGeoArg(const RexFunctionOperator *) const
Definition: sqltypes.h:60
SQLTypeInfo columnType
const std::string & getName() const
Definition: RelAlgDag.h:500
Definition: Datum.h:67
std::shared_ptr< Analyzer::Expr > translateGeoComparison(const RexOperator *) const
int get_physical_coord_cols() const
Definition: sqltypes.h:430
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:298
std::shared_ptr< Analyzer::Expr > translateBinaryGeoConstructor(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
Definition: sqldefs.h:38
SQLTypes get_ti_from_geo(const Geospatial::GeoBase *geo)
Definition: Analyzer.cpp:3775
std::shared_ptr< Analyzer::Expr > fold_expr(const Analyzer::Expr *expr)
double doubleval
Definition: Datum.h:74
const Catalog_Namespace::Catalog & cat_
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:386
virtual GeoType getType() const =0
#define IS_GEO_POLY(T)
Definition: sqltypes.h:303
HOST DEVICE void set_type(SQLTypes t)
Definition: sqltypes.h:490