Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistscan.c
4 : * routines to manage scans on GiST index relations
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/gist/gistscan.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/gist_private.h"
18 : #include "access/gistscan.h"
19 : #include "access/relscan.h"
20 : #include "utils/float.h"
21 : #include "utils/lsyscache.h"
22 : #include "utils/memutils.h"
23 : #include "utils/rel.h"
24 :
25 :
26 : /*
27 : * Pairing heap comparison function for the GISTSearchItem queue
28 : */
29 : static int
30 368418 : pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
31 : {
32 368418 : const GISTSearchItem *sa = (const GISTSearchItem *) a;
33 368418 : const GISTSearchItem *sb = (const GISTSearchItem *) b;
34 368418 : IndexScanDesc scan = (IndexScanDesc) arg;
35 : int i;
36 :
37 : /* Order according to distance comparison */
38 372600 : for (i = 0; i < scan->numberOfOrderBys; i++)
39 : {
40 69850 : if (sa->distances[i].isnull)
41 : {
42 108 : if (!sb->distances[i].isnull)
43 108 : return -1;
44 : }
45 69742 : else if (sb->distances[i].isnull)
46 : {
47 14 : return 1;
48 : }
49 : else
50 : {
51 69728 : int cmp = -float8_cmp_internal(sa->distances[i].value,
52 : sb->distances[i].value);
53 :
54 69728 : if (cmp != 0)
55 65546 : return cmp;
56 : }
57 : }
58 :
59 : /* Heap items go before inner pages, to ensure a depth-first search */
60 302750 : if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb))
61 0 : return 1;
62 302750 : if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb))
63 4 : return -1;
64 :
65 302746 : return 0;
66 : }
67 :
68 :
69 : /*
70 : * Index AM API functions for scanning GiST indexes
71 : */
72 :
73 : IndexScanDesc
74 6212 : gistbeginscan(Relation r, int nkeys, int norderbys)
75 : {
76 : IndexScanDesc scan;
77 : GISTSTATE *giststate;
78 : GISTScanOpaque so;
79 : MemoryContext oldCxt;
80 :
81 6212 : scan = RelationGetIndexScan(r, nkeys, norderbys);
82 :
83 : /* First, set up a GISTSTATE with a scan-lifespan memory context */
84 6212 : giststate = initGISTstate(scan->indexRelation);
85 :
86 : /*
87 : * Everything made below is in the scanCxt, or is a child of the scanCxt,
88 : * so it'll all go away automatically in gistendscan.
89 : */
90 6212 : oldCxt = MemoryContextSwitchTo(giststate->scanCxt);
91 :
92 : /* initialize opaque data */
93 6212 : so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
94 6212 : so->giststate = giststate;
95 6212 : giststate->tempCxt = createTempGistContext();
96 6212 : so->queue = NULL;
97 6212 : so->queueCxt = giststate->scanCxt; /* see gistrescan */
98 :
99 : /* workspaces with size dependent on numberOfOrderBys: */
100 6212 : so->distances = palloc(sizeof(so->distances[0]) * scan->numberOfOrderBys);
101 6212 : so->qual_ok = true; /* in case there are zero keys */
102 6212 : if (scan->numberOfOrderBys > 0)
103 : {
104 126 : scan->xs_orderbyvals = palloc0(sizeof(Datum) * scan->numberOfOrderBys);
105 126 : scan->xs_orderbynulls = palloc(sizeof(bool) * scan->numberOfOrderBys);
106 126 : memset(scan->xs_orderbynulls, true, sizeof(bool) * scan->numberOfOrderBys);
107 : }
108 :
109 6212 : so->killedItems = NULL; /* until needed */
110 6212 : so->numKilled = 0;
111 6212 : so->curBlkno = InvalidBlockNumber;
112 6212 : so->curPageLSN = InvalidXLogRecPtr;
113 :
114 6212 : scan->opaque = so;
115 :
116 : /*
117 : * All fields required for index-only scans are initialized in gistrescan,
118 : * as we don't know yet if we're doing an index-only scan or not.
119 : */
120 :
121 6212 : MemoryContextSwitchTo(oldCxt);
122 :
123 6212 : return scan;
124 : }
125 :
126 : void
127 6302 : gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
128 : ScanKey orderbys, int norderbys)
129 : {
130 : /* nkeys and norderbys arguments are ignored */
131 6302 : GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
132 : bool first_time;
133 : int i;
134 : MemoryContext oldCxt;
135 :
136 : /* rescan an existing indexscan --- reset state */
137 :
138 : /*
139 : * The first time through, we create the search queue in the scanCxt.
140 : * Subsequent times through, we create the queue in a separate queueCxt,
141 : * which is created on the second call and reset on later calls. Thus, in
142 : * the common case where a scan is only rescan'd once, we just put the
143 : * queue in scanCxt and don't pay the overhead of making a second memory
144 : * context. If we do rescan more than once, the first queue is just left
145 : * for dead until end of scan; this small wastage seems worth the savings
146 : * in the common case.
147 : */
148 6302 : if (so->queue == NULL)
149 : {
150 : /* first time through */
151 : Assert(so->queueCxt == so->giststate->scanCxt);
152 6212 : first_time = true;
153 : }
154 90 : else if (so->queueCxt == so->giststate->scanCxt)
155 : {
156 : /* second time through */
157 30 : so->queueCxt = AllocSetContextCreate(so->giststate->scanCxt,
158 : "GiST queue context",
159 : ALLOCSET_DEFAULT_SIZES);
160 30 : first_time = false;
161 : }
162 : else
163 : {
164 : /* third or later time through */
165 60 : MemoryContextReset(so->queueCxt);
166 60 : first_time = false;
167 : }
168 :
169 : /*
170 : * If we're doing an index-only scan, on the first call, also initialize a
171 : * tuple descriptor to represent the returned index tuples and create a
172 : * memory context to hold them during the scan.
173 : */
174 6302 : if (scan->xs_want_itup && !scan->xs_hitupdesc)
175 : {
176 : int natts;
177 : int nkeyatts;
178 : int attno;
179 :
180 : /*
181 : * The storage type of the index can be different from the original
182 : * datatype being indexed, so we cannot just grab the index's tuple
183 : * descriptor. Instead, construct a descriptor with the original data
184 : * types.
185 : */
186 668 : natts = RelationGetNumberOfAttributes(scan->indexRelation);
187 668 : nkeyatts = IndexRelationGetNumberOfKeyAttributes(scan->indexRelation);
188 668 : so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts);
189 1362 : for (attno = 1; attno <= nkeyatts; attno++)
190 : {
191 694 : TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
192 694 : scan->indexRelation->rd_opcintype[attno - 1],
193 : -1, 0);
194 : }
195 :
196 668 : for (; attno <= natts; attno++)
197 : {
198 : /* taking opcintype from giststate->tupdesc */
199 0 : TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
200 0 : TupleDescAttr(so->giststate->leafTupdesc,
201 : attno - 1)->atttypid,
202 : -1, 0);
203 : }
204 668 : scan->xs_hitupdesc = so->giststate->fetchTupdesc;
205 :
206 : /* Also create a memory context that will hold the returned tuples */
207 668 : so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
208 : "GiST page data context",
209 : ALLOCSET_DEFAULT_SIZES);
210 : }
211 :
212 : /* create new, empty pairing heap for search queue */
213 6302 : oldCxt = MemoryContextSwitchTo(so->queueCxt);
214 6302 : so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
215 6302 : MemoryContextSwitchTo(oldCxt);
216 :
217 6302 : so->firstCall = true;
218 :
219 : /* Update scan key, if a new one is given */
220 6302 : if (key && scan->numberOfKeys > 0)
221 : {
222 6152 : void **fn_extras = NULL;
223 :
224 : /*
225 : * If this isn't the first time through, preserve the fn_extra
226 : * pointers, so that if the consistentFns are using them to cache
227 : * data, that data is not leaked across a rescan.
228 : */
229 6152 : if (!first_time)
230 : {
231 30 : fn_extras = (void **) palloc(scan->numberOfKeys * sizeof(void *));
232 60 : for (i = 0; i < scan->numberOfKeys; i++)
233 30 : fn_extras[i] = scan->keyData[i].sk_func.fn_extra;
234 : }
235 :
236 6152 : memcpy(scan->keyData, key, scan->numberOfKeys * sizeof(ScanKeyData));
237 :
238 : /*
239 : * Modify the scan key so that the Consistent method is called for all
240 : * comparisons. The original operator is passed to the Consistent
241 : * function in the form of its strategy number, which is available
242 : * from the sk_strategy field, and its subtype from the sk_subtype
243 : * field.
244 : *
245 : * Next, if any of keys is a NULL and that key is not marked with
246 : * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
247 : * assume all indexable operators are strict).
248 : */
249 6152 : so->qual_ok = true;
250 :
251 15378 : for (i = 0; i < scan->numberOfKeys; i++)
252 : {
253 9226 : ScanKey skey = scan->keyData + i;
254 :
255 : /*
256 : * Copy consistent support function to ScanKey structure instead
257 : * of function implementing filtering operator.
258 : */
259 9226 : fmgr_info_copy(&(skey->sk_func),
260 9226 : &(so->giststate->consistentFn[skey->sk_attno - 1]),
261 9226 : so->giststate->scanCxt);
262 :
263 : /* Restore prior fn_extra pointers, if not first time */
264 9226 : if (!first_time)
265 30 : skey->sk_func.fn_extra = fn_extras[i];
266 :
267 9226 : if (skey->sk_flags & SK_ISNULL)
268 : {
269 26 : if (!(skey->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)))
270 0 : so->qual_ok = false;
271 : }
272 : }
273 :
274 6152 : if (!first_time)
275 30 : pfree(fn_extras);
276 : }
277 :
278 : /* Update order-by key, if a new one is given */
279 6302 : if (orderbys && scan->numberOfOrderBys > 0)
280 : {
281 198 : void **fn_extras = NULL;
282 :
283 : /* As above, preserve fn_extra if not first time through */
284 198 : if (!first_time)
285 : {
286 72 : fn_extras = (void **) palloc(scan->numberOfOrderBys * sizeof(void *));
287 144 : for (i = 0; i < scan->numberOfOrderBys; i++)
288 72 : fn_extras[i] = scan->orderByData[i].sk_func.fn_extra;
289 : }
290 :
291 198 : memcpy(scan->orderByData, orderbys, scan->numberOfOrderBys * sizeof(ScanKeyData));
292 :
293 198 : so->orderByTypes = (Oid *) palloc(scan->numberOfOrderBys * sizeof(Oid));
294 :
295 : /*
296 : * Modify the order-by key so that the Distance method is called for
297 : * all comparisons. The original operator is passed to the Distance
298 : * function in the form of its strategy number, which is available
299 : * from the sk_strategy field, and its subtype from the sk_subtype
300 : * field.
301 : */
302 396 : for (i = 0; i < scan->numberOfOrderBys; i++)
303 : {
304 198 : ScanKey skey = scan->orderByData + i;
305 198 : FmgrInfo *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]);
306 :
307 : /* Check we actually have a distance function ... */
308 198 : if (!OidIsValid(finfo->fn_oid))
309 0 : elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
310 : GIST_DISTANCE_PROC, skey->sk_attno,
311 : RelationGetRelationName(scan->indexRelation));
312 :
313 : /*
314 : * Look up the datatype returned by the original ordering
315 : * operator. GiST always uses a float8 for the distance function,
316 : * but the ordering operator could be anything else.
317 : *
318 : * XXX: The distance function is only allowed to be lossy if the
319 : * ordering operator's result type is float4 or float8. Otherwise
320 : * we don't know how to return the distance to the executor. But
321 : * we cannot check that here, as we won't know if the distance
322 : * function is lossy until it returns *recheck = true for the
323 : * first time.
324 : */
325 198 : so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
326 :
327 : /*
328 : * Copy distance support function to ScanKey structure instead of
329 : * function implementing ordering operator.
330 : */
331 198 : fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt);
332 :
333 : /* Restore prior fn_extra pointers, if not first time */
334 198 : if (!first_time)
335 72 : skey->sk_func.fn_extra = fn_extras[i];
336 : }
337 :
338 198 : if (!first_time)
339 72 : pfree(fn_extras);
340 : }
341 :
342 : /* any previous xs_hitup will have been pfree'd in context resets above */
343 6302 : scan->xs_hitup = NULL;
344 6302 : }
345 :
346 : void
347 5900 : gistendscan(IndexScanDesc scan)
348 : {
349 5900 : GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
350 :
351 : /*
352 : * freeGISTstate is enough to clean up everything made by gistbeginscan,
353 : * as well as the queueCxt if there is a separate context for it.
354 : */
355 5900 : freeGISTstate(so->giststate);
356 5900 : }
|