1 /*-------------------------------------------------------------------------
4 * routines to manage scans on GiST index relations
7 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gist/gistscan.c
13 *-------------------------------------------------------------------------
17 #include "access/gist_private.h"
18 #include "access/gistscan.h"
19 #include "access/relscan.h"
20 #include "utils/lsyscache.h"
21 #include "utils/memutils.h"
22 #include "utils/rel.h"
26 * Pairing heap comparison function for the GISTSearchItem queue
29 pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
31 const GISTSearchItem *sa = (const GISTSearchItem *) a;
32 const GISTSearchItem *sb = (const GISTSearchItem *) b;
33 IndexScanDesc scan = (IndexScanDesc) arg;
36 /* Order according to distance comparison */
37 for (i = 0; i < scan->numberOfOrderBys; i++)
39 if (sa->distances[i] != sb->distances[i])
40 return (sa->distances[i] < sb->distances[i]) ? 1 : -1;
43 /* Heap items go before inner pages, to ensure a depth-first search */
44 if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb))
46 if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb))
54 * Index AM API functions for scanning GiST indexes
58 gistbeginscan(PG_FUNCTION_ARGS)
60 Relation r = (Relation) PG_GETARG_POINTER(0);
61 int nkeys = PG_GETARG_INT32(1);
62 int norderbys = PG_GETARG_INT32(2);
68 scan = RelationGetIndexScan(r, nkeys, norderbys);
70 /* First, set up a GISTSTATE with a scan-lifespan memory context */
71 giststate = initGISTstate(scan->indexRelation);
74 * Everything made below is in the scanCxt, or is a child of the scanCxt,
75 * so it'll all go away automatically in gistendscan.
77 oldCxt = MemoryContextSwitchTo(giststate->scanCxt);
79 /* initialize opaque data */
80 so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
81 so->giststate = giststate;
82 giststate->tempCxt = createTempGistContext();
84 so->queueCxt = giststate->scanCxt; /* see gistrescan */
86 /* workspaces with size dependent on numberOfOrderBys: */
87 so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
88 so->qual_ok = true; /* in case there are zero keys */
89 if (scan->numberOfOrderBys > 0)
91 scan->xs_orderbyvals = palloc0(sizeof(Datum) * scan->numberOfOrderBys);
92 scan->xs_orderbynulls = palloc(sizeof(bool) * scan->numberOfOrderBys);
93 memset(scan->xs_orderbynulls, true, sizeof(bool) * scan->numberOfOrderBys);
96 so->killedItems = NULL; /* until needed */
98 so->curBlkno = InvalidBlockNumber;
99 so->curPageLSN = InvalidXLogRecPtr;
104 * All fields required for index-only scans are initialized in gistrescan,
105 * as we don't know yet if we're doing an index-only scan or not.
108 MemoryContextSwitchTo(oldCxt);
110 PG_RETURN_POINTER(scan);
114 gistrescan(PG_FUNCTION_ARGS)
116 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
117 ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
118 ScanKey orderbys = (ScanKey) PG_GETARG_POINTER(3);
120 /* nkeys and norderbys arguments are ignored */
121 GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
124 MemoryContext oldCxt;
126 /* rescan an existing indexscan --- reset state */
129 * The first time through, we create the search queue in the scanCxt.
130 * Subsequent times through, we create the queue in a separate queueCxt,
131 * which is created on the second call and reset on later calls. Thus, in
132 * the common case where a scan is only rescan'd once, we just put the
133 * queue in scanCxt and don't pay the overhead of making a second memory
134 * context. If we do rescan more than once, the first RBTree is just left
135 * for dead until end of scan; this small wastage seems worth the savings
136 * in the common case.
138 if (so->queue == NULL)
140 /* first time through */
141 Assert(so->queueCxt == so->giststate->scanCxt);
144 else if (so->queueCxt == so->giststate->scanCxt)
146 /* second time through */
147 so->queueCxt = AllocSetContextCreate(so->giststate->scanCxt,
148 "GiST queue context",
149 ALLOCSET_DEFAULT_MINSIZE,
150 ALLOCSET_DEFAULT_INITSIZE,
151 ALLOCSET_DEFAULT_MAXSIZE);
156 /* third or later time through */
157 MemoryContextReset(so->queueCxt);
162 * If we're doing an index-only scan, on the first call, also initialize a
163 * tuple descriptor to represent the returned index tuples and create a
164 * memory context to hold them during the scan.
166 if (scan->xs_want_itup && !scan->xs_itupdesc)
172 * The storage type of the index can be different from the original
173 * datatype being indexed, so we cannot just grab the index's tuple
174 * descriptor. Instead, construct a descriptor with the original data
177 natts = RelationGetNumberOfAttributes(scan->indexRelation);
178 so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts, false);
179 for (attno = 1; attno <= natts; attno++)
181 TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
182 scan->indexRelation->rd_opcintype[attno - 1],
185 scan->xs_itupdesc = so->giststate->fetchTupdesc;
187 so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
188 "GiST page data context",
189 ALLOCSET_DEFAULT_MINSIZE,
190 ALLOCSET_DEFAULT_INITSIZE,
191 ALLOCSET_DEFAULT_MAXSIZE);
194 /* create new, empty RBTree for search queue */
195 oldCxt = MemoryContextSwitchTo(so->queueCxt);
196 so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
197 MemoryContextSwitchTo(oldCxt);
199 so->firstCall = true;
201 /* Update scan key, if a new one is given */
202 if (key && scan->numberOfKeys > 0)
204 void **fn_extras = NULL;
207 * If this isn't the first time through, preserve the fn_extra
208 * pointers, so that if the consistentFns are using them to cache
209 * data, that data is not leaked across a rescan.
213 fn_extras = (void **) palloc(scan->numberOfKeys * sizeof(void *));
214 for (i = 0; i < scan->numberOfKeys; i++)
215 fn_extras[i] = scan->keyData[i].sk_func.fn_extra;
218 memmove(scan->keyData, key,
219 scan->numberOfKeys * sizeof(ScanKeyData));
222 * Modify the scan key so that the Consistent method is called for all
223 * comparisons. The original operator is passed to the Consistent
224 * function in the form of its strategy number, which is available
225 * from the sk_strategy field, and its subtype from the sk_subtype
228 * Next, if any of keys is a NULL and that key is not marked with
229 * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
230 * assume all indexable operators are strict).
234 for (i = 0; i < scan->numberOfKeys; i++)
236 ScanKey skey = scan->keyData + i;
238 fmgr_info_copy(&(skey->sk_func),
239 &(so->giststate->consistentFn[skey->sk_attno - 1]),
240 so->giststate->scanCxt);
242 /* Restore prior fn_extra pointers, if not first time */
244 skey->sk_func.fn_extra = fn_extras[i];
246 if (skey->sk_flags & SK_ISNULL)
248 if (!(skey->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)))
257 /* Update order-by key, if a new one is given */
258 if (orderbys && scan->numberOfOrderBys > 0)
260 void **fn_extras = NULL;
262 /* As above, preserve fn_extra if not first time through */
265 fn_extras = (void **) palloc(scan->numberOfOrderBys * sizeof(void *));
266 for (i = 0; i < scan->numberOfOrderBys; i++)
267 fn_extras[i] = scan->orderByData[i].sk_func.fn_extra;
270 memmove(scan->orderByData, orderbys,
271 scan->numberOfOrderBys * sizeof(ScanKeyData));
273 so->orderByTypes = (Oid *) palloc(scan->numberOfOrderBys * sizeof(Oid));
276 * Modify the order-by key so that the Distance method is called for
277 * all comparisons. The original operator is passed to the Distance
278 * function in the form of its strategy number, which is available
279 * from the sk_strategy field, and its subtype from the sk_subtype
282 for (i = 0; i < scan->numberOfOrderBys; i++)
284 ScanKey skey = scan->orderByData + i;
285 FmgrInfo *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]);
287 /* Check we actually have a distance function ... */
288 if (!OidIsValid(finfo->fn_oid))
289 elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
290 GIST_DISTANCE_PROC, skey->sk_attno,
291 RelationGetRelationName(scan->indexRelation));
293 fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt);
296 * Look up the datatype returned by the original ordering
297 * operator. GiST always uses a float8 for the distance function,
298 * but the ordering operator could be anything else.
300 * XXX: The distance function is only allowed to be lossy if the
301 * ordering operator's result type is float4 or float8. Otherwise
302 * we don't know how to return the distance to the executor. But
303 * we cannot check that here, as we won't know if the distance
304 * function is lossy until it returns *recheck = true for the
307 so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
309 /* Restore prior fn_extra pointers, if not first time */
311 skey->sk_func.fn_extra = fn_extras[i];
322 gistmarkpos(PG_FUNCTION_ARGS)
324 elog(ERROR, "GiST does not support mark/restore");
329 gistrestrpos(PG_FUNCTION_ARGS)
331 elog(ERROR, "GiST does not support mark/restore");
336 gistendscan(PG_FUNCTION_ARGS)
338 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
339 GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
342 * freeGISTstate is enough to clean up everything made by gistbeginscan,
343 * as well as the queueCxt if there is a separate context for it.
345 freeGISTstate(so->giststate);