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