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