PostGIS  3.3.9dev-r@@SVN_REVISION@@

◆ postgis_index_supportfn()

Datum postgis_index_supportfn ( PG_FUNCTION_ARGS  )

Definition at line 322 of file gserialized_supportfn.c.

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