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