PostGIS  3.4.0dev-r@@SVN_REVISION@@

◆ postgis_index_supportfn()

Datum postgis_index_supportfn ( PG_FUNCTION_ARGS  )

Definition at line 320 of file gserialized_supportfn.c.

321 {
322  Node *rawreq = (Node *) PG_GETARG_POINTER(0);
323  Node *ret = NULL;
324 
325  /* The support function need the cache to be populated to know what the type Oids are.
326  * Otherwise it will need look them up dynamically, which only works in the schema where Postgis
327  * is installed is part of the search path (Trac #4739)
328  */
329  postgis_initialize_cache();
330 
331  if (IsA(rawreq, SupportRequestSelectivity))
332  {
333  SupportRequestSelectivity *req = (SupportRequestSelectivity *) rawreq;
334 
335  if (req->is_join)
336  {
337  req->selectivity = gserialized_joinsel_internal(req->root, req->args, req->jointype, 2);
338  }
339  else
340  {
341  req->selectivity = gserialized_sel_internal(req->root, req->args, req->varRelid, 2);
342  }
343  POSTGIS_DEBUGF(2, "%s: got selectivity %g", __func__, req->selectivity);
344  PG_RETURN_POINTER(req);
345  }
346 
347  /*
348  * This support function is strictly for adding spatial index
349  * support.
350  */
351  if (IsA(rawreq, SupportRequestIndexCondition))
352  {
353  SupportRequestIndexCondition *req = (SupportRequestIndexCondition *) rawreq;
354 
355  if (is_funcclause(req->node)) /* ST_Something() */
356  {
357  FuncExpr *clause = (FuncExpr *) req->node;
358  Oid funcid = clause->funcid;
359  IndexableFunction idxfn = {NULL, 0, 0, 0, 0};
360  Oid opfamilyoid = req->opfamily; /* OPERATOR FAMILY of the index */
361 
362  if (needsSpatialIndex(funcid, &idxfn))
363  {
364  int nargs = list_length(clause->args);
365  Node *leftarg, *rightarg;
366  Oid leftdatatype, rightdatatype, oproid;
367  bool swapped = false;
368 
369  /*
370  * Only add an operator condition for GIST, SPGIST, BRIN indexes.
371  * Effectively this means only these opclasses will get automatic
372  * indexing when used with one of the indexable functions
373  * gist_geometry_ops_2d, gist_geometry_ops_nd,
374  * spgist_geometry_ops_2d, spgist_geometry_ops_nd
375  */
376  uint8_t opfamilydims;
377  Oid opfamilyam = opFamilyAmOid(opfamilyoid, &opfamilydims);
378  if (opfamilyam != GIST_AM_OID &&
379  opfamilyam != SPGIST_AM_OID &&
380  opfamilyam != BRIN_AM_OID)
381  {
382  PG_RETURN_POINTER((Node *)NULL);
383  }
384 
385  /*
386  * We can only do something with index matches on the first
387  * or second argument.
388  */
389  if (req->indexarg > 1)
390  PG_RETURN_POINTER((Node *)NULL);
391 
392  /*
393  * Avoid using a 3D operator (@@) with a
394  * non-3D function (like ST_Within)
395  * https://trac.osgeo.org/postgis/ticket/5025
396  */
397  if (opfamilydims == 3 && idxfn.dims != 3)
398  PG_RETURN_POINTER((Node *)NULL);
399 
400  /*
401  * Make sure we have enough arguments.
402  */
403  if (nargs < 2 || nargs < idxfn.expand_arg)
404  elog(ERROR, "%s: associated with function with %d arguments", __func__, nargs);
405 
406  /*
407  * Extract "leftarg" as the arg matching
408  * the index and "rightarg" as the other, even if
409  * they were in the opposite order in the call.
410  * NOTE: The functions we deal with here treat
411  * their first two arguments symmetrically
412  * enough that we needn't distinguish between
413  * the two cases beyond this. Could be more
414  * complications in the future.
415  */
416  if (req->indexarg == 0)
417  {
418  leftarg = linitial(clause->args);
419  rightarg = lsecond(clause->args);
420  }
421  else
422  {
423  rightarg = linitial(clause->args);
424  leftarg = lsecond(clause->args);
425  swapped = true;
426  }
427  /*
428  * Need the argument types (which should always be geometry/geography) as
429  * this support function is only ever bound to functions
430  * using those types.
431  */
432  leftdatatype = exprType(leftarg);
433  rightdatatype = exprType(rightarg);
434 
435  /*
436  * Given the index operator family and the arguments and the
437  * desired strategy number we can now lookup the operator
438  * we want (usually && or &&&).
439  */
440  oproid = get_opfamily_member(opfamilyoid,
441  leftdatatype,
442  rightdatatype,
443  get_strategy_by_type(leftdatatype, idxfn.index));
444  if (!OidIsValid(oproid))
445  elog(ERROR,
446  "no spatial operator found for '%s': opfamily %u type %d",
447  idxfn.fn_name,
448  opfamilyoid,
449  leftdatatype);
450 
451  /*
452  * For the ST_DWithin variants we need to build a more complex return.
453  * We want to expand the non-indexed side of the call by the
454  * radius and then apply the operator.
455  * st_dwithin(g1, g2, radius) yields this, if g1 is the indexarg:
456  * g1 && st_expand(g2, radius)
457  */
458  if (idxfn.expand_arg)
459  {
460  Expr *expr;
461  Node *radiusarg = (Node *) list_nth(clause->args, idxfn.expand_arg-1);
462  Oid expandfn_oid = expandFunctionOid(rightdatatype, clause->funcid);
463 
464  FuncExpr *expandexpr = makeFuncExpr(expandfn_oid, rightdatatype,
465  list_make2(rightarg, radiusarg),
466  InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
467 
468  /*
469  * The comparison expression has to be a pseudo constant,
470  * (not volatile or dependent on the target index table)
471  */
472 #if POSTGIS_PGSQL_VERSION >= 140
473  if (!is_pseudo_constant_for_index(req->root, (Node*)expandexpr, req->index))
474 #else
475  if (!is_pseudo_constant_for_index((Node*)expandexpr, req->index))
476 #endif
477  PG_RETURN_POINTER((Node*)NULL);
478 
479  /* OK, we can make an index expression */
480  expr = make_opclause(oproid, BOOLOID, false,
481  (Expr *) leftarg, (Expr *) expandexpr,
482  InvalidOid, InvalidOid);
483 
484  ret = (Node *)(list_make1(expr));
485  }
486  /*
487  * For the ST_Intersects variants we just need to return
488  * an index OpExpr with the original arguments on each
489  * side.
490  * st_intersects(g1, g2) yields: g1 && g2
491  */
492  else
493  {
494  Expr *expr;
495  /*
496  * The comparison expression has to be a pseudoconstant
497  * (not volatile or dependent on the target index's table)
498  */
499 #if POSTGIS_PGSQL_VERSION >= 140
500  if (!is_pseudo_constant_for_index(req->root, rightarg, req->index))
501 #else
502  if (!is_pseudo_constant_for_index(rightarg, req->index))
503 #endif
504  PG_RETURN_POINTER((Node*)NULL);
505 
506  /*
507  * Arguments were swapped to put the index value on the
508  * left, so we need the commutated operator for
509  * the OpExpr
510  */
511  if (swapped)
512  {
513  oproid = get_commutator(oproid);
514  if (!OidIsValid(oproid))
515  PG_RETURN_POINTER((Node *)NULL);
516  }
517 
518  expr = make_opclause(oproid, BOOLOID, false,
519  (Expr *) leftarg, (Expr *) rightarg,
520  InvalidOid, InvalidOid);
521 
522  ret = (Node *)(list_make1(expr));
523  }
524 
525  /*
526  * Set the lossy field on the SupportRequestIndexCondition parameter
527  * to indicate that the index alone is not sufficient to evaluate
528  * the condition. The function must also still be applied.
529  */
530  req->lossy = true;
531 
532  PG_RETURN_POINTER(ret);
533  }
534  else
535  {
536  elog(WARNING, "support function '%s' called from unsupported spatial function", __func__);
537  }
538  }
539  }
540 
541  PG_RETURN_POINTER(ret);
542 }
static bool needsSpatialIndex(Oid funcid, IndexableFunction *idxfn)
static Oid expandFunctionOid(Oid geotype, Oid callingfunc)
static Oid opFamilyAmOid(Oid opfamilyoid, uint8_t *dims)
float8 gserialized_sel_internal(PlannerInfo *root, List *args, int varRelid, int mode)
This function should return an estimation of the number of rows returned by a query involving an over...
float8 gserialized_joinsel_internal(PlannerInfo *root, List *args, JoinType jointype, int mode)
static int16 get_strategy_by_type(Oid first_type, uint16_t index)

References expandFunctionOid(), get_strategy_by_type(), gserialized_joinsel_internal(), gserialized_sel_internal(), needsSpatialIndex(), and opFamilyAmOid().

Here is the call graph for this function: