PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ 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 commuted 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 IndexableFunction::dims, IndexableFunction::expand_arg, expandFunctionOid(), IndexableFunction::fn_name, get_strategy_by_type(), gserialized_joinsel_internal(), gserialized_sel_internal(), IndexableFunction::index, needsSpatialIndex(), and opFamilyAmOid().

Here is the call graph for this function: