summaryrefslogtreecommitdiffstats
path: root/src/backend/access/gist/gistscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/gist/gistscan.c')
-rw-r--r--src/backend/access/gist/gistscan.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
new file mode 100644
index 0000000..4252ff4
--- /dev/null
+++ b/src/backend/access/gist/gistscan.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------------------------
+ *
+ * gistscan.c
+ * routines to manage scans on GiST index relations
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/gist/gistscan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gist_private.h"
+#include "access/gistscan.h"
+#include "access/relscan.h"
+#include "utils/float.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+
+/*
+ * Pairing heap comparison function for the GISTSearchItem queue
+ */
+static int
+pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
+{
+ const GISTSearchItem *sa = (const GISTSearchItem *) a;
+ const GISTSearchItem *sb = (const GISTSearchItem *) b;
+ IndexScanDesc scan = (IndexScanDesc) arg;
+ int i;
+
+ /* Order according to distance comparison */
+ for (i = 0; i < scan->numberOfOrderBys; i++)
+ {
+ if (sa->distances[i].isnull)
+ {
+ if (!sb->distances[i].isnull)
+ return -1;
+ }
+ else if (sb->distances[i].isnull)
+ {
+ return 1;
+ }
+ else
+ {
+ int cmp = -float8_cmp_internal(sa->distances[i].value,
+ sb->distances[i].value);
+
+ if (cmp != 0)
+ return cmp;
+ }
+ }
+
+ /* Heap items go before inner pages, to ensure a depth-first search */
+ if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb))
+ return 1;
+ if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb))
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Index AM API functions for scanning GiST indexes
+ */
+
+IndexScanDesc
+gistbeginscan(Relation r, int nkeys, int norderbys)
+{
+ IndexScanDesc scan;
+ GISTSTATE *giststate;
+ GISTScanOpaque so;
+ MemoryContext oldCxt;
+
+ scan = RelationGetIndexScan(r, nkeys, norderbys);
+
+ /* First, set up a GISTSTATE with a scan-lifespan memory context */
+ giststate = initGISTstate(scan->indexRelation);
+
+ /*
+ * Everything made below is in the scanCxt, or is a child of the scanCxt,
+ * so it'll all go away automatically in gistendscan.
+ */
+ oldCxt = MemoryContextSwitchTo(giststate->scanCxt);
+
+ /* initialize opaque data */
+ so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
+ so->giststate = giststate;
+ giststate->tempCxt = createTempGistContext();
+ so->queue = NULL;
+ so->queueCxt = giststate->scanCxt; /* see gistrescan */
+
+ /* workspaces with size dependent on numberOfOrderBys: */
+ so->distances = palloc(sizeof(so->distances[0]) * scan->numberOfOrderBys);
+ so->qual_ok = true; /* in case there are zero keys */
+ if (scan->numberOfOrderBys > 0)
+ {
+ scan->xs_orderbyvals = palloc0(sizeof(Datum) * scan->numberOfOrderBys);
+ scan->xs_orderbynulls = palloc(sizeof(bool) * scan->numberOfOrderBys);
+ memset(scan->xs_orderbynulls, true, sizeof(bool) * scan->numberOfOrderBys);
+ }
+
+ so->killedItems = NULL; /* until needed */
+ so->numKilled = 0;
+ so->curBlkno = InvalidBlockNumber;
+ so->curPageLSN = InvalidXLogRecPtr;
+
+ scan->opaque = so;
+
+ /*
+ * All fields required for index-only scans are initialized in gistrescan,
+ * as we don't know yet if we're doing an index-only scan or not.
+ */
+
+ MemoryContextSwitchTo(oldCxt);
+
+ return scan;
+}
+
+void
+gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
+ ScanKey orderbys, int norderbys)
+{
+ /* nkeys and norderbys arguments are ignored */
+ GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+ bool first_time;
+ int i;
+ MemoryContext oldCxt;
+
+ /* rescan an existing indexscan --- reset state */
+
+ /*
+ * The first time through, we create the search queue in the scanCxt.
+ * Subsequent times through, we create the queue in a separate queueCxt,
+ * which is created on the second call and reset on later calls. Thus, in
+ * the common case where a scan is only rescan'd once, we just put the
+ * queue in scanCxt and don't pay the overhead of making a second memory
+ * context. If we do rescan more than once, the first queue is just left
+ * for dead until end of scan; this small wastage seems worth the savings
+ * in the common case.
+ */
+ if (so->queue == NULL)
+ {
+ /* first time through */
+ Assert(so->queueCxt == so->giststate->scanCxt);
+ first_time = true;
+ }
+ else if (so->queueCxt == so->giststate->scanCxt)
+ {
+ /* second time through */
+ so->queueCxt = AllocSetContextCreate(so->giststate->scanCxt,
+ "GiST queue context",
+ ALLOCSET_DEFAULT_SIZES);
+ first_time = false;
+ }
+ else
+ {
+ /* third or later time through */
+ MemoryContextReset(so->queueCxt);
+ first_time = false;
+ }
+
+ /*
+ * If we're doing an index-only scan, on the first call, also initialize a
+ * tuple descriptor to represent the returned index tuples and create a
+ * memory context to hold them during the scan.
+ */
+ if (scan->xs_want_itup && !scan->xs_hitupdesc)
+ {
+ int natts;
+ int nkeyatts;
+ int attno;
+
+ /*
+ * The storage type of the index can be different from the original
+ * datatype being indexed, so we cannot just grab the index's tuple
+ * descriptor. Instead, construct a descriptor with the original data
+ * types.
+ */
+ natts = RelationGetNumberOfAttributes(scan->indexRelation);
+ nkeyatts = IndexRelationGetNumberOfKeyAttributes(scan->indexRelation);
+ so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts);
+ for (attno = 1; attno <= nkeyatts; attno++)
+ {
+ TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
+ scan->indexRelation->rd_opcintype[attno - 1],
+ -1, 0);
+ }
+
+ for (; attno <= natts; attno++)
+ {
+ /* taking opcintype from giststate->tupdesc */
+ TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
+ TupleDescAttr(so->giststate->leafTupdesc,
+ attno - 1)->atttypid,
+ -1, 0);
+ }
+ scan->xs_hitupdesc = so->giststate->fetchTupdesc;
+
+ /* Also create a memory context that will hold the returned tuples */
+ so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
+ "GiST page data context",
+ ALLOCSET_DEFAULT_SIZES);
+ }
+
+ /* create new, empty pairing heap for search queue */
+ oldCxt = MemoryContextSwitchTo(so->queueCxt);
+ so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
+ MemoryContextSwitchTo(oldCxt);
+
+ so->firstCall = true;
+
+ /* Update scan key, if a new one is given */
+ if (key && scan->numberOfKeys > 0)
+ {
+ void **fn_extras = NULL;
+
+ /*
+ * If this isn't the first time through, preserve the fn_extra
+ * pointers, so that if the consistentFns are using them to cache
+ * data, that data is not leaked across a rescan.
+ */
+ if (!first_time)
+ {
+ fn_extras = (void **) palloc(scan->numberOfKeys * sizeof(void *));
+ for (i = 0; i < scan->numberOfKeys; i++)
+ fn_extras[i] = scan->keyData[i].sk_func.fn_extra;
+ }
+
+ memmove(scan->keyData, key,
+ scan->numberOfKeys * sizeof(ScanKeyData));
+
+ /*
+ * Modify the scan key so that the Consistent method is called for all
+ * comparisons. The original operator is passed to the Consistent
+ * function in the form of its strategy number, which is available
+ * from the sk_strategy field, and its subtype from the sk_subtype
+ * field.
+ *
+ * Next, if any of keys is a NULL and that key is not marked with
+ * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
+ * assume all indexable operators are strict).
+ */
+ so->qual_ok = true;
+
+ for (i = 0; i < scan->numberOfKeys; i++)
+ {
+ ScanKey skey = scan->keyData + i;
+
+ /*
+ * Copy consistent support function to ScanKey structure instead
+ * of function implementing filtering operator.
+ */
+ fmgr_info_copy(&(skey->sk_func),
+ &(so->giststate->consistentFn[skey->sk_attno - 1]),
+ so->giststate->scanCxt);
+
+ /* Restore prior fn_extra pointers, if not first time */
+ if (!first_time)
+ skey->sk_func.fn_extra = fn_extras[i];
+
+ if (skey->sk_flags & SK_ISNULL)
+ {
+ if (!(skey->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)))
+ so->qual_ok = false;
+ }
+ }
+
+ if (!first_time)
+ pfree(fn_extras);
+ }
+
+ /* Update order-by key, if a new one is given */
+ if (orderbys && scan->numberOfOrderBys > 0)
+ {
+ void **fn_extras = NULL;
+
+ /* As above, preserve fn_extra if not first time through */
+ if (!first_time)
+ {
+ fn_extras = (void **) palloc(scan->numberOfOrderBys * sizeof(void *));
+ for (i = 0; i < scan->numberOfOrderBys; i++)
+ fn_extras[i] = scan->orderByData[i].sk_func.fn_extra;
+ }
+
+ memmove(scan->orderByData, orderbys,
+ scan->numberOfOrderBys * sizeof(ScanKeyData));
+
+ so->orderByTypes = (Oid *) palloc(scan->numberOfOrderBys * sizeof(Oid));
+
+ /*
+ * Modify the order-by key so that the Distance method is called for
+ * all comparisons. The original operator is passed to the Distance
+ * function in the form of its strategy number, which is available
+ * from the sk_strategy field, and its subtype from the sk_subtype
+ * field.
+ */
+ for (i = 0; i < scan->numberOfOrderBys; i++)
+ {
+ ScanKey skey = scan->orderByData + i;
+ FmgrInfo *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]);
+
+ /* Check we actually have a distance function ... */
+ if (!OidIsValid(finfo->fn_oid))
+ elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
+ GIST_DISTANCE_PROC, skey->sk_attno,
+ RelationGetRelationName(scan->indexRelation));
+
+ /*
+ * Look up the datatype returned by the original ordering
+ * operator. GiST always uses a float8 for the distance function,
+ * but the ordering operator could be anything else.
+ *
+ * XXX: The distance function is only allowed to be lossy if the
+ * ordering operator's result type is float4 or float8. Otherwise
+ * we don't know how to return the distance to the executor. But
+ * we cannot check that here, as we won't know if the distance
+ * function is lossy until it returns *recheck = true for the
+ * first time.
+ */
+ so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
+
+ /*
+ * Copy distance support function to ScanKey structure instead of
+ * function implementing ordering operator.
+ */
+ fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt);
+
+ /* Restore prior fn_extra pointers, if not first time */
+ if (!first_time)
+ skey->sk_func.fn_extra = fn_extras[i];
+ }
+
+ if (!first_time)
+ pfree(fn_extras);
+ }
+
+ /* any previous xs_hitup will have been pfree'd in context resets above */
+ scan->xs_hitup = NULL;
+}
+
+void
+gistendscan(IndexScanDesc scan)
+{
+ GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+
+ /*
+ * freeGISTstate is enough to clean up everything made by gistbeginscan,
+ * as well as the queueCxt if there is a separate context for it.
+ */
+ freeGISTstate(so->giststate);
+}