PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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
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 :
90 so->state.attLeafType.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
113
114static SpGistSearchItem *
115spgAllocSearchItem(SpGistScanOpaque so, bool isnull, double *distances)
116{
117 /* allocate distance array only for non-NULL items */
118 SpGistSearchItem *item =
119 palloc(SizeOfSpGistSearchItem(isnull ? 0 : so->numberOfNonNullOrderBys));
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{
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
148}
149
150/*
151 * Initialize queue to search the root page, resetting
152 * any previously active scan
153 */
154static void
156{
158
159 MemoryContextReset(so->traversalCxt);
160
161 oldCtx = MemoryContextSwitchTo(so->traversalCxt);
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
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
218 so->numberOfOrderBys = scan->numberOfOrderBys;
219 so->orderByData = scan->orderByData;
220
221 if (so->numberOfOrderBys <= 0)
222 so->numberOfNonNullOrderBys = 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
246 so->numberOfNonNullOrderBys = j;
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;
318 initSpGistState(&so->state, scan->indexRelation);
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 =
334 getSpGistTupleDesc(rel, &so->state.attType);
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 */
340 so->orderByTypes = palloc_array(Oid, scan->numberOfOrderBys);
341 so->nonNullOrderByOffsets = palloc_array(int, scan->numberOfOrderBys);
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;
350 so->infDistances[i] = get_float8_infinity();
351 }
352
354 scan->xs_orderbynulls = palloc_array(bool, scan->numberOfOrderBys);
355 memset(scan->xs_orderbynulls, true,
356 sizeof(bool) * scan->numberOfOrderBys);
357 }
358
359 fmgr_info_copy(&so->innerConsistentFn,
362
363 fmgr_info_copy(&so->leafConsistentFn,
366
367 so->indexCollation = rel->rd_indcollation[0];
368
369 scan->opaque = so;
370
371 return scan;
372}
373
374void
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 */
407 so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
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
428 MemoryContextDelete(so->tempCxt);
429 MemoryContextDelete(so->traversalCxt);
430
431 if (so->keyData)
432 pfree(so->keyData);
433
434 if (so->state.leafTupDesc &&
435 so->state.leafTupDesc != RelationGetDescr(so->state.index))
436 FreeTupleDesc(so->state.leafTupDesc);
437
438 if (so->state.deadTupleStorage)
439 pfree(so->state.deadTupleStorage);
440
441 if (scan->numberOfOrderBys > 0)
442 {
443 pfree(so->orderByTypes);
444 pfree(so->nonNullOrderByOffsets);
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,
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;
542 in.norderbys = so->numberOfNonNullOrderBys;
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
555 result = DatumGetBool(FunctionCall2Coll(&so->leafConsistentFn,
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
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 */
575 leafTuple,
576 leafValue,
577 recheck,
578 recheckDistances,
579 isnull,
580 distances);
581
583
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,
605{
606 in->scankeys = so->keyData;
607 in->orderbys = so->orderByData;
608 in->nkeys = so->numberOfKeys;
609 in->norderbys = so->numberOfNonNullOrderBys;
610 Assert(!item->isLeaf); /* else reconstructedValue would be wrong type */
611 in->reconstructedValue = item->value;
612 in->traversalMemoryContext = so->traversalCxt;
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;
621}
622
623static SpGistSearchItem *
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
640 so->state.attLeafType.attbyval,
641 so->state.attLeafType.attlen)
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
677
678 /* use user-defined inner consistent method */
679 FunctionCall2Coll(&so->innerConsistentFn,
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
708 MemoryContextSwitchTo(so->traversalCxt);
709
710 for (i = 0; i < out.nNodes; i++)
711 {
712 int nodeN = out.nodeNumbers[i];
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
733 }
734 }
735
737}
738
739/* Returns a next item in an (ordered) scan or null if the index is exhausted */
740static SpGistSearchItem *
742{
743 if (pairingheap_is_empty(so->scanQueue))
744 return NULL; /* Done when both heaps are empty */
745
746 /* Return item; caller is responsible to pfree it */
747 return (SpGistSearchItem *) pairingheap_remove_first(so->scanQueue);
748}
749
756
757static OffsetNumber
759 SpGistSearchItem *item,
760 Page page, OffsetNumber offset,
761 bool isnull, bool isroot,
762 bool *reportedSome,
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
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
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 */
832 Assert(so->numberOfNonNullOrderBys > 0);
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,
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,
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 */
916 MemoryContextReset(so->tempCxt);
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 */
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
1013 so->reconTups[so->nPtrs] = heap_form_tuple(so->reconTupDesc,
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)
1041 index_store_float8_orderby_distances(scan, so->orderByTypes,
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
#define Assert(condition)
Definition c.h:873
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
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
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)
pairingheap * pairingheap_allocate(pairingheap_comparator compare, void *arg)
Definition pairingheap.c:42
pairingheap_node * pairingheap_remove_first(pairingheap *heap)
#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
static int fb(int x)
#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
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
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
spgConfigOut config
ItemPointerData heapPtr
pairingheap_node phNode
SpGistLeafTuple leafTuple
ItemPointerData heapPtr
double distances[FLEXIBLE_ARRAY_MEMBER]
Definition type.h:96
bool canReturnData
Definition spgist.h:46
Datum reconstructedValue
Definition spgist.h:140
void * traversalValue
Definition spgist.h:141
MemoryContext traversalMemoryContext
Definition spgist.h:142
void ** traversalValues
Definition spgist.h:160
double ** distances
Definition spgist.h:161
Datum * reconstructedValues
Definition spgist.h:159
Datum reconstructedValue
Definition spgist.h:175
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