Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * spgscan.c
4 : : * routines for scanning SP-GiST indexes
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/spgist/spgscan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include "access/genam.h"
19 : : #include "access/relscan.h"
20 : : #include "access/spgist_private.h"
21 : : #include "miscadmin.h"
22 : : #include "pgstat.h"
23 : : #include "storage/bufmgr.h"
24 : : #include "utils/datum.h"
25 : : #include "utils/float.h"
26 : : #include "utils/lsyscache.h"
27 : : #include "utils/memutils.h"
28 : : #include "utils/rel.h"
29 : :
30 : : typedef void (*storeRes_func) (SpGistScanOpaque so, ItemPointer heapPtr,
31 : : Datum leafValue, bool isNull,
32 : : SpGistLeafTuple leafTuple, bool recheck,
33 : : bool recheckDistances, double *distances);
34 : :
35 : : /*
36 : : * Pairing heap comparison function for the SpGistSearchItem queue.
37 : : * KNN-searches currently only support NULLS LAST. So, preserve this logic
38 : : * here.
39 : : */
40 : : static int
2034 akorotkov@postgresql 41 :CBC 2195417 : pairingheap_SpGistSearchItem_cmp(const pairingheap_node *a,
42 : : const pairingheap_node *b, void *arg)
43 : : {
1789 tgl@sss.pgh.pa.us 44 : 2195417 : const SpGistSearchItem *sa = (const SpGistSearchItem *) a;
45 : 2195417 : const SpGistSearchItem *sb = (const SpGistSearchItem *) b;
2034 akorotkov@postgresql 46 : 2195417 : SpGistScanOpaque so = (SpGistScanOpaque) arg;
47 : : int i;
48 : :
49 [ + + ]: 2195417 : if (sa->isNull)
50 : : {
51 [ + + ]: 282 : if (!sb->isNull)
52 : 261 : return -1;
53 : : }
54 [ + + ]: 2195135 : else if (sb->isNull)
55 : : {
56 : 220 : return 1;
57 : : }
58 : : else
59 : : {
60 : : /* Order according to distance comparison */
1663 61 [ + + ]: 2314392 : for (i = 0; i < so->numberOfNonNullOrderBys; i++)
62 : : {
2034 63 [ - + - - ]: 2016278 : if (isnan(sa->distances[i]) && isnan(sb->distances[i]))
2034 akorotkov@postgresql 64 :UBC 0 : continue; /* NaN == NaN */
2034 akorotkov@postgresql 65 [ - + ]:CBC 2016278 : if (isnan(sa->distances[i]))
2034 akorotkov@postgresql 66 :UBC 0 : return -1; /* NaN > number */
2034 akorotkov@postgresql 67 [ - + ]:CBC 2016278 : if (isnan(sb->distances[i]))
2034 akorotkov@postgresql 68 :UBC 0 : return 1; /* number < NaN */
2034 akorotkov@postgresql 69 [ + + ]:CBC 2016278 : if (sa->distances[i] != sb->distances[i])
70 [ + + ]: 1896801 : return (sa->distances[i] < sb->distances[i]) ? 1 : -1;
71 : : }
72 : : }
73 : :
74 : : /* Leaf items go before inner pages, to ensure a depth-first search */
75 [ + + + + ]: 298135 : if (sa->isLeaf && !sb->isLeaf)
76 : 2094 : return 1;
77 [ + + + + ]: 296041 : if (!sa->isLeaf && sb->isLeaf)
78 : 2381 : return -1;
79 : :
80 : 293660 : return 0;
81 : : }
82 : :
83 : : static void
1789 tgl@sss.pgh.pa.us 84 : 229076 : spgFreeSearchItem(SpGistScanOpaque so, SpGistSearchItem *item)
85 : : {
86 : : /* value is of type attType if isLeaf, else of type attLeafType */
87 : : /* (no, that is not backwards; yes, it's confusing) */
1106 88 [ + - ]: 229076 : if (!(item->isLeaf ? so->state.attType.attbyval :
89 [ + + + + ]: 458152 : so->state.attLeafType.attbyval) &&
2034 akorotkov@postgresql 90 : 229076 : DatumGetPointer(item->value) != NULL)
91 : 144613 : pfree(DatumGetPointer(item->value));
92 : :
1105 tgl@sss.pgh.pa.us 93 [ + + ]: 229076 : if (item->leafTuple)
94 : 30 : pfree(item->leafTuple);
95 : :
2034 akorotkov@postgresql 96 [ + + ]: 229076 : if (item->traversalValue)
97 : 22164 : pfree(item->traversalValue);
98 : :
99 : 229076 : pfree(item);
4502 tgl@sss.pgh.pa.us 100 : 229076 : }
101 : :
102 : : /*
103 : : * Add SpGistSearchItem to queue
104 : : *
105 : : * Called in queue context
106 : : */
107 : : static void
1789 108 : 230450 : spgAddSearchItemToQueue(SpGistScanOpaque so, SpGistSearchItem *item)
109 : : {
2034 akorotkov@postgresql 110 : 230450 : pairingheap_add(so->scanQueue, &item->phNode);
111 : 230450 : }
112 : :
113 : : static SpGistSearchItem *
114 : 230450 : spgAllocSearchItem(SpGistScanOpaque so, bool isnull, double *distances)
115 : : {
116 : : /* allocate distance array only for non-NULL items */
117 : : SpGistSearchItem *item =
331 tgl@sss.pgh.pa.us 118 [ + + ]: 230450 : palloc(SizeOfSpGistSearchItem(isnull ? 0 : so->numberOfNonNullOrderBys));
119 : :
2034 akorotkov@postgresql 120 : 230450 : item->isNull = isnull;
121 : :
1669 122 [ + + + + ]: 230450 : if (!isnull && so->numberOfNonNullOrderBys > 0)
2034 123 : 188921 : memcpy(item->distances, distances,
1669 124 : 188921 : sizeof(item->distances[0]) * so->numberOfNonNullOrderBys);
125 : :
2034 126 : 230450 : return item;
127 : : }
128 : :
129 : : static void
130 : 491 : spgAddStartItem(SpGistScanOpaque so, bool isnull)
131 : : {
132 : : SpGistSearchItem *startEntry =
331 tgl@sss.pgh.pa.us 133 : 491 : spgAllocSearchItem(so, isnull, so->zeroDistances);
134 : :
2034 akorotkov@postgresql 135 [ + + ]: 491 : ItemPointerSet(&startEntry->heapPtr,
136 : : isnull ? SPGIST_NULL_BLKNO : SPGIST_ROOT_BLKNO,
137 : : FirstOffsetNumber);
138 : 491 : startEntry->isLeaf = false;
139 : 491 : startEntry->level = 0;
140 : 491 : startEntry->value = (Datum) 0;
1105 tgl@sss.pgh.pa.us 141 : 491 : startEntry->leafTuple = NULL;
2034 akorotkov@postgresql 142 : 491 : startEntry->traversalValue = NULL;
143 : 491 : startEntry->recheck = false;
144 : 491 : startEntry->recheckDistances = false;
145 : :
146 : 491 : spgAddSearchItemToQueue(so, startEntry);
4502 tgl@sss.pgh.pa.us 147 : 491 : }
148 : :
149 : : /*
150 : : * Initialize queue to search the root page, resetting
151 : : * any previously active scan
152 : : */
153 : : static void
154 : 464 : resetSpGistScanOpaque(SpGistScanOpaque so)
155 : : {
156 : : MemoryContext oldCtx;
157 : :
2042 rhodiumtoad@postgres 158 : 464 : MemoryContextReset(so->traversalCxt);
159 : :
2034 akorotkov@postgresql 160 : 464 : oldCtx = MemoryContextSwitchTo(so->traversalCxt);
161 : :
162 : : /* initialize queue only for distance-ordered scans */
163 : 464 : so->scanQueue = pairingheap_allocate(pairingheap_SpGistSearchItem_cmp, so);
164 : :
4417 tgl@sss.pgh.pa.us 165 [ + + ]: 464 : if (so->searchNulls)
166 : : /* Add a work item to scan the null index entries */
2034 akorotkov@postgresql 167 : 33 : spgAddStartItem(so, true);
168 : :
4418 tgl@sss.pgh.pa.us 169 [ + + ]: 464 : if (so->searchNonNulls)
170 : : /* Add a work item to scan the non-null index entries */
2034 akorotkov@postgresql 171 : 458 : spgAddStartItem(so, false);
172 : :
173 : 464 : MemoryContextSwitchTo(oldCtx);
174 : :
175 [ + + ]: 464 : if (so->numberOfOrderBys > 0)
176 : : {
177 : : /* Must pfree distances to avoid memory leak */
178 : : int i;
179 : :
180 [ + + ]: 45 : for (i = 0; i < so->nPtrs; i++)
181 [ + - ]: 6 : if (so->distances[i])
182 : 6 : pfree(so->distances[i]);
183 : : }
184 : :
4500 tgl@sss.pgh.pa.us 185 [ + + ]: 464 : if (so->want_itup)
186 : : {
187 : : /* Must pfree reconstructed tuples to avoid memory leak */
188 : : int i;
189 : :
190 [ + + ]: 45 : for (i = 0; i < so->nPtrs; i++)
2603 191 : 33 : pfree(so->reconTups[i]);
192 : : }
4500 193 : 464 : so->iPtr = so->nPtrs = 0;
4502 194 : 464 : }
195 : :
196 : : /*
197 : : * Prepare scan keys in SpGistScanOpaque from caller-given scan keys
198 : : *
199 : : * Sets searchNulls, searchNonNulls, numberOfKeys, keyData fields of *so.
200 : : *
201 : : * The point here is to eliminate null-related considerations from what the
202 : : * opclass consistent functions need to deal with. We assume all SPGiST-
203 : : * indexable operators are strict, so any null RHS value makes the scan
204 : : * condition unsatisfiable. We also pull out any IS NULL/IS NOT NULL
205 : : * conditions; their effect is reflected into searchNulls/searchNonNulls.
206 : : */
207 : : static void
4418 208 : 464 : spgPrepareScanKeys(IndexScanDesc scan)
209 : : {
210 : 464 : SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
211 : : bool qual_ok;
212 : : bool haveIsNull;
213 : : bool haveNotNull;
214 : : int nkeys;
215 : : int i;
216 : :
2034 akorotkov@postgresql 217 : 464 : so->numberOfOrderBys = scan->numberOfOrderBys;
218 : 464 : so->orderByData = scan->orderByData;
219 : :
1669 220 [ + + ]: 464 : if (so->numberOfOrderBys <= 0)
221 : 425 : so->numberOfNonNullOrderBys = 0;
222 : : else
223 : : {
224 : 39 : int j = 0;
225 : :
226 : : /*
227 : : * Remove all NULL keys, but remember their offsets in the original
228 : : * array.
229 : : */
230 [ + + ]: 87 : for (i = 0; i < scan->numberOfOrderBys; i++)
231 : : {
232 : 48 : ScanKey skey = &so->orderByData[i];
233 : :
234 [ + + ]: 48 : if (skey->sk_flags & SK_ISNULL)
235 : 3 : so->nonNullOrderByOffsets[i] = -1;
236 : : else
237 : : {
238 [ + + ]: 45 : if (i != j)
239 : 3 : so->orderByData[j] = *skey;
240 : :
241 : 45 : so->nonNullOrderByOffsets[i] = j++;
242 : : }
243 : : }
244 : :
245 : 39 : so->numberOfNonNullOrderBys = j;
246 : : }
247 : :
4418 tgl@sss.pgh.pa.us 248 [ + + ]: 464 : if (scan->numberOfKeys <= 0)
249 : : {
250 : : /* If no quals, whole-index scan is required */
251 : 27 : so->searchNulls = true;
252 : 27 : so->searchNonNulls = true;
253 : 27 : so->numberOfKeys = 0;
254 : 27 : return;
255 : : }
256 : :
257 : : /* Examine the given quals */
258 : 437 : qual_ok = true;
259 : 437 : haveIsNull = haveNotNull = false;
260 : 437 : nkeys = 0;
261 [ + + ]: 876 : for (i = 0; i < scan->numberOfKeys; i++)
262 : : {
263 : 439 : ScanKey skey = &scan->keyData[i];
264 : :
265 [ + + ]: 439 : if (skey->sk_flags & SK_SEARCHNULL)
266 : 6 : haveIsNull = true;
267 [ + + ]: 433 : else if (skey->sk_flags & SK_SEARCHNOTNULL)
268 : 12 : haveNotNull = true;
269 [ - + ]: 421 : else if (skey->sk_flags & SK_ISNULL)
270 : : {
271 : : /* ordinary qual with null argument - unsatisfiable */
4418 tgl@sss.pgh.pa.us 272 :UBC 0 : qual_ok = false;
273 : 0 : break;
274 : : }
275 : : else
276 : : {
277 : : /* ordinary qual, propagate into so->keyData */
4418 tgl@sss.pgh.pa.us 278 :CBC 421 : so->keyData[nkeys++] = *skey;
279 : : /* this effectively creates a not-null requirement */
280 : 421 : haveNotNull = true;
281 : : }
282 : : }
283 : :
284 : : /* IS NULL in combination with something else is unsatisfiable */
285 [ + + - + ]: 437 : if (haveIsNull && haveNotNull)
4418 tgl@sss.pgh.pa.us 286 :UBC 0 : qual_ok = false;
287 : :
288 : : /* Emit results */
4418 tgl@sss.pgh.pa.us 289 [ + - ]:CBC 437 : if (qual_ok)
290 : : {
291 : 437 : so->searchNulls = haveIsNull;
292 : 437 : so->searchNonNulls = haveNotNull;
293 : 437 : so->numberOfKeys = nkeys;
294 : : }
295 : : else
296 : : {
4418 tgl@sss.pgh.pa.us 297 :UBC 0 : so->searchNulls = false;
298 : 0 : so->searchNonNulls = false;
299 : 0 : so->numberOfKeys = 0;
300 : : }
301 : : }
302 : :
303 : : IndexScanDesc
3010 tgl@sss.pgh.pa.us 304 :CBC 452 : spgbeginscan(Relation rel, int keysz, int orderbysz)
305 : : {
306 : : IndexScanDesc scan;
307 : : SpGistScanOpaque so;
308 : : int i;
309 : :
2034 akorotkov@postgresql 310 : 452 : scan = RelationGetIndexScan(rel, keysz, orderbysz);
311 : :
4502 tgl@sss.pgh.pa.us 312 : 452 : so = (SpGistScanOpaque) palloc0(sizeof(SpGistScanOpaqueData));
4418 313 [ + + ]: 452 : if (keysz > 0)
314 : 431 : so->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * keysz);
315 : : else
316 : 21 : so->keyData = NULL;
4502 317 : 452 : initSpGistState(&so->state, scan->indexRelation);
318 : :
319 : 452 : so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
320 : : "SP-GiST search temporary context",
321 : : ALLOCSET_DEFAULT_SIZES);
2218 322 : 452 : so->traversalCxt = AllocSetContextCreate(CurrentMemoryContext,
323 : : "SP-GiST traversal-value context",
324 : : ALLOCSET_DEFAULT_SIZES);
325 : :
326 : : /*
327 : : * Set up reconTupDesc and xs_hitupdesc in case it's an index-only scan,
328 : : * making sure that the key column is shown as being of type attType.
329 : : * (It's rather annoying to do this work when it might be wasted, but for
330 : : * most opclasses we can re-use the index reldesc instead of making one.)
331 : : */
1105 332 : 452 : so->reconTupDesc = scan->xs_hitupdesc =
333 : 452 : getSpGistTupleDesc(rel, &so->state.attType);
334 : :
335 : : /* Allocate various arrays needed for order-by scans */
2034 akorotkov@postgresql 336 [ + + ]: 452 : if (scan->numberOfOrderBys > 0)
337 : : {
338 : : /* This will be filled in spgrescan, but allocate the space here */
1992 tgl@sss.pgh.pa.us 339 : 33 : so->orderByTypes = (Oid *)
340 : 33 : palloc(sizeof(Oid) * scan->numberOfOrderBys);
1669 akorotkov@postgresql 341 : 33 : so->nonNullOrderByOffsets = (int *)
342 : 33 : palloc(sizeof(int) * scan->numberOfOrderBys);
343 : :
344 : : /* These arrays have constant contents, so we can fill them now */
1992 tgl@sss.pgh.pa.us 345 : 33 : so->zeroDistances = (double *)
346 : 33 : palloc(sizeof(double) * scan->numberOfOrderBys);
347 : 33 : so->infDistances = (double *)
348 : 33 : palloc(sizeof(double) * scan->numberOfOrderBys);
349 : :
2034 akorotkov@postgresql 350 [ + + ]: 69 : for (i = 0; i < scan->numberOfOrderBys; i++)
351 : : {
352 : 36 : so->zeroDistances[i] = 0.0;
353 : 36 : so->infDistances[i] = get_float8_infinity();
354 : : }
355 : :
1992 tgl@sss.pgh.pa.us 356 : 33 : scan->xs_orderbyvals = (Datum *)
357 : 33 : palloc0(sizeof(Datum) * scan->numberOfOrderBys);
358 : 33 : scan->xs_orderbynulls = (bool *)
359 : 33 : palloc(sizeof(bool) * scan->numberOfOrderBys);
360 : 33 : memset(scan->xs_orderbynulls, true,
361 : 33 : sizeof(bool) * scan->numberOfOrderBys);
362 : : }
363 : :
2034 akorotkov@postgresql 364 : 452 : fmgr_info_copy(&so->innerConsistentFn,
365 : : index_getprocinfo(rel, 1, SPGIST_INNER_CONSISTENT_PROC),
366 : : CurrentMemoryContext);
367 : :
368 : 452 : fmgr_info_copy(&so->leafConsistentFn,
369 : : index_getprocinfo(rel, 1, SPGIST_LEAF_CONSISTENT_PROC),
370 : : CurrentMemoryContext);
371 : :
372 : 452 : so->indexCollation = rel->rd_indcollation[0];
373 : :
4502 tgl@sss.pgh.pa.us 374 : 452 : scan->opaque = so;
375 : :
3010 376 : 452 : return scan;
377 : : }
378 : :
379 : : void
380 : 464 : spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
381 : : ScanKey orderbys, int norderbys)
382 : : {
4502 383 : 464 : SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
384 : :
385 : : /* copy scankeys into local storage */
386 [ + - + + ]: 464 : if (scankey && scan->numberOfKeys > 0)
387 : 437 : memmove(scan->keyData, scankey,
388 : 437 : scan->numberOfKeys * sizeof(ScanKeyData));
389 : :
390 : : /* initialize order-by data if needed */
2034 akorotkov@postgresql 391 [ + + + + ]: 464 : if (orderbys && scan->numberOfOrderBys > 0)
392 : : {
393 : : int i;
394 : :
395 : 39 : memmove(scan->orderByData, orderbys,
396 : 39 : scan->numberOfOrderBys * sizeof(ScanKeyData));
397 : :
398 [ + + ]: 87 : for (i = 0; i < scan->numberOfOrderBys; i++)
399 : : {
400 : 48 : ScanKey skey = &scan->orderByData[i];
401 : :
402 : : /*
403 : : * Look up the datatype returned by the original ordering
404 : : * operator. SP-GiST always uses a float8 for the distance
405 : : * function, but the ordering operator could be anything else.
406 : : *
407 : : * XXX: The distance function is only allowed to be lossy if the
408 : : * ordering operator's result type is float4 or float8. Otherwise
409 : : * we don't know how to return the distance to the executor. But
410 : : * we cannot check that here, as we won't know if the distance
411 : : * function is lossy until it returns *recheck = true for the
412 : : * first time.
413 : : */
414 : 48 : so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
415 : : }
416 : : }
417 : :
418 : : /* preprocess scankeys, set up the representation in *so */
4418 tgl@sss.pgh.pa.us 419 : 464 : spgPrepareScanKeys(scan);
420 : :
421 : : /* set up starting queue entries */
4502 422 : 464 : resetSpGistScanOpaque(so);
423 : :
424 : : /* count an indexscan for stats */
961 425 [ + + + - : 464 : pgstat_count_index_scan(scan->indexRelation);
+ - ]
4502 426 : 464 : }
427 : :
428 : : void
3010 429 : 452 : spgendscan(IndexScanDesc scan)
430 : : {
4502 431 : 452 : SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
432 : :
433 : 452 : MemoryContextDelete(so->tempCxt);
2218 434 : 452 : MemoryContextDelete(so->traversalCxt);
435 : :
1992 436 [ + + ]: 452 : if (so->keyData)
437 : 431 : pfree(so->keyData);
438 : :
1105 439 [ + - ]: 452 : if (so->state.leafTupDesc &&
440 [ + + ]: 452 : so->state.leafTupDesc != RelationGetDescr(so->state.index))
441 : 4 : FreeTupleDesc(so->state.leafTupDesc);
442 : :
1992 443 [ + - ]: 452 : if (so->state.deadTupleStorage)
444 : 452 : pfree(so->state.deadTupleStorage);
445 : :
2034 akorotkov@postgresql 446 [ + + ]: 452 : if (scan->numberOfOrderBys > 0)
447 : : {
1992 tgl@sss.pgh.pa.us 448 : 33 : pfree(so->orderByTypes);
1669 akorotkov@postgresql 449 : 33 : pfree(so->nonNullOrderByOffsets);
2034 450 : 33 : pfree(so->zeroDistances);
451 : 33 : pfree(so->infDistances);
1992 tgl@sss.pgh.pa.us 452 : 33 : pfree(scan->xs_orderbyvals);
453 : 33 : pfree(scan->xs_orderbynulls);
454 : : }
455 : :
456 : 452 : pfree(so);
2034 akorotkov@postgresql 457 : 452 : }
458 : :
459 : : /*
460 : : * Leaf SpGistSearchItem constructor, called in queue context
461 : : */
462 : : static SpGistSearchItem *
1105 tgl@sss.pgh.pa.us 463 : 182946 : spgNewHeapItem(SpGistScanOpaque so, int level, SpGistLeafTuple leafTuple,
464 : : Datum leafValue, bool recheck, bool recheckDistances,
465 : : bool isnull, double *distances)
466 : : {
2034 akorotkov@postgresql 467 : 182946 : SpGistSearchItem *item = spgAllocSearchItem(so, isnull, distances);
468 : :
469 : 182946 : item->level = level;
1105 tgl@sss.pgh.pa.us 470 : 182946 : item->heapPtr = leafTuple->heapPtr;
471 : :
472 : : /*
473 : : * If we need the reconstructed value, copy it to queue cxt out of tmp
474 : : * cxt. Caution: the leaf_consistent method may not have supplied a value
475 : : * if we didn't ask it to, and mildly-broken methods might supply one of
476 : : * the wrong type. The correct leafValue type is attType not leafType.
477 : : */
1106 478 [ + + ]: 182946 : if (so->want_itup)
479 : : {
480 [ + + ]: 139668 : item->value = isnull ? (Datum) 0 :
481 : 139650 : datumCopy(leafValue, so->state.attType.attbyval,
482 : 139650 : so->state.attType.attlen);
483 : :
484 : : /*
485 : : * If we're going to need to reconstruct INCLUDE attributes, store the
486 : : * whole leaf tuple so we can get the INCLUDE attributes out of it.
487 : : */
1105 488 [ + + ]: 139668 : if (so->state.leafTupDesc->natts > 1)
489 : : {
490 : 93 : item->leafTuple = palloc(leafTuple->size);
491 : 93 : memcpy(item->leafTuple, leafTuple, leafTuple->size);
492 : : }
493 : : else
494 : 139575 : item->leafTuple = NULL;
495 : : }
496 : : else
497 : : {
1106 498 : 43278 : item->value = (Datum) 0;
1105 499 : 43278 : item->leafTuple = NULL;
500 : : }
2034 akorotkov@postgresql 501 : 182946 : item->traversalValue = NULL;
502 : 182946 : item->isLeaf = true;
503 : 182946 : item->recheck = recheck;
504 : 182946 : item->recheckDistances = recheckDistances;
505 : :
506 : 182946 : return item;
507 : : }
508 : :
509 : : /*
510 : : * Test whether a leaf tuple satisfies all the scan keys
511 : : *
512 : : * *reportedSome is set to true if:
513 : : * the scan is not ordered AND the item satisfies the scankeys
514 : : */
515 : : static bool
1789 tgl@sss.pgh.pa.us 516 : 1272495 : spgLeafTest(SpGistScanOpaque so, SpGistSearchItem *item,
517 : : SpGistLeafTuple leafTuple, bool isnull,
518 : : bool *reportedSome, storeRes_func storeRes)
519 : : {
520 : : Datum leafValue;
521 : : double *distances;
522 : : bool result;
523 : : bool recheck;
524 : : bool recheckDistances;
525 : :
4417 526 [ + + ]: 1272495 : if (isnull)
527 : : {
528 : : /* Should not have arrived on a nulls page unless nulls are wanted */
529 [ - + ]: 60 : Assert(so->searchNulls);
2034 akorotkov@postgresql 530 : 60 : leafValue = (Datum) 0;
531 : 60 : distances = NULL;
532 : 60 : recheck = false;
533 : 60 : recheckDistances = false;
534 : 60 : result = true;
535 : : }
536 : : else
537 : : {
538 : : spgLeafConsistentIn in;
539 : : spgLeafConsistentOut out;
540 : :
541 : : /* use temp context for calling leaf_consistent */
542 : 1272435 : MemoryContext oldCxt = MemoryContextSwitchTo(so->tempCxt);
543 : :
544 : 1272435 : in.scankeys = so->keyData;
545 : 1272435 : in.nkeys = so->numberOfKeys;
546 : 1272435 : in.orderbys = so->orderByData;
1669 547 : 1272435 : in.norderbys = so->numberOfNonNullOrderBys;
1106 tgl@sss.pgh.pa.us 548 [ - + ]: 1272435 : Assert(!item->isLeaf); /* else reconstructedValue would be wrong type */
2034 akorotkov@postgresql 549 : 1272435 : in.reconstructedValue = item->value;
550 : 1272435 : in.traversalValue = item->traversalValue;
551 : 1272435 : in.level = item->level;
552 : 1272435 : in.returnData = so->want_itup;
553 : 1272435 : in.leafDatum = SGLTDATUM(leafTuple, &so->state);
554 : :
555 : 1272435 : out.leafValue = (Datum) 0;
556 : 1272435 : out.recheck = false;
557 : 1272435 : out.distances = NULL;
558 : 1272435 : out.recheckDistances = false;
559 : :
560 : 1272435 : result = DatumGetBool(FunctionCall2Coll(&so->leafConsistentFn,
561 : : so->indexCollation,
562 : : PointerGetDatum(&in),
563 : : PointerGetDatum(&out)));
564 : 1272435 : recheck = out.recheck;
565 : 1272435 : recheckDistances = out.recheckDistances;
566 : 1272435 : leafValue = out.leafValue;
567 : 1272435 : distances = out.distances;
568 : :
569 : 1272435 : MemoryContextSwitchTo(oldCxt);
570 : : }
571 : :
572 [ + + ]: 1272495 : if (result)
573 : : {
574 : : /* item passes the scankeys */
1669 575 [ + + ]: 1030613 : if (so->numberOfNonNullOrderBys > 0)
576 : : {
577 : : /* the scan is ordered -> add the item to the queue */
2034 578 : 182946 : MemoryContext oldCxt = MemoryContextSwitchTo(so->traversalCxt);
579 : 182946 : SpGistSearchItem *heapItem = spgNewHeapItem(so, item->level,
580 : : leafTuple,
581 : : leafValue,
582 : : recheck,
583 : : recheckDistances,
584 : : isnull,
585 : : distances);
586 : :
587 : 182946 : spgAddSearchItemToQueue(so, heapItem);
588 : :
589 : 182946 : MemoryContextSwitchTo(oldCxt);
590 : : }
591 : : else
592 : : {
593 : : /* non-ordered scan, so report the item right away */
594 [ - + ]: 847667 : Assert(!recheckDistances);
595 : 847667 : storeRes(so, &leafTuple->heapPtr, leafValue, isnull,
596 : : leafTuple, recheck, false, NULL);
597 : 847667 : *reportedSome = true;
598 : : }
599 : : }
600 : :
601 : 1272495 : return result;
602 : : }
603 : :
604 : : /* A bundle initializer for inner_consistent methods */
605 : : static void
606 : 12226 : spgInitInnerConsistentIn(spgInnerConsistentIn *in,
607 : : SpGistScanOpaque so,
608 : : SpGistSearchItem *item,
609 : : SpGistInnerTuple innerTuple)
610 : : {
611 : 12226 : in->scankeys = so->keyData;
612 : 12226 : in->orderbys = so->orderByData;
613 : 12226 : in->nkeys = so->numberOfKeys;
1669 614 : 12226 : in->norderbys = so->numberOfNonNullOrderBys;
1106 tgl@sss.pgh.pa.us 615 [ - + ]: 12226 : Assert(!item->isLeaf); /* else reconstructedValue would be wrong type */
2034 akorotkov@postgresql 616 : 12226 : in->reconstructedValue = item->value;
617 : 12226 : in->traversalMemoryContext = so->traversalCxt;
618 : 12226 : in->traversalValue = item->traversalValue;
619 : 12226 : in->level = item->level;
620 : 12226 : in->returnData = so->want_itup;
621 : 12226 : in->allTheSame = innerTuple->allTheSame;
622 : 12226 : in->hasPrefix = (innerTuple->prefixSize > 0);
623 [ + + + + ]: 12226 : in->prefixDatum = SGITDATUM(innerTuple, &so->state);
624 : 12226 : in->nNodes = innerTuple->nNodes;
625 : 12226 : in->nodeLabels = spgExtractNodeLabels(&so->state, innerTuple);
626 : 12226 : }
627 : :
628 : : static SpGistSearchItem *
629 : 47013 : spgMakeInnerItem(SpGistScanOpaque so,
630 : : SpGistSearchItem *parentItem,
631 : : SpGistNodeTuple tuple,
632 : : spgInnerConsistentOut *out, int i, bool isnull,
633 : : double *distances)
634 : : {
635 : 47013 : SpGistSearchItem *item = spgAllocSearchItem(so, isnull, distances);
636 : :
637 : 47013 : item->heapPtr = tuple->t_tid;
638 : 115072 : item->level = out->levelAdds ? parentItem->level + out->levelAdds[i]
639 [ + + ]: 47013 : : parentItem->level;
640 : :
641 : : /* Must copy value out of temp context */
642 : : /* (recall that reconstructed values are of type leafType) */
643 : 94026 : item->value = out->reconstructedValues
644 : 6232 : ? datumCopy(out->reconstructedValues[i],
645 : 6232 : so->state.attLeafType.attbyval,
646 : 6232 : so->state.attLeafType.attlen)
647 [ + + ]: 47013 : : (Datum) 0;
648 : :
1105 tgl@sss.pgh.pa.us 649 : 47013 : item->leafTuple = NULL;
650 : :
651 : : /*
652 : : * Elements of out.traversalValues should be allocated in
653 : : * in.traversalMemoryContext, which is actually a long lived context of
654 : : * index scan.
655 : : */
2034 akorotkov@postgresql 656 : 47013 : item->traversalValue =
657 [ + + ]: 47013 : out->traversalValues ? out->traversalValues[i] : NULL;
658 : :
659 : 47013 : item->isLeaf = false;
660 : 47013 : item->recheck = false;
661 : 47013 : item->recheckDistances = false;
662 : :
663 : 47013 : return item;
664 : : }
665 : :
666 : : static void
1789 tgl@sss.pgh.pa.us 667 : 12226 : spgInnerTest(SpGistScanOpaque so, SpGistSearchItem *item,
668 : : SpGistInnerTuple innerTuple, bool isnull)
669 : : {
2034 akorotkov@postgresql 670 : 12226 : MemoryContext oldCxt = MemoryContextSwitchTo(so->tempCxt);
671 : : spgInnerConsistentOut out;
672 : 12226 : int nNodes = innerTuple->nNodes;
673 : : int i;
674 : :
675 : 12226 : memset(&out, 0, sizeof(out));
676 : :
677 [ + - ]: 12226 : if (!isnull)
678 : : {
679 : : spgInnerConsistentIn in;
680 : :
681 : 12226 : spgInitInnerConsistentIn(&in, so, item, innerTuple);
682 : :
683 : : /* use user-defined inner consistent method */
684 : 12226 : FunctionCall2Coll(&so->innerConsistentFn,
685 : : so->indexCollation,
686 : : PointerGetDatum(&in),
687 : : PointerGetDatum(&out));
688 : : }
689 : : else
690 : : {
691 : : /* force all children to be visited */
2034 akorotkov@postgresql 692 :UBC 0 : out.nNodes = nNodes;
693 : 0 : out.nodeNumbers = (int *) palloc(sizeof(int) * nNodes);
694 [ # # ]: 0 : for (i = 0; i < nNodes; i++)
695 : 0 : out.nodeNumbers[i] = i;
696 : : }
697 : :
698 : : /* If allTheSame, they should all or none of them match */
2034 akorotkov@postgresql 699 [ + + + - :CBC 12226 : if (innerTuple->allTheSame && out.nNodes != 0 && out.nNodes != nNodes)
- + ]
2034 akorotkov@postgresql 700 [ # # ]:UBC 0 : elog(ERROR, "inconsistent inner_consistent results for allTheSame inner tuple");
701 : :
2034 akorotkov@postgresql 702 [ + - ]:CBC 12226 : if (out.nNodes)
703 : : {
704 : : /* collect node pointers */
705 : : SpGistNodeTuple node;
1536 alvherre@alvh.no-ip. 706 : 12226 : SpGistNodeTuple *nodes = (SpGistNodeTuple *) palloc(sizeof(SpGistNodeTuple) * nNodes);
707 : :
2034 akorotkov@postgresql 708 [ + + ]: 113127 : SGITITERATE(innerTuple, i, node)
709 : : {
710 : 100901 : nodes[i] = node;
711 : : }
712 : :
713 : 12226 : MemoryContextSwitchTo(so->traversalCxt);
714 : :
715 [ + + ]: 92764 : for (i = 0; i < out.nNodes; i++)
716 : : {
717 : 80538 : int nodeN = out.nodeNumbers[i];
718 : : SpGistSearchItem *innerItem;
719 : : double *distances;
720 : :
721 [ + - - + ]: 80538 : Assert(nodeN >= 0 && nodeN < nNodes);
722 : :
723 : 80538 : node = nodes[nodeN];
724 : :
725 [ + + ]: 80538 : if (!ItemPointerIsValid(&node->t_tid))
726 : 33525 : continue;
727 : :
728 : : /*
729 : : * Use infinity distances if innerConsistentFn() failed to return
730 : : * them or if is a NULL item (their distances are really unused).
731 : : */
732 [ + + ]: 47013 : distances = out.distances ? out.distances[i] : so->infDistances;
733 : :
734 : 47013 : innerItem = spgMakeInnerItem(so, item, node, &out, i, isnull,
735 : : distances);
736 : :
737 : 47013 : spgAddSearchItemToQueue(so, innerItem);
738 : : }
739 : : }
740 : :
741 : 12226 : MemoryContextSwitchTo(oldCxt);
742 : 12226 : }
743 : :
744 : : /* Returns a next item in an (ordered) scan or null if the index is exhausted */
745 : : static SpGistSearchItem *
746 : 229519 : spgGetNextQueueItem(SpGistScanOpaque so)
747 : : {
748 [ + + ]: 229519 : if (pairingheap_is_empty(so->scanQueue))
749 : 443 : return NULL; /* Done when both heaps are empty */
750 : :
751 : : /* Return item; caller is responsible to pfree it */
752 : 229076 : return (SpGistSearchItem *) pairingheap_remove_first(so->scanQueue);
753 : : }
754 : :
755 : : enum SpGistSpecialOffsetNumbers
756 : : {
757 : : SpGistBreakOffsetNumber = InvalidOffsetNumber,
758 : : SpGistRedirectOffsetNumber = MaxOffsetNumber + 1,
759 : : SpGistErrorOffsetNumber = MaxOffsetNumber + 2,
760 : : };
761 : :
762 : : static OffsetNumber
763 : 1272495 : spgTestLeafTuple(SpGistScanOpaque so,
764 : : SpGistSearchItem *item,
765 : : Page page, OffsetNumber offset,
766 : : bool isnull, bool isroot,
767 : : bool *reportedSome,
768 : : storeRes_func storeRes)
769 : : {
770 : : SpGistLeafTuple leafTuple = (SpGistLeafTuple)
331 tgl@sss.pgh.pa.us 771 : 1272495 : PageGetItem(page, PageGetItemId(page, offset));
772 : :
2034 akorotkov@postgresql 773 [ - + ]: 1272495 : if (leafTuple->tupstate != SPGIST_LIVE)
774 : : {
2034 akorotkov@postgresql 775 [ # # ]:UBC 0 : if (!isroot) /* all tuples on root should be live */
776 : : {
777 [ # # ]: 0 : if (leafTuple->tupstate == SPGIST_REDIRECT)
778 : : {
779 : : /* redirection tuple should be first in chain */
780 [ # # ]: 0 : Assert(offset == ItemPointerGetOffsetNumber(&item->heapPtr));
781 : : /* transfer attention to redirect point */
782 : 0 : item->heapPtr = ((SpGistDeadTuple) leafTuple)->pointer;
783 [ # # ]: 0 : Assert(ItemPointerGetBlockNumber(&item->heapPtr) != SPGIST_METAPAGE_BLKNO);
784 : 0 : return SpGistRedirectOffsetNumber;
785 : : }
786 : :
787 [ # # ]: 0 : if (leafTuple->tupstate == SPGIST_DEAD)
788 : : {
789 : : /* dead tuple should be first in chain */
790 [ # # ]: 0 : Assert(offset == ItemPointerGetOffsetNumber(&item->heapPtr));
791 : : /* No live entries on this page */
1105 tgl@sss.pgh.pa.us 792 [ # # ]: 0 : Assert(SGLT_GET_NEXTOFFSET(leafTuple) == InvalidOffsetNumber);
2034 akorotkov@postgresql 793 : 0 : return SpGistBreakOffsetNumber;
794 : : }
795 : : }
796 : :
797 : : /* We should not arrive at a placeholder */
798 [ # # ]: 0 : elog(ERROR, "unexpected SPGiST tuple state: %d", leafTuple->tupstate);
799 : : return SpGistErrorOffsetNumber;
800 : : }
801 : :
2034 akorotkov@postgresql 802 [ - + ]:CBC 1272495 : Assert(ItemPointerIsValid(&leafTuple->heapPtr));
803 : :
804 : 1272495 : spgLeafTest(so, item, leafTuple, isnull, reportedSome, storeRes);
805 : :
1105 tgl@sss.pgh.pa.us 806 : 1272495 : return SGLT_GET_NEXTOFFSET(leafTuple);
807 : : }
808 : :
809 : : /*
810 : : * Walk the tree and report all tuples passing the scan quals to the storeRes
811 : : * subroutine.
812 : : *
813 : : * If scanWholeIndex is true, we'll do just that. If not, we'll stop at the
814 : : * next page boundary once we have reported at least one tuple.
815 : : */
816 : : static void
4502 817 : 188610 : spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
818 : : storeRes_func storeRes)
819 : : {
820 : 188610 : Buffer buffer = InvalidBuffer;
821 : 188610 : bool reportedSome = false;
822 : :
823 [ + + + + ]: 417686 : while (scanWholeIndex || !reportedSome)
824 : : {
2034 akorotkov@postgresql 825 : 229519 : SpGistSearchItem *item = spgGetNextQueueItem(so);
826 : :
827 [ + + ]: 229519 : if (item == NULL)
828 : 443 : break; /* No more items in queue -> done */
829 : :
4502 tgl@sss.pgh.pa.us 830 :UBC 0 : redirect:
831 : : /* Check for interrupts, just in case of infinite loop */
4502 tgl@sss.pgh.pa.us 832 [ - + ]:CBC 229076 : CHECK_FOR_INTERRUPTS();
833 : :
2034 akorotkov@postgresql 834 [ + + ]: 229076 : if (item->isLeaf)
835 : : {
836 : : /* We store heap items in the queue only in case of ordered search */
1669 837 [ - + ]: 181677 : Assert(so->numberOfNonNullOrderBys > 0);
2034 838 : 181677 : storeRes(so, &item->heapPtr, item->value, item->isNull,
1105 tgl@sss.pgh.pa.us 839 : 181677 : item->leafTuple, item->recheck,
840 : 181677 : item->recheckDistances, item->distances);
2034 akorotkov@postgresql 841 : 181677 : reportedSome = true;
842 : : }
843 : : else
844 : : {
845 : 47399 : BlockNumber blkno = ItemPointerGetBlockNumber(&item->heapPtr);
846 : 47399 : OffsetNumber offset = ItemPointerGetOffsetNumber(&item->heapPtr);
847 : : Page page;
848 : : bool isnull;
849 : :
850 [ + + ]: 47399 : if (buffer == InvalidBuffer)
851 : : {
852 : 9874 : buffer = ReadBuffer(index, blkno);
853 : 9874 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
854 : : }
855 [ + + ]: 37525 : else if (blkno != BufferGetBlockNumber(buffer))
856 : : {
857 : 26014 : UnlockReleaseBuffer(buffer);
858 : 26014 : buffer = ReadBuffer(index, blkno);
859 : 26014 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
860 : : }
861 : :
862 : : /* else new pointer points to the same page, no work needed */
863 : :
864 : 47399 : page = BufferGetPage(buffer);
865 : :
866 : 47399 : isnull = SpGistPageStoresNulls(page) ? true : false;
867 : :
868 [ + + ]: 47399 : if (SpGistPageIsLeaf(page))
869 : : {
870 : : /* Page is a leaf - that is, all it's tuples are heap items */
871 : 35173 : OffsetNumber max = PageGetMaxOffsetNumber(page);
872 : :
873 [ + + + + ]: 35173 : if (SpGistBlockIsRoot(blkno))
874 : : {
875 : : /* When root is a leaf, examine all its tuples */
876 [ + + ]: 3063 : for (offset = FirstOffsetNumber; offset <= max; offset++)
877 : 2964 : (void) spgTestLeafTuple(so, item, page, offset,
878 : : isnull, true,
879 : : &reportedSome, storeRes);
880 : : }
881 : : else
882 : : {
883 : : /* Normal case: just examine the chain we arrived at */
884 [ + + ]: 1304605 : while (offset != InvalidOffsetNumber)
885 : : {
886 [ + - - + ]: 1269531 : Assert(offset >= FirstOffsetNumber && offset <= max);
887 : 1269531 : offset = spgTestLeafTuple(so, item, page, offset,
888 : : isnull, false,
889 : : &reportedSome, storeRes);
890 [ - + ]: 1269531 : if (offset == SpGistRedirectOffsetNumber)
4502 tgl@sss.pgh.pa.us 891 :UBC 0 : goto redirect;
892 : : }
893 : : }
894 : : }
895 : : else /* page is inner */
896 : : {
897 : : SpGistInnerTuple innerTuple = (SpGistInnerTuple)
331 tgl@sss.pgh.pa.us 898 :CBC 12226 : PageGetItem(page, PageGetItemId(page, offset));
899 : :
2034 akorotkov@postgresql 900 [ - + ]: 12226 : if (innerTuple->tupstate != SPGIST_LIVE)
901 : : {
2034 akorotkov@postgresql 902 [ # # ]:UBC 0 : if (innerTuple->tupstate == SPGIST_REDIRECT)
903 : : {
904 : : /* transfer attention to redirect point */
905 : 0 : item->heapPtr = ((SpGistDeadTuple) innerTuple)->pointer;
906 [ # # ]: 0 : Assert(ItemPointerGetBlockNumber(&item->heapPtr) !=
907 : : SPGIST_METAPAGE_BLKNO);
908 : 0 : goto redirect;
909 : : }
910 [ # # ]: 0 : elog(ERROR, "unexpected SPGiST tuple state: %d",
911 : : innerTuple->tupstate);
912 : : }
913 : :
2034 akorotkov@postgresql 914 :CBC 12226 : spgInnerTest(so, item, innerTuple, isnull);
915 : : }
916 : : }
917 : :
918 : : /* done with this scan item */
919 : 229076 : spgFreeSearchItem(so, item);
920 : : /* clear temp context before proceeding to the next one */
4502 tgl@sss.pgh.pa.us 921 : 229076 : MemoryContextReset(so->tempCxt);
922 : : }
923 : :
924 [ + + ]: 188610 : if (buffer != InvalidBuffer)
925 : 9874 : UnlockReleaseBuffer(buffer);
926 : 188610 : }
927 : :
928 : :
929 : : /* storeRes subroutine for getbitmap case */
930 : : static void
4500 931 : 526107 : storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
932 : : Datum leafValue, bool isnull,
933 : : SpGistLeafTuple leafTuple, bool recheck,
934 : : bool recheckDistances, double *distances)
935 : : {
2034 akorotkov@postgresql 936 [ + - - + ]: 526107 : Assert(!recheckDistances && !distances);
4502 tgl@sss.pgh.pa.us 937 : 526107 : tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
938 : 526107 : so->ntids++;
939 : 526107 : }
940 : :
941 : : int64
3010 942 : 174 : spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
943 : : {
4502 944 : 174 : SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
945 : :
946 : : /* Copy want_itup to *so so we don't need to pass it around separately */
4500 947 : 174 : so->want_itup = false;
948 : :
4502 949 : 174 : so->tbm = tbm;
950 : 174 : so->ntids = 0;
951 : :
219 tmunro@postgresql.or 952 :GNC 174 : spgWalk(scan->indexRelation, so, true, storeBitmap);
953 : :
3010 tgl@sss.pgh.pa.us 954 :CBC 174 : return so->ntids;
955 : : }
956 : :
957 : : /* storeRes subroutine for gettuple case */
958 : : static void
4500 959 : 503237 : storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
960 : : Datum leafValue, bool isnull,
961 : : SpGistLeafTuple leafTuple, bool recheck,
962 : : bool recheckDistances, double *nonNullDistances)
963 : : {
4502 964 [ - + ]: 503237 : Assert(so->nPtrs < MaxIndexTuplesPerPage);
965 : 503237 : so->heapPtrs[so->nPtrs] = *heapPtr;
966 : 503237 : so->recheck[so->nPtrs] = recheck;
2034 akorotkov@postgresql 967 : 503237 : so->recheckDistances[so->nPtrs] = recheckDistances;
968 : :
969 [ + + ]: 503237 : if (so->numberOfOrderBys > 0)
970 : : {
1669 971 [ + + - + ]: 181677 : if (isnull || so->numberOfNonNullOrderBys <= 0)
2034 972 : 24 : so->distances[so->nPtrs] = NULL;
973 : : else
974 : : {
975 : : IndexOrderByDistance *distances =
331 tgl@sss.pgh.pa.us 976 : 181653 : palloc(sizeof(distances[0]) * so->numberOfOrderBys);
977 : : int i;
978 : :
1669 akorotkov@postgresql 979 [ + + ]: 363315 : for (i = 0; i < so->numberOfOrderBys; i++)
980 : : {
981 : 181662 : int offset = so->nonNullOrderByOffsets[i];
982 : :
983 [ + + ]: 181662 : if (offset >= 0)
984 : : {
985 : : /* Copy non-NULL distance value */
986 : 181659 : distances[i].value = nonNullDistances[offset];
987 : 181659 : distances[i].isnull = false;
988 : : }
989 : : else
990 : : {
991 : : /* Set distance's NULL flag. */
992 : 3 : distances[i].value = 0.0;
993 : 3 : distances[i].isnull = true;
994 : : }
995 : : }
996 : :
997 : 181653 : so->distances[so->nPtrs] = distances;
998 : : }
999 : : }
1000 : :
4500 tgl@sss.pgh.pa.us 1001 [ + + ]: 503237 : if (so->want_itup)
1002 : : {
1003 : : /*
1004 : : * Reconstruct index data. We have to copy the datum out of the temp
1005 : : * context anyway, so we may as well create the tuple here.
1006 : : */
1007 : : Datum leafDatums[INDEX_MAX_KEYS];
1008 : : bool leafIsnulls[INDEX_MAX_KEYS];
1009 : :
1010 : : /* We only need to deform the old tuple if it has INCLUDE attributes */
1105 1011 [ + + ]: 459719 : if (so->state.leafTupDesc->natts > 1)
1012 : 56 : spgDeformLeafTuple(leafTuple, so->state.leafTupDesc,
1013 : : leafDatums, leafIsnulls, isnull);
1014 : :
1015 : 459719 : leafDatums[spgKeyColumn] = leafValue;
1016 : 459719 : leafIsnulls[spgKeyColumn] = isnull;
1017 : :
1018 : 459719 : so->reconTups[so->nPtrs] = heap_form_tuple(so->reconTupDesc,
1019 : : leafDatums,
1020 : : leafIsnulls);
1021 : : }
4502 1022 : 503237 : so->nPtrs++;
1023 : 503237 : }
1024 : :
1025 : : bool
3010 1026 : 503458 : spggettuple(IndexScanDesc scan, ScanDirection dir)
1027 : : {
4502 1028 : 503458 : SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
1029 : :
1030 [ - + ]: 503458 : if (dir != ForwardScanDirection)
4502 tgl@sss.pgh.pa.us 1031 [ # # ]:UBC 0 : elog(ERROR, "SP-GiST only supports forward scan direction");
1032 : :
1033 : : /* Copy want_itup to *so so we don't need to pass it around separately */
4500 tgl@sss.pgh.pa.us 1034 :CBC 503458 : so->want_itup = scan->xs_want_itup;
1035 : :
1036 : : for (;;)
1037 : : {
4502 1038 [ + + ]: 691625 : if (so->iPtr < so->nPtrs)
1039 : : {
1040 : : /* continuing to return reported tuples */
1861 andres@anarazel.de 1041 : 503189 : scan->xs_heaptid = so->heapPtrs[so->iPtr];
4502 tgl@sss.pgh.pa.us 1042 : 503189 : scan->xs_recheck = so->recheck[so->iPtr];
2603 1043 : 503189 : scan->xs_hitup = so->reconTups[so->iPtr];
1044 : :
2034 akorotkov@postgresql 1045 [ + + ]: 503189 : if (so->numberOfOrderBys > 0)
1046 : 181677 : index_store_float8_orderby_distances(scan, so->orderByTypes,
1047 : 181677 : so->distances[so->iPtr],
1048 : 181677 : so->recheckDistances[so->iPtr]);
4502 tgl@sss.pgh.pa.us 1049 : 503189 : so->iPtr++;
3010 1050 : 503189 : return true;
1051 : : }
1052 : :
2034 akorotkov@postgresql 1053 [ + + ]: 188436 : if (so->numberOfOrderBys > 0)
1054 : : {
1055 : : /* Must pfree distances to avoid memory leak */
1056 : : int i;
1057 : :
1058 [ + + ]: 363369 : for (i = 0; i < so->nPtrs; i++)
1059 [ + + ]: 181665 : if (so->distances[i])
1060 : 181641 : pfree(so->distances[i]);
1061 : : }
1062 : :
4500 tgl@sss.pgh.pa.us 1063 [ + + ]: 188436 : if (so->want_itup)
1064 : : {
1065 : : /* Must pfree reconstructed tuples to avoid memory leak */
1066 : : int i;
1067 : :
1068 [ + + ]: 604730 : for (i = 0; i < so->nPtrs; i++)
2603 1069 : 459650 : pfree(so->reconTups[i]);
1070 : : }
4502 1071 : 188436 : so->iPtr = so->nPtrs = 0;
1072 : :
219 tmunro@postgresql.or 1073 :GNC 188436 : spgWalk(scan->indexRelation, so, false, storeGettuple);
1074 : :
4502 tgl@sss.pgh.pa.us 1075 [ + + ]:CBC 188436 : if (so->nPtrs == 0)
1076 : 269 : break; /* must have completed scan */
1077 : : }
1078 : :
3010 1079 : 269 : return false;
1080 : : }
1081 : :
1082 : : bool
1083 : 927 : spgcanreturn(Relation index, int attno)
1084 : : {
1085 : : SpGistCache *cache;
1086 : :
1087 : : /* INCLUDE attributes can always be fetched for index-only scans */
1105 1088 [ + + ]: 927 : if (attno > 1)
1089 : 14 : return true;
1090 : :
1091 : : /* We can do it if the opclass config function says so */
4500 1092 : 913 : cache = spgGetCache(index);
1093 : :
3010 1094 : 913 : return cache->config.canReturnData;
1095 : : }
|