PostgreSQL Source Code git master
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 = {
47 .type = T_IndexAmRoutine,
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
102 PG_RETURN_POINTER(&amroutine);
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;
125 AttrNumber heapcol;
126 List *indexprs;
127 ListCell *indexpr_item;
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
148 indexpr_item = list_head(indexprs);
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)
157 return getBaseType(exprType((Node *) lfirst(indexpr_item)));
158 indexpr_item = lnext(indexprs, indexpr_item);
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;
170 Form_pg_type typtup;
171
172 desc->type = type;
173 tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
174 if (!HeapTupleIsValid(tp))
175 elog(ERROR, "cache lookup failed for type %u", type);
176 typtup = (Form_pg_type) GETSTRUCT(tp);
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;
197 FmgrInfo *procinfo;
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
217 FunctionCall2Coll(procinfo,
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 {
251 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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 {
271 Buffer metabuffer;
272 SpGistMetaPageData *metadata;
273
275 LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
276
277 metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
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
285 UnlockReleaseBuffer(metabuffer);
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{
318 TupleDesc outTupDesc;
320
321 if (keyType->type ==
323 outTupDesc = RelationGetDescr(index);
324 else
325 {
327 att = TupleDescAttr(outTupDesc, spgKeyColumn);
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 /* In case we changed typlen, we'd better reset following offsets */
339 for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
340 TupleDescCompactAttr(outTupDesc, i)->attcacheoff = -1;
341
343 }
344 return outTupDesc;
345}
346
347/* Initialize SpGistState for working with the given index */
348void
350{
351 SpGistCache *cache;
352
353 state->index = index;
354
355 /* Get cached static information about index */
356 cache = spgGetCache(index);
357
358 state->config = cache->config;
359 state->attType = cache->attType;
360 state->attLeafType = cache->attLeafType;
361 state->attPrefixType = cache->attPrefixType;
362 state->attLabelType = cache->attLabelType;
363
364 /* Ensure we have a valid descriptor for leaf tuples */
365 state->leafTupDesc = getSpGistTupleDesc(state->index, &state->attLeafType);
366
367 /* Make workspace for constructing dead tuples */
368 state->deadTupleStorage = palloc0(SGDTSIZE);
369
370 /*
371 * Set horizon XID to use in redirection tuples. Use our own XID if we
372 * have one, else use InvalidTransactionId. The latter case can happen in
373 * VACUUM or REINDEX CONCURRENTLY, and in neither case would it be okay to
374 * force an XID to be assigned. VACUUM won't create any redirection
375 * tuples anyway, but REINDEX CONCURRENTLY can. Fortunately, REINDEX
376 * CONCURRENTLY doesn't mark the index valid until the end, so there could
377 * never be any concurrent scans "in flight" to a redirection tuple it has
378 * inserted. And it locks out VACUUM until the end, too. So it's okay
379 * for VACUUM to immediately expire a redirection tuple that contains an
380 * invalid xid.
381 */
382 state->redirectXid = GetTopTransactionIdIfAny();
383
384 /* Assume we're not in an index build (spgbuild will override) */
385 state->isBuild = false;
386}
387
388/*
389 * Allocate a new page (either by recycling, or by extending the index file).
390 *
391 * The returned buffer is already pinned and exclusive-locked.
392 * Caller is responsible for initializing the page by calling SpGistInitBuffer.
393 */
394Buffer
396{
397 Buffer buffer;
398
399 /* First, try to get a page from FSM */
400 for (;;)
401 {
403
404 if (blkno == InvalidBlockNumber)
405 break; /* nothing known to FSM */
406
407 /*
408 * The fixed pages shouldn't ever be listed in FSM, but just in case
409 * one is, ignore it.
410 */
411 if (SpGistBlockIsFixed(blkno))
412 continue;
413
414 buffer = ReadBuffer(index, blkno);
415
416 /*
417 * We have to guard against the possibility that someone else already
418 * recycled this page; the buffer may be locked if so.
419 */
420 if (ConditionalLockBuffer(buffer))
421 {
422 Page page = BufferGetPage(buffer);
423
424 if (PageIsNew(page))
425 return buffer; /* OK to use, if never initialized */
426
427 if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
428 return buffer; /* OK to use */
429
431 }
432
433 /* Can't use it, so release buffer and try again */
434 ReleaseBuffer(buffer);
435 }
436
439
440 return buffer;
441}
442
443/*
444 * Update index metapage's lastUsedPages info from local cache, if possible
445 *
446 * Updating meta page isn't critical for index working, so
447 * 1 use ConditionalLockBuffer to improve concurrency
448 * 2 don't WAL-log metabuffer changes to decrease WAL traffic
449 */
450void
452{
453 SpGistCache *cache = (SpGistCache *) index->rd_amcache;
454
455 if (cache != NULL)
456 {
457 Buffer metabuffer;
458
460
461 if (ConditionalLockBuffer(metabuffer))
462 {
463 Page metapage = BufferGetPage(metabuffer);
464 SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
465
466 metadata->lastUsedPages = cache->lastUsedPages;
467
468 /*
469 * Set pd_lower just past the end of the metadata. This is
470 * essential, because without doing so, metadata will be lost if
471 * xlog.c compresses the page. (We must do this here because
472 * pre-v11 versions of PG did not set the metapage's pd_lower
473 * correctly, so a pg_upgraded index might contain the wrong
474 * value.)
475 */
476 ((PageHeader) metapage)->pd_lower =
477 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
478
479 MarkBufferDirty(metabuffer);
480 UnlockReleaseBuffer(metabuffer);
481 }
482 else
483 {
484 ReleaseBuffer(metabuffer);
485 }
486 }
487}
488
489/* Macro to select proper element of lastUsedPages cache depending on flags */
490/* Masking flags with SPGIST_CACHED_PAGES is just for paranoia's sake */
491#define GET_LUP(c, f) (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])
492
493/*
494 * Allocate and initialize a new buffer of the type and parity specified by
495 * flags. The returned buffer is already pinned and exclusive-locked.
496 *
497 * When requesting an inner page, if we get one with the wrong parity,
498 * we just release the buffer and try again. We will get a different page
499 * because GetFreeIndexPage will have marked the page used in FSM. The page
500 * is entered in our local lastUsedPages cache, so there's some hope of
501 * making use of it later in this session, but otherwise we rely on VACUUM
502 * to eventually re-enter the page in FSM, making it available for recycling.
503 * Note that such a page does not get marked dirty here, so unless it's used
504 * fairly soon, the buffer will just get discarded and the page will remain
505 * as it was on disk.
506 *
507 * When we return a buffer to the caller, the page is *not* entered into
508 * the lastUsedPages cache; we expect the caller will do so after it's taken
509 * whatever space it will use. This is because after the caller has used up
510 * some space, the page might have less space than whatever was cached already
511 * so we'd rather not trash the old cache entry.
512 */
513static Buffer
515{
516 SpGistCache *cache = spgGetCache(index);
517 uint16 pageflags = 0;
518
519 if (GBUF_REQ_LEAF(flags))
520 pageflags |= SPGIST_LEAF;
521 if (GBUF_REQ_NULLS(flags))
522 pageflags |= SPGIST_NULLS;
523
524 for (;;)
525 {
526 Buffer buffer;
527
528 buffer = SpGistNewBuffer(index);
529 SpGistInitBuffer(buffer, pageflags);
530
531 if (pageflags & SPGIST_LEAF)
532 {
533 /* Leaf pages have no parity concerns, so just use it */
534 return buffer;
535 }
536 else
537 {
538 BlockNumber blkno = BufferGetBlockNumber(buffer);
539 int blkFlags = GBUF_INNER_PARITY(blkno);
540
541 if ((flags & GBUF_PARITY_MASK) == blkFlags)
542 {
543 /* Page has right parity, use it */
544 return buffer;
545 }
546 else
547 {
548 /* Page has wrong parity, record it in cache and try again */
549 if (pageflags & SPGIST_NULLS)
550 blkFlags |= GBUF_NULLS;
551 cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
552 cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
554 UnlockReleaseBuffer(buffer);
555 }
556 }
557 }
558}
559
560/*
561 * Get a buffer of the type and parity specified by flags, having at least
562 * as much free space as indicated by needSpace. We use the lastUsedPages
563 * cache to assign the same buffer previously requested when possible.
564 * The returned buffer is already pinned and exclusive-locked.
565 *
566 * *isNew is set true if the page was initialized here, false if it was
567 * already valid.
568 */
569Buffer
570SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
571{
572 SpGistCache *cache = spgGetCache(index);
574
575 /* Bail out if even an empty page wouldn't meet the demand */
576 if (needSpace > SPGIST_PAGE_CAPACITY)
577 elog(ERROR, "desired SPGiST tuple size is too big");
578
579 /*
580 * If possible, increase the space request to include relation's
581 * fillfactor. This ensures that when we add unrelated tuples to a page,
582 * we try to keep 100-fillfactor% available for adding tuples that are
583 * related to the ones already on it. But fillfactor mustn't cause an
584 * error for requests that would otherwise be legal.
585 */
587 needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
588
589 /* Get the cache entry for this flags setting */
590 lup = GET_LUP(cache, flags);
591
592 /* If we have nothing cached, just turn it over to allocNewBuffer */
593 if (lup->blkno == InvalidBlockNumber)
594 {
595 *isNew = true;
596 return allocNewBuffer(index, flags);
597 }
598
599 /* fixed pages should never be in cache */
601
602 /* If cached freeSpace isn't enough, don't bother looking at the page */
603 if (lup->freeSpace >= needSpace)
604 {
605 Buffer buffer;
606 Page page;
607
608 buffer = ReadBuffer(index, lup->blkno);
609
610 if (!ConditionalLockBuffer(buffer))
611 {
612 /*
613 * buffer is locked by another process, so return a new buffer
614 */
615 ReleaseBuffer(buffer);
616 *isNew = true;
617 return allocNewBuffer(index, flags);
618 }
619
620 page = BufferGetPage(buffer);
621
622 if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
623 {
624 /* OK to initialize the page */
625 uint16 pageflags = 0;
626
627 if (GBUF_REQ_LEAF(flags))
628 pageflags |= SPGIST_LEAF;
629 if (GBUF_REQ_NULLS(flags))
630 pageflags |= SPGIST_NULLS;
631 SpGistInitBuffer(buffer, pageflags);
632 lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
633 *isNew = true;
634 return buffer;
635 }
636
637 /*
638 * Check that page is of right type and has enough space. We must
639 * recheck this since our cache isn't necessarily up to date.
640 */
641 if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
643 {
644 int freeSpace = PageGetExactFreeSpace(page);
645
646 if (freeSpace >= needSpace)
647 {
648 /* Success, update freespace info and return the buffer */
649 lup->freeSpace = freeSpace - needSpace;
650 *isNew = false;
651 return buffer;
652 }
653 }
654
655 /*
656 * fallback to allocation of new buffer
657 */
658 UnlockReleaseBuffer(buffer);
659 }
660
661 /* No success with cache, so return a new buffer */
662 *isNew = true;
663 return allocNewBuffer(index, flags);
664}
665
666/*
667 * Update lastUsedPages cache when done modifying a page.
668 *
669 * We update the appropriate cache entry if it already contained this page
670 * (its freeSpace is likely obsolete), or if this page has more space than
671 * whatever we had cached.
672 */
673void
675{
676 SpGistCache *cache = spgGetCache(index);
678 int freeSpace;
679 Page page = BufferGetPage(buffer);
680 BlockNumber blkno = BufferGetBlockNumber(buffer);
681 int flags;
682
683 /* Never enter fixed pages (root pages) in cache, though */
684 if (SpGistBlockIsFixed(blkno))
685 return;
686
687 if (SpGistPageIsLeaf(page))
688 flags = GBUF_LEAF;
689 else
690 flags = GBUF_INNER_PARITY(blkno);
691 if (SpGistPageStoresNulls(page))
692 flags |= GBUF_NULLS;
693
694 lup = GET_LUP(cache, flags);
695
696 freeSpace = PageGetExactFreeSpace(page);
697 if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
698 lup->freeSpace < freeSpace)
699 {
700 lup->blkno = blkno;
701 lup->freeSpace = freeSpace;
702 }
703}
704
705/*
706 * Initialize an SPGiST page to empty, with specified flags
707 */
708void
710{
711 SpGistPageOpaque opaque;
712
713 PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
714 opaque = SpGistPageGetOpaque(page);
715 opaque->flags = f;
717}
718
719/*
720 * Initialize a buffer's page to empty, with specified flags
721 */
722void
724{
725 Assert(BufferGetPageSize(b) == BLCKSZ);
727}
728
729/*
730 * Initialize metadata page
731 */
732void
734{
735 SpGistMetaPageData *metadata;
736 int i;
737
739 metadata = SpGistPageGetMeta(page);
740 memset(metadata, 0, sizeof(SpGistMetaPageData));
742
743 /* initialize last-used-page cache to empty */
744 for (i = 0; i < SPGIST_CACHED_PAGES; i++)
746
747 /*
748 * Set pd_lower just past the end of the metadata. This is essential,
749 * because without doing so, metadata will be lost if xlog.c compresses
750 * the page.
751 */
752 ((PageHeader) page)->pd_lower =
753 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
754}
755
756/*
757 * reloptions processing for SPGiST
758 */
759bytea *
760spgoptions(Datum reloptions, bool validate)
761{
762 static const relopt_parse_elt tab[] = {
763 {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
764 };
765
766 return (bytea *) build_reloptions(reloptions, validate,
768 sizeof(SpGistOptions),
769 tab, lengthof(tab));
770}
771
772/*
773 * Get the space needed to store a non-null datum of the indicated type
774 * in an inner tuple (that is, as a prefix or node label).
775 * Note the result is already rounded up to a MAXALIGN boundary.
776 * Here we follow the convention that pass-by-val types are just stored
777 * in their Datum representation (compare memcpyInnerDatum).
778 */
779unsigned int
781{
782 unsigned int size;
783
784 if (att->attbyval)
785 size = sizeof(Datum);
786 else if (att->attlen > 0)
787 size = att->attlen;
788 else
789 size = VARSIZE_ANY(DatumGetPointer(datum));
790
791 return MAXALIGN(size);
792}
793
794/*
795 * Copy the given non-null datum to *target, in the inner-tuple case
796 */
797static void
798memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
799{
800 unsigned int size;
801
802 if (att->attbyval)
803 {
804 memcpy(target, &datum, sizeof(Datum));
805 }
806 else
807 {
808 size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(DatumGetPointer(datum));
809 memcpy(target, DatumGetPointer(datum), size);
810 }
811}
812
813/*
814 * Compute space required for a leaf tuple holding the given data.
815 *
816 * This must match the size-calculation portion of spgFormLeafTuple.
817 */
818Size
820 const Datum *datums, const bool *isnulls)
821{
822 Size size;
823 Size data_size;
824 bool needs_null_mask = false;
825 int natts = tupleDescriptor->natts;
826
827 /*
828 * Decide whether we need a nulls bitmask.
829 *
830 * If there is only a key attribute (natts == 1), never use a bitmask, for
831 * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
832 * need one if any attribute is null.
833 */
834 if (natts > 1)
835 {
836 for (int i = 0; i < natts; i++)
837 {
838 if (isnulls[i])
839 {
840 needs_null_mask = true;
841 break;
842 }
843 }
844 }
845
846 /*
847 * Calculate size of the data part; same as for heap tuples.
848 */
849 data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
850
851 /*
852 * Compute total size.
853 */
854 size = SGLTHDRSZ(needs_null_mask);
855 size += data_size;
856 size = MAXALIGN(size);
857
858 /*
859 * Ensure that we can replace the tuple with a dead tuple later. This test
860 * is unnecessary when there are any non-null attributes, but be safe.
861 */
862 if (size < SGDTSIZE)
863 size = SGDTSIZE;
864
865 return size;
866}
867
868/*
869 * Construct a leaf tuple containing the given heap TID and datum values
870 */
873 const Datum *datums, const bool *isnulls)
874{
875 SpGistLeafTuple tup;
876 TupleDesc tupleDescriptor = state->leafTupDesc;
877 Size size;
878 Size hoff;
879 Size data_size;
880 bool needs_null_mask = false;
881 int natts = tupleDescriptor->natts;
882 char *tp; /* ptr to tuple data */
883 uint16 tupmask = 0; /* unused heap_fill_tuple output */
884
885 /*
886 * Decide whether we need a nulls bitmask.
887 *
888 * If there is only a key attribute (natts == 1), never use a bitmask, for
889 * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
890 * need one if any attribute is null.
891 */
892 if (natts > 1)
893 {
894 for (int i = 0; i < natts; i++)
895 {
896 if (isnulls[i])
897 {
898 needs_null_mask = true;
899 break;
900 }
901 }
902 }
903
904 /*
905 * Calculate size of the data part; same as for heap tuples.
906 */
907 data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
908
909 /*
910 * Compute total size.
911 */
912 hoff = SGLTHDRSZ(needs_null_mask);
913 size = hoff + data_size;
914 size = MAXALIGN(size);
915
916 /*
917 * Ensure that we can replace the tuple with a dead tuple later. This test
918 * is unnecessary when there are any non-null attributes, but be safe.
919 */
920 if (size < SGDTSIZE)
921 size = SGDTSIZE;
922
923 /* OK, form the tuple */
924 tup = (SpGistLeafTuple) palloc0(size);
925
926 tup->size = size;
928 tup->heapPtr = *heapPtr;
929
930 tp = (char *) tup + hoff;
931
932 if (needs_null_mask)
933 {
934 bits8 *bp; /* ptr to null bitmap in tuple */
935
936 /* Set nullmask presence bit in SpGistLeafTuple header */
937 SGLT_SET_HASNULLMASK(tup, true);
938 /* Fill the data area and null mask */
939 bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
940 heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
941 &tupmask, bp);
942 }
943 else if (natts > 1 || !isnulls[spgKeyColumn])
944 {
945 /* Fill data area only */
946 heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
947 &tupmask, (bits8 *) NULL);
948 }
949 /* otherwise we have no data, nor a bitmap, to fill */
950
951 return tup;
952}
953
954/*
955 * Construct a node (to go into an inner tuple) containing the given label
956 *
957 * Note that the node's downlink is just set invalid here. Caller will fill
958 * it in later.
959 */
962{
963 SpGistNodeTuple tup;
964 unsigned int size;
965 unsigned short infomask = 0;
966
967 /* compute space needed (note result is already maxaligned) */
968 size = SGNTHDRSZ;
969 if (!isnull)
970 size += SpGistGetInnerTypeSize(&state->attLabelType, label);
971
972 /*
973 * Here we make sure that the size will fit in the field reserved for it
974 * in t_info.
975 */
976 if ((size & INDEX_SIZE_MASK) != size)
978 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
979 errmsg("index row requires %zu bytes, maximum size is %zu",
980 (Size) size, (Size) INDEX_SIZE_MASK)));
981
982 tup = (SpGistNodeTuple) palloc0(size);
983
984 if (isnull)
985 infomask |= INDEX_NULL_MASK;
986 /* we don't bother setting the INDEX_VAR_MASK bit */
987 infomask |= size;
988 tup->t_info = infomask;
989
990 /* The TID field will be filled in later */
992
993 if (!isnull)
994 memcpyInnerDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
995
996 return tup;
997}
998
999/*
1000 * Construct an inner tuple containing the given prefix and node array
1001 */
1003spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
1004 int nNodes, SpGistNodeTuple *nodes)
1005{
1006 SpGistInnerTuple tup;
1007 unsigned int size;
1008 unsigned int prefixSize;
1009 int i;
1010 char *ptr;
1011
1012 /* Compute size needed */
1013 if (hasPrefix)
1014 prefixSize = SpGistGetInnerTypeSize(&state->attPrefixType, prefix);
1015 else
1016 prefixSize = 0;
1017
1018 size = SGITHDRSZ + prefixSize;
1019
1020 /* Note: we rely on node tuple sizes to be maxaligned already */
1021 for (i = 0; i < nNodes; i++)
1022 size += IndexTupleSize(nodes[i]);
1023
1024 /*
1025 * Ensure that we can replace the tuple with a dead tuple later. This
1026 * test is unnecessary given current tuple layouts, but let's be safe.
1027 */
1028 if (size < SGDTSIZE)
1029 size = SGDTSIZE;
1030
1031 /*
1032 * Inner tuple should be small enough to fit on a page
1033 */
1034 if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
1035 ereport(ERROR,
1036 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1037 errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
1038 (Size) size,
1040 errhint("Values larger than a buffer page cannot be indexed.")));
1041
1042 /*
1043 * Check for overflow of header fields --- probably can't fail if the
1044 * above succeeded, but let's be paranoid
1045 */
1046 if (size > SGITMAXSIZE ||
1047 prefixSize > SGITMAXPREFIXSIZE ||
1048 nNodes > SGITMAXNNODES)
1049 elog(ERROR, "SPGiST inner tuple header field is too small");
1050
1051 /* OK, form the tuple */
1052 tup = (SpGistInnerTuple) palloc0(size);
1053
1054 tup->nNodes = nNodes;
1055 tup->prefixSize = prefixSize;
1056 tup->size = size;
1057
1058 if (hasPrefix)
1059 memcpyInnerDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
1060
1061 ptr = (char *) SGITNODEPTR(tup);
1062
1063 for (i = 0; i < nNodes; i++)
1064 {
1065 SpGistNodeTuple node = nodes[i];
1066
1067 memcpy(ptr, node, IndexTupleSize(node));
1068 ptr += IndexTupleSize(node);
1069 }
1070
1071 return tup;
1072}
1073
1074/*
1075 * Construct a "dead" tuple to replace a tuple being deleted.
1076 *
1077 * The state can be SPGIST_REDIRECT, SPGIST_DEAD, or SPGIST_PLACEHOLDER.
1078 * For a REDIRECT tuple, a pointer (blkno+offset) must be supplied, and
1079 * the xid field is filled in automatically.
1080 *
1081 * This is called in critical sections, so we don't use palloc; the tuple
1082 * is built in preallocated storage. It should be copied before another
1083 * call with different parameters can occur.
1084 */
1087 BlockNumber blkno, OffsetNumber offnum)
1088{
1089 SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
1090
1091 tuple->tupstate = tupstate;
1092 tuple->size = SGDTSIZE;
1094
1095 if (tupstate == SPGIST_REDIRECT)
1096 {
1097 ItemPointerSet(&tuple->pointer, blkno, offnum);
1098 tuple->xid = state->redirectXid;
1099 }
1100 else
1101 {
1103 tuple->xid = InvalidTransactionId;
1104 }
1105
1106 return tuple;
1107}
1108
1109/*
1110 * Convert an SPGiST leaf tuple into Datum/isnull arrays.
1111 *
1112 * The caller must allocate sufficient storage for the output arrays.
1113 * (INDEX_MAX_KEYS entries should be enough.)
1114 */
1115void
1117 Datum *datums, bool *isnulls, bool keyColumnIsNull)
1118{
1119 bool hasNullsMask = SGLT_GET_HASNULLMASK(tup);
1120 char *tp; /* ptr to tuple data */
1121 bits8 *bp; /* ptr to null bitmap in tuple */
1122
1123 if (keyColumnIsNull && tupleDescriptor->natts == 1)
1124 {
1125 /*
1126 * Trivial case: there is only the key attribute and we're in a nulls
1127 * tree. The hasNullsMask bit in the tuple header should not be set
1128 * (and thus we can't use index_deform_tuple_internal), but
1129 * nonetheless the result is NULL.
1130 *
1131 * Note: currently this is dead code, because noplace calls this when
1132 * there is only the key attribute. But we should cover the case.
1133 */
1134 Assert(!hasNullsMask);
1135
1136 datums[spgKeyColumn] = (Datum) 0;
1137 isnulls[spgKeyColumn] = true;
1138 return;
1139 }
1140
1141 tp = (char *) tup + SGLTHDRSZ(hasNullsMask);
1142 bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
1143
1144 index_deform_tuple_internal(tupleDescriptor,
1145 datums, isnulls,
1146 tp, bp, hasNullsMask);
1147
1148 /*
1149 * Key column isnull value from the tuple should be consistent with
1150 * keyColumnIsNull flag from the caller.
1151 */
1152 Assert(keyColumnIsNull == isnulls[spgKeyColumn]);
1153}
1154
1155/*
1156 * Extract the label datums of the nodes within innerTuple
1157 *
1158 * Returns NULL if label datums are NULLs
1159 */
1160Datum *
1162{
1163 Datum *nodeLabels;
1164 int i;
1165 SpGistNodeTuple node;
1166
1167 /* Either all the labels must be NULL, or none. */
1168 node = SGITNODEPTR(innerTuple);
1169 if (IndexTupleHasNulls(node))
1170 {
1171 SGITITERATE(innerTuple, i, node)
1172 {
1173 if (!IndexTupleHasNulls(node))
1174 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1175 }
1176 /* They're all null, so just return NULL */
1177 return NULL;
1178 }
1179 else
1180 {
1181 nodeLabels = palloc_array(Datum, innerTuple->nNodes);
1182 SGITITERATE(innerTuple, i, node)
1183 {
1184 if (IndexTupleHasNulls(node))
1185 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1186 nodeLabels[i] = SGNTDATUM(node, state);
1187 }
1188 return nodeLabels;
1189 }
1190}
1191
1192/*
1193 * Add a new item to the page, replacing a PLACEHOLDER item if possible.
1194 * Return the location it's inserted at, or InvalidOffsetNumber on failure.
1195 *
1196 * If startOffset isn't NULL, we start searching for placeholders at
1197 * *startOffset, and update that to the next place to search. This is just
1198 * an optimization for repeated insertions.
1199 *
1200 * If errorOK is false, we throw error when there's not enough room,
1201 * rather than returning InvalidOffsetNumber.
1202 */
1204SpGistPageAddNewItem(SpGistState *state, Page page, const void *item, Size size,
1205 OffsetNumber *startOffset, bool errorOK)
1206{
1209 maxoff,
1210 offnum;
1211
1212 if (opaque->nPlaceholder > 0 &&
1213 PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
1214 {
1215 /* Try to replace a placeholder */
1216 maxoff = PageGetMaxOffsetNumber(page);
1217 offnum = InvalidOffsetNumber;
1218
1219 for (;;)
1220 {
1221 if (startOffset && *startOffset != InvalidOffsetNumber)
1222 i = *startOffset;
1223 else
1225 for (; i <= maxoff; i++)
1226 {
1228 PageGetItemId(page, i));
1229
1230 if (it->tupstate == SPGIST_PLACEHOLDER)
1231 {
1232 offnum = i;
1233 break;
1234 }
1235 }
1236
1237 /* Done if we found a placeholder */
1238 if (offnum != InvalidOffsetNumber)
1239 break;
1240
1241 if (startOffset && *startOffset != InvalidOffsetNumber)
1242 {
1243 /* Hint was no good, re-search from beginning */
1244 *startOffset = InvalidOffsetNumber;
1245 continue;
1246 }
1247
1248 /* Hmm, no placeholder found? */
1249 opaque->nPlaceholder = 0;
1250 break;
1251 }
1252
1253 if (offnum != InvalidOffsetNumber)
1254 {
1255 /* Replace the placeholder tuple */
1256 PageIndexTupleDelete(page, offnum);
1257
1258 offnum = PageAddItem(page, item, size, offnum, false, false);
1259
1260 /*
1261 * We should not have failed given the size check at the top of
1262 * the function, but test anyway. If we did fail, we must PANIC
1263 * because we've already deleted the placeholder tuple, and
1264 * there's no other way to keep the damage from getting to disk.
1265 */
1266 if (offnum != InvalidOffsetNumber)
1267 {
1268 Assert(opaque->nPlaceholder > 0);
1269 opaque->nPlaceholder--;
1270 if (startOffset)
1271 *startOffset = offnum + 1;
1272 }
1273 else
1274 elog(PANIC, "failed to add item of size %zu to SPGiST index page",
1275 size);
1276
1277 return offnum;
1278 }
1279 }
1280
1281 /* No luck in replacing a placeholder, so just add it to the page */
1282 offnum = PageAddItem(page, item, size,
1283 InvalidOffsetNumber, false, false);
1284
1285 if (offnum == InvalidOffsetNumber && !errorOK)
1286 elog(ERROR, "failed to add item of size %zu to SPGiST index page",
1287 size);
1288
1289 return offnum;
1290}
1291
1292/*
1293 * spgproperty() -- Check boolean properties of indexes.
1294 *
1295 * This is optional for most AMs, but is required for SP-GiST because the core
1296 * property code doesn't support AMPROP_DISTANCE_ORDERABLE.
1297 */
1298bool
1299spgproperty(Oid index_oid, int attno,
1300 IndexAMProperty prop, const char *propname,
1301 bool *res, bool *isnull)
1302{
1303 Oid opclass,
1304 opfamily,
1305 opcintype;
1306 CatCList *catlist;
1307 int i;
1308
1309 /* Only answer column-level inquiries */
1310 if (attno == 0)
1311 return false;
1312
1313 switch (prop)
1314 {
1316 break;
1317 default:
1318 return false;
1319 }
1320
1321 /*
1322 * Currently, SP-GiST distance-ordered scans require that there be a
1323 * distance operator in the opclass with the default types. So we assume
1324 * that if such an operator exists, then there's a reason for it.
1325 */
1326
1327 /* First we need to know the column's opclass. */
1328 opclass = get_index_column_opclass(index_oid, attno);
1329 if (!OidIsValid(opclass))
1330 {
1331 *isnull = true;
1332 return true;
1333 }
1334
1335 /* Now look up the opclass family and input datatype. */
1336 if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1337 {
1338 *isnull = true;
1339 return true;
1340 }
1341
1342 /* And now we can check whether the operator is provided. */
1343 catlist = SearchSysCacheList1(AMOPSTRATEGY,
1344 ObjectIdGetDatum(opfamily));
1345
1346 *res = false;
1347
1348 for (i = 0; i < catlist->n_members; i++)
1349 {
1350 HeapTuple amoptup = &catlist->members[i]->tuple;
1351 Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1352
1353 if (amopform->amoppurpose == AMOP_ORDER &&
1354 (amopform->amoplefttype == opcintype ||
1355 amopform->amoprighttype == opcintype) &&
1356 opfamily_can_sort_type(amopform->amopsortfamily,
1357 get_op_rettype(amopform->amopopr)))
1358 {
1359 *res = true;
1360 break;
1361 }
1362 }
1363
1364 ReleaseSysCacheList(catlist);
1365
1366 *isnull = false;
1367
1368 return true;
1369}
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:4318
void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition: bufmgr.c:5699
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:939
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:5725
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5461
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5478
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:3037
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:839
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:436
@ BUFFER_LOCK_SHARE
Definition: bufmgr.h:206
@ BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:205
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:425
@ 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:223
PageHeaderData * PageHeader
Definition: bufpage.h:173
static bool PageIsNew(const PageData *page)
Definition: bufpage.h:233
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
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:471
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:371
#define Min(x, y)
Definition: c.h:995
#define MAXALIGN(LEN)
Definition: c.h:824
uint8 bits8
Definition: c.h:559
uint16_t uint16
Definition: c.h:551
#define lengthof(array)
Definition: c.h:801
#define OidIsValid(objectId)
Definition: c.h:788
size_t Size
Definition: c.h:624
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#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:1150
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
Assert(PointerIsAligned(start, uint64))
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)
Definition: htup_details.h:728
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:479
int b
Definition: isn.c:74
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 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:1337
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1483
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3662
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2671
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:989
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
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
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
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
static int fillfactor
Definition: pgbench.c:188
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetDescr(relation)
Definition: rel.h:541
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:527
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5092
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1954
@ RELOPT_KIND_SPGIST
Definition: reloptions.h:50
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
@ 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:8246
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 spgFirstIncludeColumn
#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)
struct SpGistLeafTupleData SpGistLeafTupleData
#define SPGIST_LEAF
#define spgKeyColumn
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition: spgscan.c:304
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1077
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:1020
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:423
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
OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page, const void *item, Size size, OffsetNumber *startOffset, bool errorOK)
Definition: spgutils.c:1204
Datum * spgExtractNodeLabels(SpGistState *state, SpGistInnerTuple innerTuple)
Definition: spgutils.c:1161
void initSpGistState(SpGistState *state, Relation index)
Definition: spgutils.c:349
void SpGistUpdateMetaPage(Relation index)
Definition: spgutils.c:451
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Definition: spgutils.c:316
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:395
SpGistLeafTuple spgFormLeafTuple(SpGistState *state, const ItemPointerData *heapPtr, const Datum *datums, const bool *isnulls)
Definition: spgutils.c:872
SpGistInnerTuple spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
Definition: spgutils.c:1003
static void memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:798
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:189
void spgDeformLeafTuple(SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
Definition: spgutils.c:1116
SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
Definition: spgutils.c:1086
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:780
static Oid GetIndexInputType(Relation index, AttrNumber indexcol)
Definition: spgutils.c:122
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:723
Buffer SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
Definition: spgutils.c:570
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:760
static Buffer allocNewBuffer(Relation index, int flags)
Definition: spgutils.c:514
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:1299
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
Definition: spgutils.c:674
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:819
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:709
#define GET_LUP(c, f)
Definition: spgutils.c:491
SpGistNodeTuple spgFormNodeTuple(SpGistState *state, Datum label, bool isnull)
Definition: spgutils.c:961
void SpGistInitMetapage(Page page)
Definition: spgutils.c:733
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)
Definition: spgvalidate.c:323
int32 attcacheoff
Definition: tupdesc.h:70
Definition: fmgr.h:57
NodeTag type
Definition: amapi.h:234
ItemPointerData t_tid
Definition: itup.h:37
unsigned short t_info
Definition: itup.h:49
Definition: pg_list.h:54
Definition: nodes.h:135
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
SpGistLUPCache lastUsedPages
spgConfigOut config
SpGistTypeDesc attLabelType
unsigned int tupstate
ItemPointerData pointer
unsigned int prefixSize
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
ItemPointerData heapPtr
SpGistLUPCache lastUsedPages
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:182
int n_members
Definition: catcache.h:180
HeapTupleData tuple
Definition: catcache.h:124
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: regguts.h:323
Definition: c.h:706
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:264
HeapTuple SearchSysCache1(int 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
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:252
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
#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:442