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