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