OmniSciDB  06b3bd477c
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RelAlgTranslatorGeo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #include <memory>
20 #include <vector>
21 
23 #include "Shared/geo_compression.h"
24 #include "Shared/geo_types.h"
25 
26 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoColumn(
27  const RexInput* rex_input,
28  SQLTypeInfo& ti,
29  const bool with_bounds,
30  const bool with_render_group,
31  const bool expand_geo_col) const {
32  std::vector<std::shared_ptr<Analyzer::Expr>> args;
33  const auto source = rex_input->getSourceNode();
34  const auto it_rte_idx = input_to_nest_level_.find(source);
35  CHECK(it_rte_idx != input_to_nest_level_.end());
36  const int rte_idx = it_rte_idx->second;
37  const auto& in_metainfo = source->getOutputMetainfo();
38 
39  int32_t table_id{0};
40  int column_id{-1};
41  const auto scan_source = dynamic_cast<const RelScan*>(source);
42  if (scan_source) {
43  // We're at leaf (scan) level and not supposed to have input metadata,
44  // the name and type information come directly from the catalog.
45  CHECK(in_metainfo.empty());
46 
47  const auto td = scan_source->getTableDescriptor();
48  table_id = td->tableId;
49 
50  const auto gcd = cat_.getMetadataForColumnBySpi(table_id, rex_input->getIndex() + 1);
51  CHECK(gcd);
52  ti = gcd->columnType;
53  column_id = gcd->columnId;
54 
55  } else {
56  // Likely backed by a temp table. Read the table ID from the source node and negate it
57  // (see RelAlgTranslator::translateInput)
58  table_id = -source->getId();
59 
60  CHECK(!expand_geo_col);
61  if (with_bounds || with_render_group) {
62  throw QueryNotSupported(
63  "Geospatial columns not yet supported in intermediate results.");
64  }
65 
66  CHECK(!in_metainfo.empty());
67  CHECK_GE(rte_idx, 0);
68  column_id = rex_input->getIndex();
69  CHECK_LT(static_cast<size_t>(column_id), in_metainfo.size());
70  ti = in_metainfo[column_id].get_type_info();
71  }
72  CHECK(IS_GEO(ti.get_type()));
73 
74  // Return geo column reference. The geo column may be expanded if required for extension
75  // function arguments. Otherwise, the geo column reference will be translated into
76  // physical columns as required. Bounds column will be added if present and requested.
77  if (expand_geo_col) {
78  for (auto i = 0; i < ti.get_physical_coord_cols(); i++) {
79  const auto pcd = cat_.getMetadataForColumnBySpi(
80  table_id, SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(), i + 1));
81  auto pcol_ti = pcd->columnType;
82  args.push_back(std::make_shared<Analyzer::ColumnVar>(
83  pcol_ti, table_id, pcd->columnId, rte_idx));
84  }
85  } else {
86  args.push_back(
87  std::make_shared<Analyzer::ColumnVar>(ti, table_id, column_id, rte_idx));
88  }
89  if (with_bounds && ti.has_bounds()) {
90  const auto bounds_cd = cat_.getMetadataForColumnBySpi(
91  table_id,
93  ti.get_physical_coord_cols() + 1));
94  auto bounds_ti = bounds_cd->columnType;
95  args.push_back(std::make_shared<Analyzer::ColumnVar>(
96  bounds_ti, table_id, bounds_cd->columnId, rte_idx));
97  }
98  if (with_render_group && ti.has_render_group()) {
99  const auto render_group_cd = cat_.getMetadataForColumnBySpi(
100  table_id,
101  SPIMAP_GEO_PHYSICAL_INPUT(rex_input->getIndex(),
102  ti.get_physical_coord_cols() + 2));
103  auto render_group_ti = render_group_cd->columnType;
104  args.push_back(std::make_shared<Analyzer::ColumnVar>(
105  render_group_ti, table_id, render_group_cd->columnId, rte_idx));
106  }
107  return args;
108 }
109 
110 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoLiteral(
111  const RexLiteral* rex_literal,
112  SQLTypeInfo& ti,
113  bool with_bounds) const {
114  CHECK(rex_literal);
115  if (rex_literal->getType() != kTEXT) {
116  throw std::runtime_error("Geo literals must be strings");
117  }
118  const auto e = translateLiteral(rex_literal);
119  auto wkt = std::dynamic_pointer_cast<Analyzer::Constant>(e);
120  CHECK(wkt);
121  std::vector<double> coords;
122  std::vector<double> bounds;
123  std::vector<int> ring_sizes;
124  std::vector<int> poly_rings;
125  int32_t srid = ti.get_output_srid();
127  *wkt->get_constval().stringval, ti, coords, bounds, ring_sizes, poly_rings)) {
128  throw QueryNotSupported("Could not read geometry from text");
129  }
131  ti.set_input_srid(srid);
132  ti.set_output_srid(srid);
133  // Compress geo literals by default
134  if (srid == 4326) {
136  ti.set_comp_param(32);
137  }
138 
139  std::vector<std::shared_ptr<Analyzer::Expr>> args;
140 
141  std::vector<uint8_t> compressed_coords = geospatial::compress_coords(coords, ti);
142  std::list<std::shared_ptr<Analyzer::Expr>> compressed_coords_exprs;
143  for (auto cc : compressed_coords) {
144  Datum d;
145  d.tinyintval = cc;
146  auto e = makeExpr<Analyzer::Constant>(kTINYINT, false, d);
147  compressed_coords_exprs.push_back(e);
148  }
149  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
150  arr_ti.set_subtype(kTINYINT);
151  arr_ti.set_size(compressed_coords.size() * sizeof(int8_t));
152  arr_ti.set_compression(ti.get_compression());
153  arr_ti.set_comp_param((ti.get_compression() == kENCODING_GEOINT) ? 32 : 64);
154  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, compressed_coords_exprs));
155 
156  auto lit_type = ti.get_type();
157  if (lit_type == kPOLYGON || lit_type == kMULTIPOLYGON) {
158  // ring sizes
159  std::list<std::shared_ptr<Analyzer::Expr>> ring_size_exprs;
160  for (auto c : ring_sizes) {
161  Datum d;
162  d.intval = c;
163  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
164  ring_size_exprs.push_back(e);
165  }
166  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
167  arr_ti.set_subtype(kINT);
168  arr_ti.set_size(ring_sizes.size() * sizeof(int32_t));
169  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, ring_size_exprs));
170 
171  // poly rings
172  if (lit_type == kMULTIPOLYGON) {
173  std::list<std::shared_ptr<Analyzer::Expr>> poly_rings_exprs;
174  for (auto c : poly_rings) {
175  Datum d;
176  d.intval = c;
177  auto e = makeExpr<Analyzer::Constant>(kINT, false, d);
178  poly_rings_exprs.push_back(e);
179  }
180  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
181  arr_ti.set_subtype(kINT);
182  arr_ti.set_size(poly_rings.size() * sizeof(int32_t));
183  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, poly_rings_exprs));
184  }
185  }
186 
187  if (with_bounds && ti.has_bounds()) {
188  // bounds
189  std::list<std::shared_ptr<Analyzer::Expr>> bounds_exprs;
190  for (auto b : bounds) {
191  Datum d;
192  d.doubleval = b;
193  auto e = makeExpr<Analyzer::Constant>(kDOUBLE, false, d);
194  bounds_exprs.push_back(e);
195  }
196  SQLTypeInfo arr_ti = SQLTypeInfo(kARRAY, true);
197  arr_ti.set_subtype(kDOUBLE);
198  arr_ti.set_size(bounds.size() * sizeof(double));
199  args.push_back(makeExpr<Analyzer::Constant>(arr_ti, false, bounds_exprs));
200  }
201 
202  return args;
203 }
204 
205 std::vector<std::shared_ptr<Analyzer::Expr>> RelAlgTranslator::translateGeoFunctionArg(
206  const RexScalar* rex_scalar,
207  SQLTypeInfo& arg_ti,
208  int32_t& lindex,
209  const bool with_bounds,
210  const bool with_render_group,
211  const bool expand_geo_col,
212  const bool is_projection) const {
213  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
214 
215  const auto rex_input = dynamic_cast<const RexInput*>(rex_scalar);
216  if (rex_input) {
217  const auto input = translateInput(rex_input);
218  const auto column = dynamic_cast<const Analyzer::ColumnVar*>(input.get());
219  if (!column || !column->get_type_info().is_geometry()) {
220  throw QueryNotSupported("Geo function is expecting a geo column argument");
221  }
222  return translateGeoColumn(
223  rex_input, arg_ti, with_bounds, with_render_group, expand_geo_col);
224  }
225  const auto rex_function = dynamic_cast<const RexFunctionOperator*>(rex_scalar);
226  if (rex_function) {
227  if (rex_function->getName() == "ST_Transform"sv) {
228  CHECK_EQ(size_t(2), rex_function->size());
229  const auto rex_scalar0 =
230  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
231  if (!rex_scalar0) {
232  throw QueryNotSupported(rex_function->getName() + ": unexpected first argument");
233  }
234 
235  const auto rex_literal =
236  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
237  if (!rex_literal) {
238  throw QueryNotSupported(rex_function->getName() +
239  ": second argument is expected to be a literal");
240  }
241  const auto e = translateLiteral(rex_literal);
242  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
243  if (!ce || !e->get_type_info().is_integer()) {
244  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
245  }
246  int32_t srid = 0;
247  if (e->get_type_info().get_type() == kSMALLINT) {
248  srid = static_cast<int32_t>(ce->get_constval().smallintval);
249  } else if (e->get_type_info().get_type() == kTINYINT) {
250  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
251  } else if (e->get_type_info().get_type() == kINT) {
252  srid = static_cast<int32_t>(ce->get_constval().intval);
253  } else {
254  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
255  }
256  if (srid != 900913) {
257  throw QueryNotSupported(rex_function->getName() + ": unsupported output SRID " +
258  std::to_string(srid));
259  }
260  arg_ti.set_output_srid(srid); // Forward output srid down to argument translation
261  auto arg0 = translateGeoFunctionArg(
262  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
263 
264  if (arg_ti.get_input_srid() > 0) {
265  if (arg_ti.get_input_srid() != 4326) {
266  throw QueryNotSupported(rex_function->getName() + ": unsupported input SRID " +
267  std::to_string(arg_ti.get_input_srid()));
268  }
269  arg_ti.set_output_srid(
270  srid); // We have a valid input SRID, register the output SRID for transform
271  } else {
272  throw QueryNotSupported(rex_function->getName() +
273  ": unexpected input SRID, unable to transform");
274  }
275  return arg0;
276  } else if (func_resolve(
277  rex_function->getName(), "ST_GeomFromText"sv, "ST_GeogFromText"sv)) {
278  CHECK(rex_function->size() == size_t(1) || rex_function->size() == size_t(2));
279  // First - register srid, then send it to geo literal translation
280  int32_t srid = 0;
281  if (rex_function->size() == 2) {
282  const auto rex_literal =
283  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
284  if (!rex_literal) {
285  throw QueryNotSupported(rex_function->getName() +
286  ": second argument is expected to be a literal");
287  }
288  const auto e = translateLiteral(rex_literal);
289  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
290  if (!ce || !e->get_type_info().is_integer()) {
291  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
292  }
293  if (e->get_type_info().get_type() == kSMALLINT) {
294  srid = static_cast<int32_t>(ce->get_constval().smallintval);
295  } else if (e->get_type_info().get_type() == kTINYINT) {
296  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
297  } else if (e->get_type_info().get_type() == kINT) {
298  srid = static_cast<int32_t>(ce->get_constval().intval);
299  } else {
300  throw QueryNotSupported(rex_function->getName() + " expecting integer SRID");
301  }
302  if (srid != 0 && srid != 4326 && srid != 900913) {
303  throw QueryNotSupported(rex_function->getName() + ": unsupported SRID " +
304  std::to_string(srid));
305  }
306  }
307  arg_ti.set_input_srid(srid); // Input SRID
308  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
309 
310  const auto rex_literal =
311  dynamic_cast<const RexLiteral*>(rex_function->getOperand(0));
312  if (!rex_literal) {
313  throw QueryNotSupported(rex_function->getName() +
314  " expects a string literal as first argument");
315  }
316  auto arg0 = translateGeoLiteral(rex_literal, arg_ti, with_bounds);
317  arg_ti.set_subtype((rex_function->getName() == "ST_GeogFromText"sv) ? kGEOGRAPHY
318  : kGEOMETRY);
319  return arg0;
320  } else if (rex_function->getName() == "ST_PointN"sv) {
321  CHECK_EQ(size_t(2), rex_function->size());
322  const auto rex_scalar0 =
323  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
324  if (!rex_scalar0) {
325  throw QueryNotSupported(rex_function->getName() +
326  ": expects scalar as first argument");
327  }
328  auto arg0 = translateGeoFunctionArg(
329  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
330  if (arg_ti.get_type() != kLINESTRING) {
331  throw QueryNotSupported(rex_function->getName() +
332  " expects LINESTRING as first argument");
333  }
334  const auto rex_literal =
335  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
336  if (!rex_literal) {
337  throw QueryNotSupported(rex_function->getName() +
338  ": second argument is expected to be a literal");
339  }
340  const auto e = translateLiteral(rex_literal);
341  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
342  if (!ce || !e->get_type_info().is_integer()) {
343  throw QueryNotSupported(rex_function->getName() +
344  ": expecting integer index as second argument");
345  }
346  int32_t index = 0;
347  if (e->get_type_info().get_type() == kSMALLINT) {
348  index = static_cast<int32_t>(ce->get_constval().smallintval);
349  } else if (e->get_type_info().get_type() == kTINYINT) {
350  index = static_cast<int32_t>(ce->get_constval().tinyintval);
351  } else if (e->get_type_info().get_type() == kINT) {
352  index = static_cast<int32_t>(ce->get_constval().intval);
353  } else {
354  throw QueryNotSupported(rex_function->getName() + " expecting integer index");
355  }
356  if (lindex != 0) {
357  throw QueryNotSupported(rex_function->getName() +
358  ": LINESTRING is already indexed");
359  }
360  if (index == 0) {
361  throw QueryNotSupported(rex_function->getName() + ": invalid index");
362  }
363  lindex = index;
364  return arg0;
365  } else if (rex_function->getName() == "ST_StartPoint"sv) {
366  CHECK_EQ(size_t(1), rex_function->size());
367  const auto rex_scalar0 =
368  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
369  if (!rex_scalar0) {
370  throw QueryNotSupported(rex_function->getName() +
371  ": expects scalar as first argument");
372  }
373  auto arg0 = translateGeoFunctionArg(
374  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
375  if (arg_ti.get_type() != kLINESTRING) {
376  throw QueryNotSupported(rex_function->getName() +
377  " expects LINESTRING as first argument");
378  }
379  if (lindex != 0) {
380  throw QueryNotSupported(rex_function->getName() +
381  ": LINESTRING is already indexed");
382  }
383  lindex = 1;
384  return arg0;
385  } else if (rex_function->getName() == "ST_EndPoint"sv) {
386  CHECK_EQ(size_t(1), rex_function->size());
387  const auto rex_scalar0 =
388  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
389  if (!rex_scalar0) {
390  throw QueryNotSupported(rex_function->getName() +
391  ": expects scalar as first argument");
392  }
393  auto arg0 = translateGeoFunctionArg(
394  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
395  if (arg_ti.get_type() != kLINESTRING) {
396  throw QueryNotSupported(rex_function->getName() +
397  " expects LINESTRING as first argument");
398  }
399  if (lindex != 0) {
400  throw QueryNotSupported(rex_function->getName() +
401  ": LINESTRING is already indexed");
402  }
403  lindex = -1;
404  return arg0;
405  } else if (rex_function->getName() == "ST_SRID"sv) {
406  CHECK_EQ(size_t(1), rex_function->size());
407  const auto rex_scalar0 =
408  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
409  if (!rex_scalar0) {
410  throw QueryNotSupported(rex_function->getName() +
411  ": expects scalar as first argument");
412  }
413  auto arg0 = translateGeoFunctionArg(
414  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
415  if (!IS_GEO(arg_ti.get_type())) {
416  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
417  }
418  return arg0;
419  } else if (rex_function->getName() == "ST_SetSRID"sv) {
420  CHECK_EQ(size_t(2), rex_function->size());
421  const auto rex_scalar0 =
422  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
423  if (!rex_scalar0) {
424  throw QueryNotSupported(rex_function->getName() +
425  ": expects scalar as first argument");
426  }
427  auto arg0 = translateGeoFunctionArg(rex_scalar0,
428  arg_ti,
429  lindex,
430  with_bounds,
431  with_render_group,
432  expand_geo_col,
433  is_projection);
434  if (!IS_GEO(arg_ti.get_type())) {
435  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
436  }
437  const auto rex_literal =
438  dynamic_cast<const RexLiteral*>(rex_function->getOperand(1));
439  if (!rex_literal) {
440  throw QueryNotSupported(rex_function->getName() +
441  ": second argument is expected to be a literal");
442  }
443  const auto e = translateLiteral(rex_literal);
444  auto ce = std::dynamic_pointer_cast<Analyzer::Constant>(e);
445  if (!ce || !e->get_type_info().is_integer()) {
446  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
447  }
448  int32_t srid = 0;
449  if (e->get_type_info().get_type() == kSMALLINT) {
450  srid = static_cast<int32_t>(ce->get_constval().smallintval);
451  } else if (e->get_type_info().get_type() == kTINYINT) {
452  srid = static_cast<int32_t>(ce->get_constval().tinyintval);
453  } else if (e->get_type_info().get_type() == kINT) {
454  srid = static_cast<int32_t>(ce->get_constval().intval);
455  } else {
456  throw QueryNotSupported(rex_function->getName() + ": expecting integer SRID");
457  }
458  arg_ti.set_input_srid(srid); // Input SRID
459  arg_ti.set_output_srid(srid); // Output SRID is the same - no transform
460  return arg0;
461  } else if (rex_function->getName() == "CastToGeography"sv) {
462  CHECK_EQ(size_t(1), rex_function->size());
463  const auto rex_scalar0 =
464  dynamic_cast<const RexScalar*>(rex_function->getOperand(0));
465  if (!rex_scalar0) {
466  throw QueryNotSupported(rex_function->getName() +
467  ": expects scalar as first argument");
468  }
469  auto arg0 = translateGeoFunctionArg(
470  rex_scalar0, arg_ti, lindex, with_bounds, with_render_group, expand_geo_col);
471  if (!IS_GEO(arg_ti.get_type())) {
472  throw QueryNotSupported(rex_function->getName() + " expects geometry argument");
473  }
474  if (arg_ti.get_output_srid() != 4326) {
475  throw QueryNotSupported(rex_function->getName() +
476  " expects geometry with SRID=4326");
477  }
478  arg_ti.set_subtype(kGEOGRAPHY);
479  return arg0;
480  } else if (rex_function->getName() == "ST_Point"sv) {
481  CHECK_EQ(size_t(2), rex_function->size());
482  arg_ti.set_type(kPOINT);
483  arg_ti.set_subtype(kGEOMETRY);
484  arg_ti.set_input_srid(0);
485  arg_ti.set_output_srid(0);
487 
488  auto coord1 = translateScalarRex(rex_function->getOperand(0));
489  auto coord2 = translateScalarRex(rex_function->getOperand(1));
490  auto d_ti = SQLTypeInfo(kDOUBLE, true);
491  auto cast_coord1 = coord1->add_cast(d_ti);
492  auto cast_coord2 = coord2->add_cast(d_ti);
493  // First try to fold to geo literal
494  auto fold1 = fold_expr(cast_coord1.get());
495  auto fold2 = fold_expr(cast_coord2.get());
496  auto const_coord1 = std::dynamic_pointer_cast<Analyzer::Constant>(fold1);
497  auto const_coord2 = std::dynamic_pointer_cast<Analyzer::Constant>(fold2);
498  if (const_coord1 && const_coord2) {
499  CHECK(const_coord1->get_type_info().get_type() == kDOUBLE);
500  CHECK(const_coord2->get_type_info().get_type() == kDOUBLE);
501  std::string wkt = "POINT(" +
502  std::to_string(const_coord1->get_constval().doubleval) + " " +
503  std::to_string(const_coord2->get_constval().doubleval) + ")";
504  RexLiteral rex_literal{wkt, kTEXT, kNULLT, 0, 0, 0, 0};
505  auto args = translateGeoLiteral(&rex_literal, arg_ti, false);
506  CHECK(arg_ti.get_type() == kPOINT);
507  return args;
508  }
509  // Couldn't fold to geo literal, construct on the fly
510  auto da_ti = SQLTypeInfo(kARRAY, true);
511  da_ti.set_subtype(kDOUBLE);
512  da_ti.set_size(16);
513  auto cast_coords = {cast_coord1, cast_coord2};
514  auto is_local_alloca = !is_projection;
515  auto ae = makeExpr<Analyzer::ArrayExpr>(da_ti, cast_coords, false, is_local_alloca);
516  // cast it to tinyint[16]
517  SQLTypeInfo tia_ti = SQLTypeInfo(kARRAY, true);
518  tia_ti.set_subtype(kTINYINT);
519  tia_ti.set_size(16);
520  return {makeExpr<Analyzer::UOper>(tia_ti, false, kCAST, ae)};
521  } else if (func_resolve(rex_function->getName(),
522  "ST_Intersection"sv,
523  "ST_Difference"sv,
524  "ST_Union"sv,
525  "ST_Buffer"sv)) {
526  CHECK_EQ(size_t(2), rex_function->size());
527  // What geo type will the constructor return? Could be anything.
528  return {translateGeoBinaryConstructor(rex_function, arg_ti, with_bounds)};
529  } else if (func_resolve(rex_function->getName(), "ST_IsEmpty"sv, "ST_IsValid"sv)) {
530  CHECK_EQ(size_t(1), rex_function->size());
531  return {translateGeoPredicate(rex_function, arg_ti, with_bounds)};
532  } else {
533  throw QueryNotSupported("Unsupported argument: " + rex_function->getName());
534  }
535  }
536  const auto rex_literal = dynamic_cast<const RexLiteral*>(rex_scalar);
537  if (rex_literal) {
538  return translateGeoLiteral(rex_literal, arg_ti, with_bounds);
539  }
540  throw QueryNotSupported("Geo function argument not supported");
541 }
542 
543 namespace {
544 
545 std::string suffix(SQLTypes type) {
546  if (type == kPOINT) {
547  return std::string("_Point");
548  }
549  if (type == kLINESTRING) {
550  return std::string("_LineString");
551  }
552  if (type == kPOLYGON) {
553  return std::string("_Polygon");
554  }
555  if (type == kMULTIPOLYGON) {
556  return std::string("_MultiPolygon");
557  }
558  throw QueryNotSupported("Unsupported argument type");
559 }
560 
561 } // namespace
562 
563 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoProjection(
564  const RexFunctionOperator* rex_function,
565  SQLTypeInfo& ti,
566  const bool with_bounds) const {
567  int32_t lindex = 0;
568  auto geoargs =
569  translateGeoFunctionArg(rex_function, ti, lindex, false, false, true, true);
570  if (lindex != 0) {
571  throw QueryNotSupported(
572  "Indexed LINESTRING geometries not supported in this context");
573  }
574  return makeExpr<Analyzer::GeoUOper>(
576 }
577 
578 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoBinaryConstructor(
579  const RexFunctionOperator* rex_function,
580  SQLTypeInfo& ti,
581  const bool with_bounds) const {
582 #ifndef ENABLE_GEOS
583  throw QueryNotSupported(rex_function->getName() +
584  " geo constructor requires enabled GEOS support");
585 #endif
587  if (rex_function->getName() == "ST_Difference"sv) {
589  } else if (rex_function->getName() == "ST_Union"sv) {
591  } else if (rex_function->getName() == "ST_Buffer"sv) {
593  }
594 
597  SQLTypeInfo arg0_ti;
598  SQLTypeInfo arg1_ti;
599  if (func_resolve(rex_function->getName(),
600  "ST_Intersection"sv,
601  "ST_Difference"sv,
602  "ST_Union"sv,
603  "ST_Buffer"sv)) {
604  // First arg: geometry
605  int32_t lindex0 = 0;
606  geoargs0 = translateGeoFunctionArg(
607  rex_function->getOperand(0), arg0_ti, lindex0, false, false, true, true);
608  if (arg0_ti.get_type() == kLINESTRING) {
609  if (lindex0 != 0) {
610  throw QueryNotSupported(
611  "Indexed LINESTRING geometries not supported in this context");
612  }
613  }
614  }
615  if (func_resolve(rex_function->getName(),
616  "ST_Intersection"sv,
617  "ST_Difference"sv,
618  "ST_Union"sv)) {
619  // Second arg: geometry
620  int32_t lindex1 = 0;
621  geoargs1 = translateGeoFunctionArg(
622  rex_function->getOperand(1), arg1_ti, lindex1, false, false, true, true);
623  if (arg1_ti.get_type() == kLINESTRING) {
624  if (lindex1 != 0) {
625  throw QueryNotSupported(
626  "Indexed LINESTRING geometries not supported in this context");
627  }
628  }
629  } else if (func_resolve(rex_function->getName(), "ST_Buffer"sv)) {
630  // Second arg: double scalar
631  auto param_expr = translateScalarRex(rex_function->getOperand(1));
632  arg1_ti = SQLTypeInfo(kDOUBLE, false);
633  if (param_expr->get_type_info().get_type() != kDOUBLE) {
634  param_expr = param_expr->add_cast(arg1_ti);
635  }
636  geoargs1 = {param_expr};
637  }
638 
639  auto srid = ti.get_output_srid();
640  ti = arg0_ti;
642  ti.set_compression(kENCODING_NONE); // Constructed geometries are not compressed
643  ti.set_comp_param(0);
644  if (srid > 0) {
645  ti.set_output_srid(srid); // Requested SRID sent from above
646  }
647  return makeExpr<Analyzer::GeoBinOper>(op, ti, arg0_ti, arg1_ti, geoargs0, geoargs1);
648 }
649 
650 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoPredicate(
651  const RexFunctionOperator* rex_function,
652  SQLTypeInfo& ti,
653  const bool with_bounds) const {
654 #ifndef ENABLE_GEOS
655  throw QueryNotSupported(rex_function->getName() +
656  " geo predicate requires enabled GEOS support");
657 #endif
658  int32_t lindex = 0;
659  SQLTypeInfo arg_ti;
660  auto geoargs = translateGeoFunctionArg(
661  rex_function->getOperand(0), arg_ti, lindex, false, false, true, true);
662  if (lindex != 0) {
663  throw QueryNotSupported(
664  "Indexed LINESTRING geometries not supported in this context");
665  }
666  ti = SQLTypeInfo(kBOOLEAN, false);
667  auto op = (rex_function->getName() == "ST_IsEmpty"sv)
670  return makeExpr<Analyzer::GeoUOper>(op, ti, arg_ti, geoargs);
671 }
672 
673 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateUnaryGeoFunction(
674  const RexFunctionOperator* rex_function) const {
675  CHECK_EQ(size_t(1), rex_function->size());
676 
677  int32_t lindex = 0;
678  std::string specialized_geofunc{rex_function->getName()};
679 
680  // Geo function calls which do not need the coords col but do need cols associated with
681  // physical coords (e.g. ring_sizes / poly_rings)
682  if (rex_function->getName() == "ST_NRings"sv) {
683  SQLTypeInfo arg_ti;
684  auto geoargs = translateGeoFunctionArg(
685  rex_function->getOperand(0), arg_ti, lindex, false, false, true);
686  if (arg_ti.get_type() == kPOLYGON) {
687  CHECK_EQ(geoargs.size(), 2u);
688  geoargs.erase(geoargs.begin(), geoargs.begin() + 1); // remove the coords
689  return makeExpr<Analyzer::FunctionOper>(
690  rex_function->getType(), specialized_geofunc, geoargs);
691  } else if (arg_ti.get_type() == kMULTIPOLYGON) {
692  CHECK_EQ(geoargs.size(), 3u);
693  geoargs.erase(geoargs.begin(), geoargs.begin() + 1); // remove the coords
694  geoargs.erase(geoargs.begin() + 1, geoargs.end()); // remove the poly_rings
695  return makeExpr<Analyzer::FunctionOper>(
696  rex_function->getType(), specialized_geofunc, geoargs);
697  } else {
698  throw QueryNotSupported(rex_function->getName() +
699  " expects a POLYGON or MULTIPOLYGON");
700  }
701  } else if (rex_function->getName() == "ST_NPoints"sv) {
702  SQLTypeInfo arg_ti;
703  auto geoargs = translateGeoFunctionArg(
704  rex_function->getOperand(0), arg_ti, lindex, false, false, true);
705  geoargs.erase(geoargs.begin() + 1, geoargs.end()); // remove all but coords
706  // Add compression information
707  Datum input_compression;
708  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
709  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
710  return makeExpr<Analyzer::FunctionOper>(
711  rex_function->getType(), specialized_geofunc, geoargs);
712  } else if (func_resolve(rex_function->getName(), "ST_Perimeter"sv, "ST_Area"sv)) {
713  SQLTypeInfo arg_ti;
714  auto geoargs = translateGeoFunctionArg(
715  rex_function->getOperand(0), arg_ti, lindex, false, false, true);
716  if (arg_ti.get_type() != kPOLYGON && arg_ti.get_type() != kMULTIPOLYGON) {
717  throw QueryNotSupported(rex_function->getName() +
718  " expects a POLYGON or MULTIPOLYGON");
719  }
720  specialized_geofunc += suffix(arg_ti.get_type());
721  if (arg_ti.get_subtype() == kGEOGRAPHY && arg_ti.get_output_srid() == 4326) {
722  specialized_geofunc += "_Geodesic"s;
723  }
724  // Add compression information
725  Datum input_compression;
726  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
727  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
728  Datum input_srid;
729  input_srid.intval = arg_ti.get_input_srid();
730  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
731  Datum output_srid;
732  output_srid.intval = arg_ti.get_output_srid();
733  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
734  return makeExpr<Analyzer::FunctionOper>(
735  rex_function->getType(), specialized_geofunc, geoargs);
736  }
737 
738  // Accessors for poly bounds and render group for in-situ poly render queries
739  if (func_resolve(rex_function->getName(),
740  "MapD_GeoPolyBoundsPtr"sv /* deprecated */,
741  "OmniSci_Geo_PolyBoundsPtr"sv)) {
742  SQLTypeInfo arg_ti;
743  // get geo column plus bounds only (not expanded)
744  auto geoargs = translateGeoFunctionArg(
745  rex_function->getOperand(0), arg_ti, lindex, true, false, false);
746  // this function only works on polys
747  if (!IS_GEO_POLY(arg_ti.get_type())) {
748  throw QueryNotSupported(rex_function->getName() +
749  " expects a POLYGON or MULTIPOLYGON");
750  }
751  // only need the bounds argument (last), discard the rest
752  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
753  // done
754  return makeExpr<Analyzer::FunctionOper>(
755  rex_function->getType(), specialized_geofunc, geoargs);
756  } else if (func_resolve(rex_function->getName(),
757  "MapD_GeoPolyRenderGroup"sv /* deprecated */,
758  "OmniSci_Geo_PolyRenderGroup"sv)) {
759  SQLTypeInfo arg_ti;
760  // get geo column plus render_group only (not expanded)
761  auto geoargs = translateGeoFunctionArg(
762  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
763  // this function only works on polys
764  if (!IS_GEO_POLY(arg_ti.get_type())) {
765  throw QueryNotSupported(rex_function->getName() +
766  " expects a POLYGON or MULTIPOLYGON");
767  }
768  // only need the render_group argument (last), discard the rest
769  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
770  // done
771  return makeExpr<Analyzer::FunctionOper>(
772  rex_function->getType(), specialized_geofunc, geoargs);
773  }
774 
775  // All functions below use geo col as reference and expand it as necessary
776  SQLTypeInfo arg_ti;
777  bool with_bounds = true;
778  auto geoargs = translateGeoFunctionArg(
779  rex_function->getOperand(0), arg_ti, lindex, with_bounds, false, false);
780 
781  if (rex_function->getName() == "ST_SRID"sv) {
782  Datum output_srid;
783  output_srid.intval = arg_ti.get_output_srid();
784  return makeExpr<Analyzer::Constant>(kINT, false, output_srid);
785  }
786 
787  if (func_resolve(
788  rex_function->getName(), "ST_XMin"sv, "ST_YMin"sv, "ST_XMax"sv, "ST_YMax"sv)) {
789  // If type has bounds - use them, otherwise look at coords
790  if (arg_ti.has_bounds()) {
791  if (lindex != 0) {
792  throw QueryNotSupported(rex_function->getName() +
793  " doesn't support indexed LINESTRINGs");
794  }
795  // Only need the bounds argument, discard the rest
796  geoargs.erase(geoargs.begin(), geoargs.end() - 1);
797 
798  // Supply srids too - transformed geo would have a transformed bounding box
799  Datum input_srid;
800  input_srid.intval = arg_ti.get_input_srid();
801  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
802  Datum output_srid;
803  output_srid.intval = arg_ti.get_output_srid();
804  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
805 
806  specialized_geofunc += "_Bounds"s;
807  return makeExpr<Analyzer::FunctionOper>(
808  rex_function->getType(), specialized_geofunc, geoargs);
809  }
810  }
811 
812  // All geo function calls translated below only need the coords, extras e.g. ring_sizes
813  // are dropped. Specialize for other/new functions if needed.
814  geoargs.erase(geoargs.begin() + 1, geoargs.end());
815 
816  if (func_resolve(rex_function->getName(), "ST_X"sv, "ST_Y"sv)) {
817  if (arg_ti.get_type() == kLINESTRING) {
818  if (lindex == 0) {
819  throw QueryNotSupported(
820  rex_function->getName() +
821  " expects a POINT, use LINESTRING accessor, e.g. ST_POINTN");
822  }
823  Datum index;
824  index.intval = lindex;
825  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
826  } else if (arg_ti.get_type() != kPOINT) {
827  throw QueryNotSupported(rex_function->getName() + " expects a POINT");
828  }
829  specialized_geofunc += suffix(arg_ti.get_type());
830  } else if (rex_function->getName() == "ST_Length"sv) {
831  if (arg_ti.get_type() != kLINESTRING || lindex != 0) {
832  throw QueryNotSupported(rex_function->getName() + " expects unindexed LINESTRING");
833  }
834  specialized_geofunc += suffix(arg_ti.get_type());
835  if (arg_ti.get_subtype() == kGEOGRAPHY && arg_ti.get_output_srid() == 4326) {
836  specialized_geofunc += "_Geodesic"s;
837  }
838  }
839 
840  // Add input compression mode and SRID args to enable on-the-fly
841  // decompression/transforms
842  Datum input_compression;
843  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
844  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
845  Datum input_srid;
846  input_srid.intval = arg_ti.get_input_srid();
847  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
848 
849  // Add output SRID arg to enable on-the-fly transforms
850  Datum output_srid;
851  output_srid.intval = arg_ti.get_output_srid();
852  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
853 
854  return makeExpr<Analyzer::FunctionOper>(
855  rex_function->getType(), specialized_geofunc, geoargs);
856 }
857 
858 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateBinaryGeoFunction(
859  const RexFunctionOperator* rex_function) const {
860  auto function_name = rex_function->getName();
861  auto return_type = rex_function->getType();
862 
863  if (function_name == "ST_Overlaps"sv) {
864  // Overlaps join is the only implementation supported for now, only translate bounds
865  CHECK_EQ(size_t(2), rex_function->size());
866  auto extract_geo_bounds_from_input =
867  [this, &rex_function](const size_t index) -> std::shared_ptr<Analyzer::Expr> {
868  const auto rex_input =
869  dynamic_cast<const RexInput*>(rex_function->getOperand(index));
870  if (rex_input) {
871  SQLTypeInfo ti;
872  const auto exprs = translateGeoColumn(rex_input, ti, true, false, false);
873  CHECK_GT(exprs.size(), size_t(0));
874  if (ti.get_type() == kPOINT) {
875  throw std::runtime_error("ST_Overlaps is not supported for point arguments.");
876  } else {
877  return exprs.back();
878  }
879  } else {
880  throw std::runtime_error(
881  "Only inputs are supported as arguments to ST_Overlaps for now.");
882  }
883  };
884  std::vector<std::shared_ptr<Analyzer::Expr>> geo_args;
885  geo_args.push_back(extract_geo_bounds_from_input(0));
886  geo_args.push_back(extract_geo_bounds_from_input(1));
887 
888  return makeExpr<Analyzer::FunctionOper>(return_type, function_name, geo_args);
889  }
890 
891  bool swap_args = false;
892  bool with_bounds = false;
893  bool negate_result = false;
894  if (function_name == "ST_DWithin"sv) {
895  CHECK_EQ(size_t(3), rex_function->size());
896  function_name = "ST_Distance";
897  return_type = SQLTypeInfo(kDOUBLE, false);
898  } else if (function_name == "ST_DFullyWithin"sv) {
899  CHECK_EQ(size_t(3), rex_function->size());
900  function_name = "ST_MaxDistance";
901  return_type = SQLTypeInfo(kDOUBLE, false);
902  } else {
903  CHECK_EQ(size_t(2), rex_function->size());
904  }
905  if (function_name == "ST_Within"sv) {
906  function_name = "ST_Contains";
907  swap_args = true;
908  } else if (function_name == "ST_Disjoint"sv) {
909  function_name = "ST_Intersects";
910  negate_result = true;
911  }
912  if (func_resolve(function_name, "ST_Contains"sv, "ST_Intersects"sv)) {
913  with_bounds = true;
914  }
915 
916  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
917  SQLTypeInfo arg0_ti;
918  SQLTypeInfo arg1_ti;
919  int32_t lindex0 = 0;
920  int32_t lindex1 = 0;
921 
922  auto geoargs0 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 1 : 0),
923  arg0_ti,
924  lindex0,
925  with_bounds,
926  false,
927  false);
928  if (arg0_ti.get_type() == kLINESTRING) {
929  Datum index;
930  index.intval = lindex0;
931  geoargs0.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
932  }
933  geoargs.insert(geoargs.end(), geoargs0.begin(), geoargs0.end());
934  auto geoargs1 = translateGeoFunctionArg(rex_function->getOperand(swap_args ? 0 : 1),
935  arg1_ti,
936  lindex1,
937  with_bounds,
938  false,
939  false);
940  if (arg1_ti.get_type() == kLINESTRING) {
941  Datum index;
942  index.intval = lindex1;
943  geoargs1.push_back(makeExpr<Analyzer::Constant>(kINT, false, index));
944  }
945  geoargs.insert(geoargs.end(), geoargs1.begin(), geoargs1.end());
946 
947  if (arg0_ti.get_subtype() != kNULLT && arg0_ti.get_subtype() != arg1_ti.get_subtype()) {
948  throw QueryNotSupported(rex_function->getName() +
949  " accepts either two GEOGRAPHY or two GEOMETRY arguments");
950  }
951  // Check SRID match if at least one is set/valid
952  if ((arg0_ti.get_output_srid() > 0 || arg1_ti.get_output_srid() > 0) &&
953  arg0_ti.get_output_srid() != arg1_ti.get_output_srid()) {
954  throw QueryNotSupported(rex_function->getName() + " cannot accept different SRIDs");
955  }
956 
957  std::string specialized_geofunc{function_name + suffix(arg0_ti.get_type()) +
958  suffix(arg1_ti.get_type())};
959 
960  if (arg0_ti.get_subtype() == kGEOGRAPHY && arg0_ti.get_output_srid() == 4326) {
961  // Need to call geodesic runtime functions
962  if (function_name == "ST_Distance"sv) {
963  if ((arg0_ti.get_type() == kPOINT ||
964  (arg0_ti.get_type() == kLINESTRING && lindex0 != 0)) &&
965  (arg1_ti.get_type() == kPOINT ||
966  (arg1_ti.get_type() == kLINESTRING && lindex1 != 0))) {
967  // Geodesic distance between points (or indexed linestrings)
968  specialized_geofunc += "_Geodesic"s;
969  } else {
970  throw QueryNotSupported(function_name +
971  " currently doesn't accept non-POINT geographies");
972  }
973  } else if (rex_function->getName() == "ST_Contains"sv) {
974  // We currently don't have a geodesic implementation of ST_Contains,
975  // allowing calls to a [less precise] cartesian implementation.
976  } else {
977  throw QueryNotSupported(function_name + " doesn't accept geographies");
978  }
979  } else if (function_name == "ST_Distance"sv && rex_function->size() == 3) {
980  if (arg0_ti.get_type() == kPOINT && arg1_ti.get_type() == kPOINT) {
981  // Cartesian distance between points used by ST_DWithin - switch to faster Squared
982  specialized_geofunc += "_Squared"s;
983  }
984  }
985 
986  // Add first input's compression mode and SRID args to enable on-the-fly
987  // decompression/transforms
988  Datum input_compression0;
989  input_compression0.intval = geospatial::get_compression_scheme(arg0_ti);
990  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression0));
991  Datum input_srid0;
992  input_srid0.intval = arg0_ti.get_input_srid();
993  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid0));
994 
995  // Add second input's compression mode and SRID args to enable on-the-fly
996  // decompression/transforms
997  Datum input_compression1;
998  input_compression1.intval = geospatial::get_compression_scheme(arg1_ti);
999  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression1));
1000  Datum input_srid1;
1001  input_srid1.intval = arg1_ti.get_input_srid();
1002  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid1));
1003 
1004  // Add output SRID arg to enable on-the-fly transforms
1005  Datum output_srid;
1006  output_srid.intval = arg0_ti.get_output_srid();
1007  geoargs.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1008 
1009  auto result =
1010  makeExpr<Analyzer::FunctionOper>(return_type, specialized_geofunc, geoargs);
1011  if (negate_result) {
1012  return makeExpr<Analyzer::UOper>(kBOOLEAN, kNOT, result);
1013  }
1014  return result;
1015 }
1016 
1017 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateTernaryGeoFunction(
1018  const RexFunctionOperator* rex_function) const {
1019  CHECK_EQ(size_t(3), rex_function->size());
1020 
1021  auto distance_expr = translateScalarRex(rex_function->getOperand(2));
1022  const auto& distance_ti = SQLTypeInfo(kDOUBLE, false);
1023  if (distance_expr->get_type_info().get_type() != kDOUBLE) {
1024  distance_expr->add_cast(distance_ti);
1025  }
1026 
1027  // Translate the geo distance function call portion
1028  const auto geo_distance_expr = translateBinaryGeoFunction(rex_function);
1029 
1030  if (rex_function->getName() == "ST_DWithin") {
1031  auto func_oper = dynamic_cast<Analyzer::FunctionOper*>(geo_distance_expr.get());
1032  if (func_oper && func_oper->getName() == "ST_Distance_Point_Point_Squared"sv) {
1033  // Point_Point combination will yield geo_distance squared which is faster,
1034  // need to compare it with distance squared
1035  distance_expr = makeExpr<Analyzer::BinOper>(distance_ti,
1036  distance_expr->get_contains_agg(),
1037  kMULTIPLY,
1038  kONE,
1039  distance_expr,
1040  distance_expr);
1041  distance_expr = fold_expr(distance_expr.get());
1042  }
1043  }
1044 
1045  return makeExpr<Analyzer::BinOper>(
1046  kBOOLEAN, kLE, kONE, geo_distance_expr, distance_expr);
1047 }
1048 
1049 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoComparison(
1050  const RexOperator* rex_operator) const {
1051  if (rex_operator->size() != size_t(2)) {
1052  return nullptr;
1053  }
1054 
1055  auto geo_distance_expr = translateScalarRex(rex_operator->getOperand(0));
1056  auto func_oper = dynamic_cast<Analyzer::FunctionOper*>(geo_distance_expr.get());
1057  if (func_oper && func_oper->getName() == "ST_Distance_Point_Point"sv) {
1058  const auto& distance_ti = SQLTypeInfo(kDOUBLE, false);
1059  std::vector<std::shared_ptr<Analyzer::Expr>> geoargs;
1060  for (size_t i = 0; i < func_oper->getArity(); i++) {
1061  geoargs.push_back(func_oper->getOwnArg(i));
1062  }
1063  geo_distance_expr = makeExpr<Analyzer::FunctionOper>(
1064  distance_ti, "ST_Distance_Point_Point_Squared"s, geoargs);
1065  auto distance_expr = translateScalarRex(rex_operator->getOperand(1));
1066  if (distance_expr->get_type_info().get_type() != kDOUBLE) {
1067  distance_expr->add_cast(distance_ti);
1068  }
1069  distance_expr = makeExpr<Analyzer::BinOper>(distance_ti,
1070  distance_expr->get_contains_agg(),
1071  kMULTIPLY,
1072  kONE,
1073  distance_expr,
1074  distance_expr);
1075  distance_expr = fold_expr(distance_expr.get());
1076  return makeExpr<Analyzer::BinOper>(
1077  kBOOLEAN, rex_operator->getOperator(), kONE, geo_distance_expr, distance_expr);
1078  }
1079  return nullptr;
1080 }
1081 
1082 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateFunctionWithGeoArg(
1083  const RexFunctionOperator* rex_function) const {
1084  int32_t lindex = 0;
1085  std::string specialized_geofunc{rex_function->getName()};
1086  if (func_resolve(rex_function->getName(),
1087  "convert_meters_to_pixel_width"sv,
1088  "convert_meters_to_pixel_height"sv)) {
1089  CHECK_EQ(rex_function->size(), 6u);
1090  SQLTypeInfo arg_ti;
1091  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1092  args.push_back(translateScalarRex(rex_function->getOperand(0)));
1093  auto geoargs = translateGeoFunctionArg(
1094  rex_function->getOperand(1), arg_ti, lindex, false, true, false);
1095  // only works on points
1096  if (arg_ti.get_type() != kPOINT) {
1097  throw QueryNotSupported(rex_function->getName() +
1098  " expects a point for the second argument");
1099  }
1100 
1101  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1102 
1103  // Add compression information
1104  Datum input_compression;
1105  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
1106  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1107  if (arg_ti.get_input_srid() != 4326) {
1108  throw QueryNotSupported(
1109  rex_function->getName() +
1110  " currently only supports points of with SRID WGS84/EPSG:4326");
1111  }
1112  Datum input_srid;
1113  input_srid.intval = arg_ti.get_input_srid();
1114  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_srid));
1115  Datum output_srid;
1116  // Forcing web-mercator projection for now
1117  // TODO(croot): check that the input-to-output conversion routines exist?
1118  output_srid.intval =
1119  arg_ti.get_output_srid() != 900913 ? 900913 : arg_ti.get_output_srid();
1120  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, output_srid));
1121 
1122  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1123  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1124  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1125  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1126  return makeExpr<Analyzer::FunctionOper>(
1127  rex_function->getType(), specialized_geofunc, args);
1128  } else if (rex_function->getName() == "is_point_in_view"sv) {
1129  CHECK_EQ(rex_function->size(), 5u);
1130  SQLTypeInfo arg_ti;
1131  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1132  auto geoargs = translateGeoFunctionArg(
1133  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
1134  // only works on points
1135  if (arg_ti.get_type() != kPOINT) {
1136  throw QueryNotSupported(rex_function->getName() +
1137  " expects a point for the second argument");
1138  }
1139 
1140  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1141 
1142  // Add compression information
1143  Datum input_compression;
1144  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
1145  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1146  if (arg_ti.get_input_srid() != 4326) {
1147  throw QueryNotSupported(
1148  rex_function->getName() +
1149  " currently only supports points of with SRID WGS84/EPSG:4326");
1150  }
1151  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1152  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1153  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1154  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1155  return makeExpr<Analyzer::FunctionOper>(
1156  rex_function->getType(), specialized_geofunc, args);
1157  } else if (rex_function->getName() == "is_point_size_in_view"sv) {
1158  CHECK_EQ(rex_function->size(), 6u);
1159  SQLTypeInfo arg_ti;
1160  std::vector<std::shared_ptr<Analyzer::Expr>> args;
1161  auto geoargs = translateGeoFunctionArg(
1162  rex_function->getOperand(0), arg_ti, lindex, false, true, false);
1163  // only works on points
1164  if (arg_ti.get_type() != kPOINT) {
1165  throw QueryNotSupported(rex_function->getName() +
1166  " expects a point for the second argument");
1167  }
1168 
1169  args.insert(args.end(), geoargs.begin(), geoargs.begin() + 1);
1170 
1171  // Add compression information
1172  Datum input_compression;
1173  input_compression.intval = geospatial::get_compression_scheme(arg_ti);
1174  args.push_back(makeExpr<Analyzer::Constant>(kINT, false, input_compression));
1175  if (arg_ti.get_input_srid() != 4326) {
1176  throw QueryNotSupported(
1177  rex_function->getName() +
1178  " currently only supports points of with SRID WGS84/EPSG:4326");
1179  }
1180  args.push_back(translateScalarRex(rex_function->getOperand(1)));
1181  args.push_back(translateScalarRex(rex_function->getOperand(2)));
1182  args.push_back(translateScalarRex(rex_function->getOperand(3)));
1183  args.push_back(translateScalarRex(rex_function->getOperand(4)));
1184  args.push_back(translateScalarRex(rex_function->getOperand(5)));
1185  return makeExpr<Analyzer::FunctionOper>(
1186  rex_function->getType(), specialized_geofunc, args);
1187  }
1188  CHECK(false);
1189  return nullptr;
1190 }
1191 
1192 std::shared_ptr<Analyzer::Expr> RelAlgTranslator::translateGeoOverlapsOper(
1193  const RexOperator* rex_operator) const {
1194  CHECK_EQ(rex_operator->size(), 2u);
1195 
1196  auto translate_input =
1197  [&](const RexScalar* operand) -> std::shared_ptr<Analyzer::Expr> {
1198  const auto input = dynamic_cast<const RexInput*>(operand);
1199  CHECK(input);
1200 
1201  SQLTypeInfo ti;
1202  const auto exprs = translateGeoColumn(input, ti, true, false, false);
1203  CHECK_GT(exprs.size(), 0u);
1204  if (ti.get_type() == kPOINT) {
1205  return exprs.front();
1206  } else {
1207  return exprs.back();
1208  }
1209  };
1210 
1211  SQLQualifier sql_qual{kONE};
1212  SQLOps sql_op{kOVERLAPS};
1213  return makeExpr<Analyzer::BinOper>(SQLTypeInfo(kBOOLEAN, false),
1214  false,
1215  sql_op,
1216  sql_qual,
1217  translate_input(rex_operator->getOperand(1)),
1218  translate_input(rex_operator->getOperand(0)));
1219 }
int8_t tinyintval
Definition: sqltypes.h:134
HOST DEVICE SQLTypes get_subtype() const
Definition: sqltypes.h:259
void set_compression(EncodingType c)
Definition: sqltypes.h:358
void set_size(int s)
Definition: sqltypes.h:356
#define CHECK_EQ(x, y)
Definition: Logger.h:205
auto func_resolve
SQLTypes
Definition: sqltypes.h:39
#define SPIMAP_GEO_PHYSICAL_INPUT(c, i)
Definition: Catalog.h:75
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
HOST DEVICE void set_subtype(SQLTypes st)
Definition: sqltypes.h:349
#define CHECK_GE(x, y)
Definition: Logger.h:210
Definition: sqldefs.h:49
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:258
#define CHECK_GT(x, y)
Definition: Logger.h:209
std::shared_ptr< Analyzer::Expr > translateGeoProjection(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
int32_t intval
Definition: sqltypes.h:136
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:338
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:352
unsigned getIndex() const
static std::shared_ptr< Analyzer::Expr > translateLiteral(const RexLiteral *)
SQLOps getOperator() const
CHECK(cgen_state)
bool has_bounds() const
Definition: sqltypes.h:327
const ColumnDescriptor * getMetadataForColumnBySpi(const int tableId, const size_t spi) const
Definition: Catalog.cpp:1545
void set_output_srid(int s)
Definition: sqltypes.h:354
const std::unordered_map< const RelAlgNode *, int > input_to_nest_level_
void set_comp_param(int p)
Definition: sqltypes.h:359
#define CHECK_LT(x, y)
Definition: Logger.h:207
Definition: sqltypes.h:53
Definition: sqldefs.h:69
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:266
const RelAlgNode * getSourceNode() const
static bool getGeoColumns(const std::string &wkt, 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: geo_types.cpp:637
std::shared_ptr< Analyzer::Expr > translateGeoBinaryConstructor(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
std::vector< uint8_t > compress_coords(std::vector< double > &coords, const SQLTypeInfo &ti)
HOST DEVICE int get_input_srid() const
Definition: sqltypes.h:262
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:183
int32_t get_compression_scheme(const SQLTypeInfo &ti)
std::shared_ptr< Analyzer::Expr > translateFunctionWithGeoArg(const RexFunctionOperator *) const
Definition: sqltypes.h:46
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:294
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:173
Definition: sqldefs.h:39
std::shared_ptr< Analyzer::Expr > fold_expr(const Analyzer::Expr *expr)
std::vector< std::shared_ptr< Analyzer::Expr > > translateGeoFunctionArg(const RexScalar *rex_scalar, SQLTypeInfo &arg_ti, int32_t &lindex, const bool with_bounds, const bool with_render_group, const bool expand_geo_col, const bool is_projection=false) const
double doubleval
Definition: sqltypes.h:139
const Catalog_Namespace::Catalog & cat_
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:264
std::shared_ptr< Analyzer::Expr > translateGeoPredicate(const RexFunctionOperator *, SQLTypeInfo &, const bool with_bounds) const
#define IS_GEO_POLY(T)
Definition: sqltypes.h:177
HOST DEVICE void set_type(SQLTypes t)
Definition: sqltypes.h:348