PostgreSQL Source Code git master
Loading...
Searching...
No Matches
spgutils.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * spgutils.c
4 * various support functions for SP-GiST
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/spgutils.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres.h"
17
18#include "access/amvalidate.h"
19#include "access/htup_details.h"
20#include "access/reloptions.h"
23#include "access/transam.h"
24#include "access/xact.h"
25#include "catalog/pg_amop.h"
26#include "commands/vacuum.h"
27#include "nodes/nodeFuncs.h"
28#include "parser/parse_coerce.h"
29#include "storage/bufmgr.h"
30#include "storage/indexfsm.h"
31#include "utils/catcache.h"
32#include "utils/fmgrprotos.h"
34#include "utils/lsyscache.h"
35#include "utils/rel.h"
36#include "utils/syscache.h"
37
38
39/*
40 * SP-GiST handler function: return IndexAmRoutine with access method parameters
41 * and callbacks.
42 */
45{
46 static const IndexAmRoutine amroutine = {
48 .amstrategies = 0,
49 .amsupport = SPGISTNProc,
50 .amoptsprocnum = SPGIST_OPTIONS_PROC,
51 .amcanorder = false,
52 .amcanorderbyop = true,
53 .amcanhash = false,
54 .amconsistentequality = false,
55 .amconsistentordering = false,
56 .amcanbackward = false,
57 .amcanunique = false,
58 .amcanmulticol = false,
59 .amoptionalkey = true,
60 .amsearcharray = false,
61 .amsearchnulls = true,
62 .amstorage = true,
63 .amclusterable = false,
64 .ampredlocks = false,
65 .amcanparallel = false,
66 .amcanbuildparallel = false,
67 .amcaninclude = true,
68 .amusemaintenanceworkmem = false,
69 .amsummarizing = false,
70 .amparallelvacuumoptions =
72 .amkeytype = InvalidOid,
73
74 .ambuild = spgbuild,
75 .ambuildempty = spgbuildempty,
76 .aminsert = spginsert,
77 .aminsertcleanup = NULL,
78 .ambulkdelete = spgbulkdelete,
79 .amvacuumcleanup = spgvacuumcleanup,
80 .amcanreturn = spgcanreturn,
81 .amcostestimate = spgcostestimate,
82 .amgettreeheight = NULL,
83 .amoptions = spgoptions,
84 .amproperty = spgproperty,
85 .ambuildphasename = NULL,
86 .amvalidate = spgvalidate,
87 .amadjustmembers = spgadjustmembers,
88 .ambeginscan = spgbeginscan,
89 .amrescan = spgrescan,
90 .amgettuple = spggettuple,
91 .amgetbitmap = spggetbitmap,
92 .amendscan = spgendscan,
93 .ammarkpos = NULL,
94 .amrestrpos = NULL,
95 .amestimateparallelscan = NULL,
96 .aminitparallelscan = NULL,
97 .amparallelrescan = NULL,
98 .amtranslatestrategy = NULL,
99 .amtranslatecmptype = NULL,
100 };
101
103}
104
105/*
106 * GetIndexInputType
107 * Determine the nominal input data type for an index column
108 *
109 * We define the "nominal" input type as the associated opclass's opcintype,
110 * or if that is a polymorphic type, the base type of the heap column or
111 * expression that is the index's input. The reason for preferring the
112 * opcintype is that non-polymorphic opclasses probably don't want to hear
113 * about binary-compatible input types. For instance, if a text opclass
114 * is being used with a varchar heap column, we want to report "text" not
115 * "varchar". Likewise, opclasses don't want to hear about domain types,
116 * so if we do consult the actual input type, we make sure to flatten domains.
117 *
118 * At some point maybe this should go somewhere else, but it's not clear
119 * if any other index AMs have a use for it.
120 */
121static Oid
123{
124 Oid opcintype;
126 List *indexprs;
128
129 Assert(index->rd_index != NULL);
130 Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts);
131 opcintype = index->rd_opcintype[indexcol - 1];
132 if (!IsPolymorphicType(opcintype))
133 return opcintype;
134 heapcol = index->rd_index->indkey.values[indexcol - 1];
135 if (heapcol != 0) /* Simple index column? */
136 return getBaseType(get_atttype(index->rd_index->indrelid, heapcol));
137
138 /*
139 * If the index expressions are already cached, skip calling
140 * RelationGetIndexExpressions, as it will make a copy which is overkill.
141 * We're not going to modify the trees, and we're not going to do anything
142 * that would invalidate the relcache entry before we're done.
143 */
144 if (index->rd_indexprs)
145 indexprs = index->rd_indexprs;
146 else
149 for (int i = 1; i <= index->rd_index->indnkeyatts; i++)
150 {
151 if (index->rd_index->indkey.values[i - 1] == 0)
152 {
153 /* expression column */
154 if (indexpr_item == NULL)
155 elog(ERROR, "wrong number of index expressions");
156 if (i == indexcol)
159 }
160 }
161 elog(ERROR, "wrong number of index expressions");
162 return InvalidOid; /* keep compiler quiet */
163}
164
165/* Fill in a SpGistTypeDesc struct with info about the specified data type */
166static void
168{
169 HeapTuple tp;
171
172 desc->type = type;
174 if (!HeapTupleIsValid(tp))
175 elog(ERROR, "cache lookup failed for type %u", type);
177 desc->attlen = typtup->typlen;
178 desc->attbyval = typtup->typbyval;
179 desc->attalign = typtup->typalign;
180 desc->attstorage = typtup->typstorage;
181 ReleaseSysCache(tp);
182}
183
184/*
185 * Fetch local cache of AM-specific info about the index, initializing it
186 * if necessary
187 */
190{
191 SpGistCache *cache;
192
193 if (index->rd_amcache == NULL)
194 {
195 Oid atttype;
196 spgConfigIn in;
198
199 cache = MemoryContextAllocZero(index->rd_indexcxt,
200 sizeof(SpGistCache));
201
202 /* SPGiST must have one key column and can also have INCLUDE columns */
205
206 /*
207 * Get the actual (well, nominal) data type of the key column. We
208 * pass this to the opclass config function so that polymorphic
209 * opclasses are possible.
210 */
211 atttype = GetIndexInputType(index, spgKeyColumn + 1);
212
213 /* Call the config function to get config info for the opclass */
214 in.attType = atttype;
215
218 index->rd_indcollation[spgKeyColumn],
219 PointerGetDatum(&in),
220 PointerGetDatum(&cache->config));
221
222 /*
223 * If leafType isn't specified, use the declared index column type,
224 * which index.c will have derived from the opclass's opcintype.
225 * (Although we now make spgvalidate.c warn if these aren't the same,
226 * old user-defined opclasses may not set the STORAGE parameter
227 * correctly, so believe leafType if it's given.)
228 */
229 if (!OidIsValid(cache->config.leafType))
230 {
231 cache->config.leafType =
233
234 /*
235 * If index column type is binary-coercible to atttype (for
236 * example, it's a domain over atttype), treat it as plain atttype
237 * to avoid thinking we need to compress.
238 */
239 if (cache->config.leafType != atttype &&
240 IsBinaryCoercible(cache->config.leafType, atttype))
241 cache->config.leafType = atttype;
242 }
243
244 /* Get the information we need about each relevant datatype */
245 fillTypeDesc(&cache->attType, atttype);
246
247 if (cache->config.leafType != atttype)
248 {
252 errmsg("compress method must be defined when leaf type is different from input type")));
253
254 fillTypeDesc(&cache->attLeafType, cache->config.leafType);
255 }
256 else
257 {
258 /* Save lookups in this common case */
259 cache->attLeafType = cache->attType;
260 }
261
263 fillTypeDesc(&cache->attLabelType, cache->config.labelType);
264
265 /*
266 * Finally, if it's a real index (not a partitioned one), get the
267 * lastUsedPages data from the metapage
268 */
269 if (index->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
270 {
272 SpGistMetaPageData *metadata;
273
276
278
279 if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
280 elog(ERROR, "index \"%s\" is not an SP-GiST index",
282
283 cache->lastUsedPages = metadata->lastUsedPages;
284
286 }
287
288 index->rd_amcache = cache;
289 }
290 else
291 {
292 /* assume it's up to date */
293 cache = (SpGistCache *) index->rd_amcache;
294 }
295
296 return cache;
297}
298
299/*
300 * Compute a tuple descriptor for leaf tuples or index-only-scan result tuples.
301 *
302 * We can use the relcache's tupdesc as-is in many cases, and it's always
303 * OK so far as any INCLUDE columns are concerned. However, the entry for
304 * the key column has to match leafType in the first case or attType in the
305 * second case. While the relcache's tupdesc *should* show leafType, this
306 * might not hold for legacy user-defined opclasses, since before v14 they
307 * were not allowed to declare their true storage type in CREATE OPCLASS.
308 * Also, attType can be different from what is in the relcache.
309 *
310 * This function gives back either a pointer to the relcache's tupdesc
311 * if that is suitable, or a palloc'd copy that's been adjusted to match
312 * the specified key column type. We can avoid doing any catalog lookups
313 * here by insisting that the caller pass an SpGistTypeDesc not just an OID.
314 */
317{
320
321 if (keyType->type ==
324 else
325 {
328 /* It's sufficient to update the type-dependent fields of the column */
329 att->atttypid = keyType->type;
330 att->atttypmod = -1;
331 att->attlen = keyType->attlen;
332 att->attbyval = keyType->attbyval;
333 att->attalign = keyType->attalign;
334 att->attstorage = keyType->attstorage;
335 /* We shouldn't need to bother with making these valid: */
336 att->attcompression = InvalidCompressionMethod;
337 att->attcollation = InvalidOid;
338
341 }
342 return outTupDesc;
343}
344
345/* Initialize SpGistState for working with the given index */
346void
348{
349 SpGistCache *cache;
350
351 state->index = index;
352
353 /* Get cached static information about index */
354 cache = spgGetCache(index);
355
356 state->config = cache->config;
357 state->attType = cache->attType;
358 state->attLeafType = cache->attLeafType;
359 state->attPrefixType = cache->attPrefixType;
360 state->attLabelType = cache->attLabelType;
361
362 /* Ensure we have a valid descriptor for leaf tuples */
363 state->leafTupDesc = getSpGistTupleDesc(state->index, &state->attLeafType);
364
365 /* Make workspace for constructing dead tuples */
366 state->deadTupleStorage = palloc0(SGDTSIZE);
367
368 /*
369 * Set horizon XID to use in redirection tuples. Use our own XID if we
370 * have one, else use InvalidTransactionId. The latter case can happen in
371 * VACUUM or REINDEX CONCURRENTLY, and in neither case would it be okay to
372 * force an XID to be assigned. VACUUM won't create any redirection
373 * tuples anyway, but REINDEX CONCURRENTLY can. Fortunately, REINDEX
374 * CONCURRENTLY doesn't mark the index valid until the end, so there could
375 * never be any concurrent scans "in flight" to a redirection tuple it has
376 * inserted. And it locks out VACUUM until the end, too. So it's okay
377 * for VACUUM to immediately expire a redirection tuple that contains an
378 * invalid xid.
379 */
380 state->redirectXid = GetTopTransactionIdIfAny();
381
382 /* Assume we're not in an index build (spgbuild will override) */
383 state->isBuild = false;
384}
385
386/*
387 * Allocate a new page (either by recycling, or by extending the index file).
388 *
389 * The returned buffer is already pinned and exclusive-locked.
390 * Caller is responsible for initializing the page by calling SpGistInitBuffer.
391 */
392Buffer
394{
395 Buffer buffer;
396
397 /* First, try to get a page from FSM */
398 for (;;)
399 {
401
402 if (blkno == InvalidBlockNumber)
403 break; /* nothing known to FSM */
404
405 /*
406 * The fixed pages shouldn't ever be listed in FSM, but just in case
407 * one is, ignore it.
408 */
409 if (SpGistBlockIsFixed(blkno))
410 continue;
411
412 buffer = ReadBuffer(index, blkno);
413
414 /*
415 * We have to guard against the possibility that someone else already
416 * recycled this page; the buffer may be locked if so.
417 */
418 if (ConditionalLockBuffer(buffer))
419 {
420 Page page = BufferGetPage(buffer);
421
422 if (PageIsNew(page))
423 return buffer; /* OK to use, if never initialized */
424
425 if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
426 return buffer; /* OK to use */
427
429 }
430
431 /* Can't use it, so release buffer and try again */
432 ReleaseBuffer(buffer);
433 }
434
437
438 return buffer;
439}
440
441/*
442 * Update index metapage's lastUsedPages info from local cache, if possible
443 *
444 * Updating meta page isn't critical for index working, so
445 * 1 use ConditionalLockBuffer to improve concurrency
446 * 2 don't WAL-log metabuffer changes to decrease WAL traffic
447 */
448void
450{
451 SpGistCache *cache = (SpGistCache *) index->rd_amcache;
452
453 if (cache != NULL)
454 {
456
458
460 {
463
464 metadata->lastUsedPages = cache->lastUsedPages;
465
466 /*
467 * Set pd_lower just past the end of the metadata. This is
468 * essential, because without doing so, metadata will be lost if
469 * xlog.c compresses the page. (We must do this here because
470 * pre-v11 versions of PG did not set the metapage's pd_lower
471 * correctly, so a pg_upgraded index might contain the wrong
472 * value.)
473 */
474 ((PageHeader) metapage)->pd_lower =
475 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
476
479 }
480 else
481 {
483 }
484 }
485}
486
487/* Macro to select proper element of lastUsedPages cache depending on flags */
488/* Masking flags with SPGIST_CACHED_PAGES is just for paranoia's sake */
489#define GET_LUP(c, f) (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])
490
491/*
492 * Allocate and initialize a new buffer of the type and parity specified by
493 * flags. The returned buffer is already pinned and exclusive-locked.
494 *
495 * When requesting an inner page, if we get one with the wrong parity,
496 * we just release the buffer and try again. We will get a different page
497 * because GetFreeIndexPage will have marked the page used in FSM. The page
498 * is entered in our local lastUsedPages cache, so there's some hope of
499 * making use of it later in this session, but otherwise we rely on VACUUM
500 * to eventually re-enter the page in FSM, making it available for recycling.
501 * Note that such a page does not get marked dirty here, so unless it's used
502 * fairly soon, the buffer will just get discarded and the page will remain
503 * as it was on disk.
504 *
505 * When we return a buffer to the caller, the page is *not* entered into
506 * the lastUsedPages cache; we expect the caller will do so after it's taken
507 * whatever space it will use. This is because after the caller has used up
508 * some space, the page might have less space than whatever was cached already
509 * so we'd rather not trash the old cache entry.
510 */
511static Buffer
513{
514 SpGistCache *cache = spgGetCache(index);
515 uint16 pageflags = 0;
516
517 if (GBUF_REQ_LEAF(flags))
519 if (GBUF_REQ_NULLS(flags))
521
522 for (;;)
523 {
524 Buffer buffer;
525
526 buffer = SpGistNewBuffer(index);
528
530 {
531 /* Leaf pages have no parity concerns, so just use it */
532 return buffer;
533 }
534 else
535 {
536 BlockNumber blkno = BufferGetBlockNumber(buffer);
537 int blkFlags = GBUF_INNER_PARITY(blkno);
538
539 if ((flags & GBUF_PARITY_MASK) == blkFlags)
540 {
541 /* Page has right parity, use it */
542 return buffer;
543 }
544 else
545 {
546 /* Page has wrong parity, record it in cache and try again */
549 cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
552 UnlockReleaseBuffer(buffer);
553 }
554 }
555 }
556}
557
558/*
559 * Get a buffer of the type and parity specified by flags, having at least
560 * as much free space as indicated by needSpace. We use the lastUsedPages
561 * cache to assign the same buffer previously requested when possible.
562 * The returned buffer is already pinned and exclusive-locked.
563 *
564 * *isNew is set true if the page was initialized here, false if it was
565 * already valid.
566 */
567Buffer
568SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
569{
570 SpGistCache *cache = spgGetCache(index);
572
573 /* Bail out if even an empty page wouldn't meet the demand */
575 elog(ERROR, "desired SPGiST tuple size is too big");
576
577 /*
578 * If possible, increase the space request to include relation's
579 * fillfactor. This ensures that when we add unrelated tuples to a page,
580 * we try to keep 100-fillfactor% available for adding tuples that are
581 * related to the ones already on it. But fillfactor mustn't cause an
582 * error for requests that would otherwise be legal.
583 */
586
587 /* Get the cache entry for this flags setting */
588 lup = GET_LUP(cache, flags);
589
590 /* If we have nothing cached, just turn it over to allocNewBuffer */
591 if (lup->blkno == InvalidBlockNumber)
592 {
593 *isNew = true;
594 return allocNewBuffer(index, flags);
595 }
596
597 /* fixed pages should never be in cache */
598 Assert(!SpGistBlockIsFixed(lup->blkno));
599
600 /* If cached freeSpace isn't enough, don't bother looking at the page */
601 if (lup->freeSpace >= needSpace)
602 {
603 Buffer buffer;
604 Page page;
605
606 buffer = ReadBuffer(index, lup->blkno);
607
608 if (!ConditionalLockBuffer(buffer))
609 {
610 /*
611 * buffer is locked by another process, so return a new buffer
612 */
613 ReleaseBuffer(buffer);
614 *isNew = true;
615 return allocNewBuffer(index, flags);
616 }
617
618 page = BufferGetPage(buffer);
619
620 if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
621 {
622 /* OK to initialize the page */
623 uint16 pageflags = 0;
624
625 if (GBUF_REQ_LEAF(flags))
627 if (GBUF_REQ_NULLS(flags))
630 lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
631 *isNew = true;
632 return buffer;
633 }
634
635 /*
636 * Check that page is of right type and has enough space. We must
637 * recheck this since our cache isn't necessarily up to date.
638 */
639 if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
641 {
642 int freeSpace = PageGetExactFreeSpace(page);
643
644 if (freeSpace >= needSpace)
645 {
646 /* Success, update freespace info and return the buffer */
647 lup->freeSpace = freeSpace - needSpace;
648 *isNew = false;
649 return buffer;
650 }
651 }
652
653 /*
654 * fallback to allocation of new buffer
655 */
656 UnlockReleaseBuffer(buffer);
657 }
658
659 /* No success with cache, so return a new buffer */
660 *isNew = true;
661 return allocNewBuffer(index, flags);
662}
663
664/*
665 * Update lastUsedPages cache when done modifying a page.
666 *
667 * We update the appropriate cache entry if it already contained this page
668 * (its freeSpace is likely obsolete), or if this page has more space than
669 * whatever we had cached.
670 */
671void
673{
674 SpGistCache *cache = spgGetCache(index);
676 int freeSpace;
677 Page page = BufferGetPage(buffer);
678 BlockNumber blkno = BufferGetBlockNumber(buffer);
679 int flags;
680
681 /* Never enter fixed pages (root pages) in cache, though */
682 if (SpGistBlockIsFixed(blkno))
683 return;
684
685 if (SpGistPageIsLeaf(page))
686 flags = GBUF_LEAF;
687 else
688 flags = GBUF_INNER_PARITY(blkno);
689 if (SpGistPageStoresNulls(page))
690 flags |= GBUF_NULLS;
691
692 lup = GET_LUP(cache, flags);
693
694 freeSpace = PageGetExactFreeSpace(page);
695 if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
696 lup->freeSpace < freeSpace)
697 {
698 lup->blkno = blkno;
699 lup->freeSpace = freeSpace;
700 }
701}
702
703/*
704 * Initialize an SPGiST page to empty, with specified flags
705 */
706void
708{
709 SpGistPageOpaque opaque;
710
711 PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
712 opaque = SpGistPageGetOpaque(page);
713 opaque->flags = f;
715}
716
717/*
718 * Initialize a buffer's page to empty, with specified flags
719 */
720void
726
727/*
728 * Initialize metadata page
729 */
730void
732{
733 SpGistMetaPageData *metadata;
734 int i;
735
737 metadata = SpGistPageGetMeta(page);
738 memset(metadata, 0, sizeof(SpGistMetaPageData));
740
741 /* initialize last-used-page cache to empty */
742 for (i = 0; i < SPGIST_CACHED_PAGES; i++)
744
745 /*
746 * Set pd_lower just past the end of the metadata. This is essential,
747 * because without doing so, metadata will be lost if xlog.c compresses
748 * the page.
749 */
750 ((PageHeader) page)->pd_lower =
751 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
752}
753
754/*
755 * reloptions processing for SPGiST
756 */
757bytea *
758spgoptions(Datum reloptions, bool validate)
759{
760 static const relopt_parse_elt tab[] = {
762 };
763
764 return (bytea *) build_reloptions(reloptions, validate,
766 sizeof(SpGistOptions),
767 tab, lengthof(tab));
768}
769
770/*
771 * Get the space needed to store a non-null datum of the indicated type
772 * in an inner tuple (that is, as a prefix or node label).
773 * Note the result is already rounded up to a MAXALIGN boundary.
774 * Here we follow the convention that pass-by-val types are just stored
775 * in their Datum representation (compare memcpyInnerDatum).
776 */
777unsigned int
779{
780 unsigned int size;
781
782 if (att->attbyval)
783 size = sizeof(Datum);
784 else if (att->attlen > 0)
785 size = att->attlen;
786 else
787 size = VARSIZE_ANY(DatumGetPointer(datum));
788
789 return MAXALIGN(size);
790}
791
792/*
793 * Copy the given non-null datum to *target, in the inner-tuple case
794 */
795static void
797{
798 unsigned int size;
799
800 if (att->attbyval)
801 {
802 memcpy(target, &datum, sizeof(Datum));
803 }
804 else
805 {
806 size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(DatumGetPointer(datum));
807 memcpy(target, DatumGetPointer(datum), size);
808 }
809}
810
811/*
812 * Compute space required for a leaf tuple holding the given data.
813 *
814 * This must match the size-calculation portion of spgFormLeafTuple.
815 */
816Size
818 const Datum *datums, const bool *isnulls)
819{
820 Size size;
822 bool needs_null_mask = false;
823 int natts = tupleDescriptor->natts;
824
825 /*
826 * Decide whether we need a nulls bitmask.
827 *
828 * If there is only a key attribute (natts == 1), never use a bitmask, for
829 * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
830 * need one if any attribute is null.
831 */
832 if (natts > 1)
833 {
834 for (int i = 0; i < natts; i++)
835 {
836 if (isnulls[i])
837 {
838 needs_null_mask = true;
839 break;
840 }
841 }
842 }
843
844 /*
845 * Calculate size of the data part; same as for heap tuples.
846 */
848
849 /*
850 * Compute total size.
851 */
853 size += data_size;
854 size = MAXALIGN(size);
855
856 /*
857 * Ensure that we can replace the tuple with a dead tuple later. This test
858 * is unnecessary when there are any non-null attributes, but be safe.
859 */
860 if (size < SGDTSIZE)
861 size = SGDTSIZE;
862
863 return size;
864}
865
866/*
867 * Construct a leaf tuple containing the given heap TID and datum values
868 */
871 const Datum *datums, const bool *isnulls)
872{
874 TupleDesc tupleDescriptor = state->leafTupDesc;
875 Size size;
876 Size hoff;
878 bool needs_null_mask = false;
879 int natts = tupleDescriptor->natts;
880 char *tp; /* ptr to tuple data */
881 uint16 tupmask = 0; /* unused heap_fill_tuple output */
882
883 /*
884 * Decide whether we need a nulls bitmask.
885 *
886 * If there is only a key attribute (natts == 1), never use a bitmask, for
887 * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
888 * need one if any attribute is null.
889 */
890 if (natts > 1)
891 {
892 for (int i = 0; i < natts; i++)
893 {
894 if (isnulls[i])
895 {
896 needs_null_mask = true;
897 break;
898 }
899 }
900 }
901
902 /*
903 * Calculate size of the data part; same as for heap tuples.
904 */
906
907 /*
908 * Compute total size.
909 */
911 size = hoff + data_size;
912 size = MAXALIGN(size);
913
914 /*
915 * Ensure that we can replace the tuple with a dead tuple later. This test
916 * is unnecessary when there are any non-null attributes, but be safe.
917 */
918 if (size < SGDTSIZE)
919 size = SGDTSIZE;
920
921 /* OK, form the tuple */
922 tup = (SpGistLeafTuple) palloc0(size);
923
924 tup->size = size;
926 tup->heapPtr = *heapPtr;
927
928 tp = (char *) tup + hoff;
929
930 if (needs_null_mask)
931 {
932 bits8 *bp; /* ptr to null bitmap in tuple */
933
934 /* Set nullmask presence bit in SpGistLeafTuple header */
936 /* Fill the data area and null mask */
937 bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
939 &tupmask, bp);
940 }
941 else if (natts > 1 || !isnulls[spgKeyColumn])
942 {
943 /* Fill data area only */
945 &tupmask, (bits8 *) NULL);
946 }
947 /* otherwise we have no data, nor a bitmap, to fill */
948
949 return tup;
950}
951
952/*
953 * Construct a node (to go into an inner tuple) containing the given label
954 *
955 * Note that the node's downlink is just set invalid here. Caller will fill
956 * it in later.
957 */
960{
962 unsigned int size;
963 unsigned short infomask = 0;
964
965 /* compute space needed (note result is already maxaligned) */
966 size = SGNTHDRSZ;
967 if (!isnull)
968 size += SpGistGetInnerTypeSize(&state->attLabelType, label);
969
970 /*
971 * Here we make sure that the size will fit in the field reserved for it
972 * in t_info.
973 */
974 if ((size & INDEX_SIZE_MASK) != size)
977 errmsg("index row requires %zu bytes, maximum size is %zu",
978 (Size) size, (Size) INDEX_SIZE_MASK)));
979
980 tup = (SpGistNodeTuple) palloc0(size);
981
982 if (isnull)
984 /* we don't bother setting the INDEX_VAR_MASK bit */
985 infomask |= size;
986 tup->t_info = infomask;
987
988 /* The TID field will be filled in later */
989 ItemPointerSetInvalid(&tup->t_tid);
990
991 if (!isnull)
992 memcpyInnerDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
993
994 return tup;
995}
996
997/*
998 * Construct an inner tuple containing the given prefix and node array
999 */
1001spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
1002 int nNodes, SpGistNodeTuple *nodes)
1003{
1005 unsigned int size;
1006 unsigned int prefixSize;
1007 int i;
1008 char *ptr;
1009
1010 /* Compute size needed */
1011 if (hasPrefix)
1012 prefixSize = SpGistGetInnerTypeSize(&state->attPrefixType, prefix);
1013 else
1014 prefixSize = 0;
1015
1016 size = SGITHDRSZ + prefixSize;
1017
1018 /* Note: we rely on node tuple sizes to be maxaligned already */
1019 for (i = 0; i < nNodes; i++)
1020 size += IndexTupleSize(nodes[i]);
1021
1022 /*
1023 * Ensure that we can replace the tuple with a dead tuple later. This
1024 * test is unnecessary given current tuple layouts, but let's be safe.
1025 */
1026 if (size < SGDTSIZE)
1027 size = SGDTSIZE;
1028
1029 /*
1030 * Inner tuple should be small enough to fit on a page
1031 */
1032 if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
1033 ereport(ERROR,
1035 errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
1036 (Size) size,
1038 errhint("Values larger than a buffer page cannot be indexed.")));
1039
1040 /*
1041 * Check for overflow of header fields --- probably can't fail if the
1042 * above succeeded, but let's be paranoid
1043 */
1044 if (size > SGITMAXSIZE ||
1045 prefixSize > SGITMAXPREFIXSIZE ||
1046 nNodes > SGITMAXNNODES)
1047 elog(ERROR, "SPGiST inner tuple header field is too small");
1048
1049 /* OK, form the tuple */
1050 tup = (SpGistInnerTuple) palloc0(size);
1051
1052 tup->nNodes = nNodes;
1053 tup->prefixSize = prefixSize;
1054 tup->size = size;
1055
1056 if (hasPrefix)
1057 memcpyInnerDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
1058
1059 ptr = (char *) SGITNODEPTR(tup);
1060
1061 for (i = 0; i < nNodes; i++)
1062 {
1063 SpGistNodeTuple node = nodes[i];
1064
1065 memcpy(ptr, node, IndexTupleSize(node));
1066 ptr += IndexTupleSize(node);
1067 }
1068
1069 return tup;
1070}
1071
1072/*
1073 * Construct a "dead" tuple to replace a tuple being deleted.
1074 *
1075 * The state can be SPGIST_REDIRECT, SPGIST_DEAD, or SPGIST_PLACEHOLDER.
1076 * For a REDIRECT tuple, a pointer (blkno+offset) must be supplied, and
1077 * the xid field is filled in automatically.
1078 *
1079 * This is called in critical sections, so we don't use palloc; the tuple
1080 * is built in preallocated storage. It should be copied before another
1081 * call with different parameters can occur.
1082 */
1085 BlockNumber blkno, OffsetNumber offnum)
1086{
1087 SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
1088
1089 tuple->tupstate = tupstate;
1090 tuple->size = SGDTSIZE;
1092
1093 if (tupstate == SPGIST_REDIRECT)
1094 {
1095 ItemPointerSet(&tuple->pointer, blkno, offnum);
1096 tuple->xid = state->redirectXid;
1097 }
1098 else
1099 {
1101 tuple->xid = InvalidTransactionId;
1102 }
1103
1104 return tuple;
1105}
1106
1107/*
1108 * Convert an SPGiST leaf tuple into Datum/isnull arrays.
1109 *
1110 * The caller must allocate sufficient storage for the output arrays.
1111 * (INDEX_MAX_KEYS entries should be enough.)
1112 */
1113void
1115 Datum *datums, bool *isnulls, bool keyColumnIsNull)
1116{
1118 char *tp; /* ptr to tuple data */
1119 bits8 *bp; /* ptr to null bitmap in tuple */
1120
1121 if (keyColumnIsNull && tupleDescriptor->natts == 1)
1122 {
1123 /*
1124 * Trivial case: there is only the key attribute and we're in a nulls
1125 * tree. The hasNullsMask bit in the tuple header should not be set
1126 * (and thus we can't use index_deform_tuple_internal), but
1127 * nonetheless the result is NULL.
1128 *
1129 * Note: currently this is dead code, because noplace calls this when
1130 * there is only the key attribute. But we should cover the case.
1131 */
1133
1134 datums[spgKeyColumn] = (Datum) 0;
1135 isnulls[spgKeyColumn] = true;
1136 return;
1137 }
1138
1139 tp = (char *) tup + SGLTHDRSZ(hasNullsMask);
1140 bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
1141
1143 datums, isnulls,
1144 tp, bp, hasNullsMask);
1145
1146 /*
1147 * Key column isnull value from the tuple should be consistent with
1148 * keyColumnIsNull flag from the caller.
1149 */
1151}
1152
1153/*
1154 * Extract the label datums of the nodes within innerTuple
1155 *
1156 * Returns NULL if label datums are NULLs
1157 */
1158Datum *
1160{
1161 Datum *nodeLabels;
1162 int i;
1163 SpGistNodeTuple node;
1164
1165 /* Either all the labels must be NULL, or none. */
1166 node = SGITNODEPTR(innerTuple);
1167 if (IndexTupleHasNulls(node))
1168 {
1169 SGITITERATE(innerTuple, i, node)
1170 {
1171 if (!IndexTupleHasNulls(node))
1172 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1173 }
1174 /* They're all null, so just return NULL */
1175 return NULL;
1176 }
1177 else
1178 {
1179 nodeLabels = palloc_array(Datum, innerTuple->nNodes);
1180 SGITITERATE(innerTuple, i, node)
1181 {
1182 if (IndexTupleHasNulls(node))
1183 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1184 nodeLabels[i] = SGNTDATUM(node, state);
1185 }
1186 return nodeLabels;
1187 }
1188}
1189
1190/*
1191 * Add a new item to the page, replacing a PLACEHOLDER item if possible.
1192 * Return the location it's inserted at, or InvalidOffsetNumber on failure.
1193 *
1194 * If startOffset isn't NULL, we start searching for placeholders at
1195 * *startOffset, and update that to the next place to search. This is just
1196 * an optimization for repeated insertions.
1197 *
1198 * If errorOK is false, we throw error when there's not enough room,
1199 * rather than returning InvalidOffsetNumber.
1200 */
1202SpGistPageAddNewItem(SpGistState *state, Page page, const void *item, Size size,
1203 OffsetNumber *startOffset, bool errorOK)
1204{
1207 maxoff,
1208 offnum;
1209
1210 if (opaque->nPlaceholder > 0 &&
1211 PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
1212 {
1213 /* Try to replace a placeholder */
1214 maxoff = PageGetMaxOffsetNumber(page);
1215 offnum = InvalidOffsetNumber;
1216
1217 for (;;)
1218 {
1219 if (startOffset && *startOffset != InvalidOffsetNumber)
1220 i = *startOffset;
1221 else
1223 for (; i <= maxoff; i++)
1224 {
1226 PageGetItemId(page, i));
1227
1228 if (it->tupstate == SPGIST_PLACEHOLDER)
1229 {
1230 offnum = i;
1231 break;
1232 }
1233 }
1234
1235 /* Done if we found a placeholder */
1236 if (offnum != InvalidOffsetNumber)
1237 break;
1238
1239 if (startOffset && *startOffset != InvalidOffsetNumber)
1240 {
1241 /* Hint was no good, re-search from beginning */
1242 *startOffset = InvalidOffsetNumber;
1243 continue;
1244 }
1245
1246 /* Hmm, no placeholder found? */
1247 opaque->nPlaceholder = 0;
1248 break;
1249 }
1250
1251 if (offnum != InvalidOffsetNumber)
1252 {
1253 /* Replace the placeholder tuple */
1254 PageIndexTupleDelete(page, offnum);
1255
1256 offnum = PageAddItem(page, item, size, offnum, false, false);
1257
1258 /*
1259 * We should not have failed given the size check at the top of
1260 * the function, but test anyway. If we did fail, we must PANIC
1261 * because we've already deleted the placeholder tuple, and
1262 * there's no other way to keep the damage from getting to disk.
1263 */
1264 if (offnum != InvalidOffsetNumber)
1265 {
1266 Assert(opaque->nPlaceholder > 0);
1267 opaque->nPlaceholder--;
1268 if (startOffset)
1269 *startOffset = offnum + 1;
1270 }
1271 else
1272 elog(PANIC, "failed to add item of size %zu to SPGiST index page",
1273 size);
1274
1275 return offnum;
1276 }
1277 }
1278
1279 /* No luck in replacing a placeholder, so just add it to the page */
1280 offnum = PageAddItem(page, item, size,
1281 InvalidOffsetNumber, false, false);
1282
1283 if (offnum == InvalidOffsetNumber && !errorOK)
1284 elog(ERROR, "failed to add item of size %zu to SPGiST index page",
1285 size);
1286
1287 return offnum;
1288}
1289
1290/*
1291 * spgproperty() -- Check boolean properties of indexes.
1292 *
1293 * This is optional for most AMs, but is required for SP-GiST because the core
1294 * property code doesn't support AMPROP_DISTANCE_ORDERABLE.
1295 */
1296bool
1298 IndexAMProperty prop, const char *propname,
1299 bool *res, bool *isnull)
1300{
1301 Oid opclass,
1302 opfamily,
1303 opcintype;
1305 int i;
1306
1307 /* Only answer column-level inquiries */
1308 if (attno == 0)
1309 return false;
1310
1311 switch (prop)
1312 {
1314 break;
1315 default:
1316 return false;
1317 }
1318
1319 /*
1320 * Currently, SP-GiST distance-ordered scans require that there be a
1321 * distance operator in the opclass with the default types. So we assume
1322 * that if such an operator exists, then there's a reason for it.
1323 */
1324
1325 /* First we need to know the column's opclass. */
1326 opclass = get_index_column_opclass(index_oid, attno);
1327 if (!OidIsValid(opclass))
1328 {
1329 *isnull = true;
1330 return true;
1331 }
1332
1333 /* Now look up the opclass family and input datatype. */
1334 if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1335 {
1336 *isnull = true;
1337 return true;
1338 }
1339
1340 /* And now we can check whether the operator is provided. */
1342 ObjectIdGetDatum(opfamily));
1343
1344 *res = false;
1345
1346 for (i = 0; i < catlist->n_members; i++)
1347 {
1348 HeapTuple amoptup = &catlist->members[i]->tuple;
1350
1351 if (amopform->amoppurpose == AMOP_ORDER &&
1352 (amopform->amoplefttype == opcintype ||
1353 amopform->amoprighttype == opcintype) &&
1354 opfamily_can_sort_type(amopform->amopsortfamily,
1355 get_op_rettype(amopform->amopopr)))
1356 {
1357 *res = true;
1358 break;
1359 }
1360 }
1361
1363
1364 *isnull = false;
1365
1366 return true;
1367}
IndexAMProperty
Definition amapi.h:39
@ AMPROP_DISTANCE_ORDERABLE
Definition amapi.h:46
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition amvalidate.c:271
int16 AttrNumber
Definition attnum.h:21
static bool validate(Port *port, const char *auth)
Definition auth-oauth.c:638
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
int Buffer
Definition buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4357
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition bufmgr.c:974
bool ConditionalLockBuffer(Buffer buffer)
Definition bufmgr.c:6484
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5505
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5522
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3063
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition bufmgr.c:874
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:470
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:210
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:205
static Size BufferGetPageSize(Buffer buffer)
Definition bufmgr.h:459
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:332
@ EB_LOCK_FIRST
Definition bufmgr.h:87
#define BMR_REL(p_rel)
Definition bufmgr.h:114
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition bufpage.c:1051
Size PageGetExactFreeSpace(const PageData *page)
Definition bufpage.c:957
void PageInit(Page page, Size pageSize, Size specialSize)
Definition bufpage.c:42
static bool PageIsEmpty(const PageData *page)
Definition bufpage.h:249
PageHeaderData * PageHeader
Definition bufpage.h:199
static bool PageIsNew(const PageData *page)
Definition bufpage.h:259
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:269
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:379
PageData * Page
Definition bufpage.h:81
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition bufpage.h:504
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:397
#define Min(x, y)
Definition c.h:1093
#define MAXALIGN(LEN)
Definition c.h:898
#define Assert(condition)
Definition c.h:945
uint8 bits8
Definition c.h:625
uint16_t uint16
Definition c.h:617
#define lengthof(array)
Definition c.h:875
#define OidIsValid(objectId)
Definition c.h:860
size_t Size
Definition c.h:691
int errcode(int sqlerrcode)
Definition elog.c:874
int errhint(const char *fmt,...) pg_attribute_printf(1
#define PANIC
Definition elog.h:42
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
#define palloc_array(type, count)
Definition fe_memutils.h:76
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition fmgr.c:1151
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
Size heap_compute_data_size(TupleDesc tupleDesc, const Datum *values, const bool *isnull)
Definition heaptuple.c:219
void heap_fill_tuple(TupleDesc tupleDesc, const Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition heaptuple.c:401
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition indexam.c:917
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition indexam.c:883
BlockNumber GetFreeIndexPage(Relation rel)
Definition indexfsm.c:38
void index_deform_tuple_internal(TupleDesc tupleDescriptor, Datum *values, bool *isnull, char *tp, bits8 *bp, int hasnulls)
Definition indextuple.c:387
int b
Definition isn.c:74
int i
Definition isn.c:77
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition itemptr.h:135
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
#define INDEX_NULL_MASK
Definition itup.h:68
static bool IndexTupleHasNulls(const IndexTupleData *itup)
Definition itup.h:77
static Size IndexTupleSize(const IndexTupleData *itup)
Definition itup.h:71
#define INDEX_SIZE_MASK
Definition itup.h:65
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition lsyscache.c:1407
Oid get_op_rettype(Oid opno)
Definition lsyscache.c:1553
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition lsyscache.c:3734
Oid getBaseType(Oid typid)
Definition lsyscache.c:2743
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition lsyscache.c:1032
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
void * palloc0(Size size)
Definition mcxt.c:1417
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
static char * errmsg
#define InvalidOffsetNumber
Definition off.h:26
uint16 OffsetNumber
Definition off.h:24
#define FirstOffsetNumber
Definition off.h:27
bool IsBinaryCoercible(Oid srctype, Oid targettype)
END_CATALOG_STRUCT typedef FormData_pg_amop * Form_pg_amop
Definition pg_amop.h:92
FormData_pg_attribute * Form_pg_attribute
static char * label
#define INDEX_MAX_KEYS
#define lfirst(lc)
Definition pg_list.h:172
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:343
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
static int fillfactor
Definition pgbench.c:188
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define InvalidOid
unsigned int Oid
static int fb(int x)
#define RelationGetDescr(relation)
Definition rel.h:540
#define RelationGetRelationName(relation)
Definition rel.h:548
#define IndexRelationGetNumberOfAttributes(relation)
Definition rel.h:526
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:533
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5087
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
@ RELOPT_KIND_SPGIST
Definition reloptions.h:51
@ RELOPT_TYPE_INT
Definition reloptions.h:33
@ MAIN_FORKNUM
Definition relpath.h:58
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition selfuncs.c:8264
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition spginsert.c:73
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition spginsert.c:183
void spgbuildempty(Relation index)
Definition spginsert.c:154
#define SPGIST_OPTIONS_PROC
Definition spgist.h:29
#define SPGIST_COMPRESS_PROC
Definition spgist.h:28
#define SPGISTNProc
Definition spgist.h:31
#define SPGIST_CONFIG_PROC
Definition spgist.h:23
#define SGNTDATAPTR(x)
SpGistDeadTupleData * SpGistDeadTuple
#define SGLT_GET_HASNULLMASK(spgLeafTuple)
#define GBUF_NULLS
#define SPGIST_REDIRECT
SpGistInnerTupleData * SpGistInnerTuple
#define SGDTSIZE
#define SpGistPageStoresNulls(page)
#define SPGIST_PLACEHOLDER
#define SGITDATAPTR(x)
#define SGLT_SET_HASNULLMASK(spgLeafTuple, hasnulls)
#define SGITITERATE(x, i, nt)
#define SpGistGetTargetPageFreeSpace(relation)
#define GBUF_PARITY_MASK
#define GBUF_LEAF
#define SGITMAXSIZE
#define SpGistPageGetMeta(p)
SpGistNodeTupleData * SpGistNodeTuple
#define SGITMAXPREFIXSIZE
#define SpGistPageIsLeaf(page)
#define SPGIST_METAPAGE_BLKNO
#define SpGistPageIsDeleted(page)
#define SGLTHDRSZ(hasnulls)
#define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber)
#define SGITHDRSZ
#define SPGIST_META
struct SpGistLeafTupleData * SpGistLeafTuple
#define SPGIST_MAGIC_NUMBER
#define SPGIST_CACHED_PAGES
#define SPGIST_NULLS
#define GBUF_REQ_NULLS(flags)
#define SPGIST_PAGE_CAPACITY
#define SGITMAXNNODES
#define SGNTDATUM(x, s)
#define SpGistPageGetOpaque(page)
#define SPGIST_PAGE_ID
#define SGNTHDRSZ
#define GBUF_INNER_PARITY(x)
#define SGITNODEPTR(x)
#define SpGistBlockIsFixed(blkno)
#define GBUF_REQ_LEAF(flags)
#define SPGIST_LEAF
#define spgKeyColumn
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition spgscan.c:305
bool spgcanreturn(Relation index, int attno)
Definition spgscan.c:1078
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition spgscan.c:1021
void spgendscan(IndexScanDesc scan)
Definition spgscan.c:424
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
OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page, const void *item, Size size, OffsetNumber *startOffset, bool errorOK)
Definition spgutils.c:1202
Datum * spgExtractNodeLabels(SpGistState *state, SpGistInnerTuple innerTuple)
Definition spgutils.c:1159
void initSpGistState(SpGistState *state, Relation index)
Definition spgutils.c:347
void SpGistUpdateMetaPage(Relation index)
Definition spgutils.c:449
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Definition spgutils.c:316
Buffer SpGistNewBuffer(Relation index)
Definition spgutils.c:393
SpGistLeafTuple spgFormLeafTuple(SpGistState *state, const ItemPointerData *heapPtr, const Datum *datums, const bool *isnulls)
Definition spgutils.c:870
SpGistInnerTuple spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
Definition spgutils.c:1001
static void memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition spgutils.c:796
SpGistCache * spgGetCache(Relation index)
Definition spgutils.c:189
void spgDeformLeafTuple(SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
Definition spgutils.c:1114
SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
Definition spgutils.c:1084
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
Definition spgutils.c:778
static Oid GetIndexInputType(Relation index, AttrNumber indexcol)
Definition spgutils.c:122
void SpGistInitBuffer(Buffer b, uint16 f)
Definition spgutils.c:721
Buffer SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
Definition spgutils.c:568
bytea * spgoptions(Datum reloptions, bool validate)
Definition spgutils.c:758
static Buffer allocNewBuffer(Relation index, int flags)
Definition spgutils.c:512
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition spgutils.c:1297
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
Definition spgutils.c:672
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type)
Definition spgutils.c:167
Datum spghandler(PG_FUNCTION_ARGS)
Definition spgutils.c:44
Size SpGistGetLeafTupleSize(TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
Definition spgutils.c:817
void SpGistInitPage(Page page, uint16 f)
Definition spgutils.c:707
#define GET_LUP(c, f)
Definition spgutils.c:489
SpGistNodeTuple spgFormNodeTuple(SpGistState *state, Datum label, bool isnull)
Definition spgutils.c:959
void SpGistInitMetapage(Page page)
Definition spgutils.c:731
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition spgvacuum.c:950
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition spgvacuum.c:981
bool spgvalidate(Oid opclassoid)
Definition spgvalidate.c:38
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
NodeTag type
Definition amapi.h:234
Definition pg_list.h:54
Definition nodes.h:135
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
SpGistLUPCache lastUsedPages
spgConfigOut config
SpGistTypeDesc attLabelType
ItemPointerData pointer
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
SpGistLUPCache lastUsedPages
Definition type.h:96
Oid attType
Definition spgist.h:38
Oid leafType
Definition spgist.h:45
Oid labelType
Definition spgist.h:44
Oid prefixType
Definition spgist.h:43
Definition c.h:778
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
#define ReleaseSysCacheList(x)
Definition syscache.h:134
#define SearchSysCacheList1(cacheId, key1)
Definition syscache.h:127
#define InvalidCompressionMethod
#define InvalidTransactionId
Definition transam.h:31
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:508
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition vacuum.h:55
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460
const char * type
TransactionId GetTopTransactionIdIfAny(void)
Definition xact.c:443