1/*-------------------------------------------------------------------------
4 * Private declarations for SP-GiST access method.
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/include/access/spgist_private.h
12 *-------------------------------------------------------------------------
14#ifndef SPGIST_PRIVATE_H
15#define SPGIST_PRIVATE_H
19#include "catalog/pg_am_d.h"
29 int fillfactor;
/* page fill factor in percent (0..100) */
32 #define SpGistGetFillFactor(relation) \
33 (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \
34 relation->rd_rel->relam == SPGIST_AM_OID), \
35 (relation)->rd_options ? \
36 ((SpGistOptions *) (relation)->rd_options)->fillfactor : \
37 SPGIST_DEFAULT_FILLFACTOR)
38 #define SpGistGetTargetPageFreeSpace(relation) \
39 (BLCKSZ * (100 - SpGistGetFillFactor(relation)) / 100)
42/* SPGiST leaf tuples have one key column, optionally have included columns */
43 #define spgKeyColumn 0
44 #define spgFirstIncludeColumn 1
46/* Page numbers of fixed-location pages */
47 #define SPGIST_METAPAGE_BLKNO (0) /* metapage */
48 #define SPGIST_ROOT_BLKNO (1) /* root for normal entries */
49 #define SPGIST_NULL_BLKNO (2) /* root for null-value entries */
50 #define SPGIST_LAST_FIXED_BLKNO SPGIST_NULL_BLKNO
52 #define SpGistBlockIsRoot(blkno) \
53 ((blkno) == SPGIST_ROOT_BLKNO || (blkno) == SPGIST_NULL_BLKNO)
54 #define SpGistBlockIsFixed(blkno) \
55 ((BlockNumber) (blkno) <= (BlockNumber) SPGIST_LAST_FIXED_BLKNO)
58 * Contents of page special space on SPGiST index pages
65 /* note there's no count of either LIVE or DEAD tuples ... */
71/* Flag bits in page special space */
72 #define SPGIST_META (1<<0)
73 #define SPGIST_DELETED (1<<1) /* never set, but keep for backwards
75 #define SPGIST_LEAF (1<<2)
76#define SPGIST_NULLS (1<<3)
78 #define SpGistPageGetOpaque(page) ((SpGistPageOpaque) PageGetSpecialPointer(page))
79 #define SpGistPageIsMeta(page) (SpGistPageGetOpaque(page)->flags & SPGIST_META)
80 #define SpGistPageIsDeleted(page) (SpGistPageGetOpaque(page)->flags & SPGIST_DELETED)
81 #define SpGistPageIsLeaf(page) (SpGistPageGetOpaque(page)->flags & SPGIST_LEAF)
82#define SpGistPageStoresNulls(page) (SpGistPageGetOpaque(page)->flags & SPGIST_NULLS)
85 * The page ID is for the convenience of pg_filedump and similar utilities,
86 * which otherwise would have a hard time telling pages of different index
87 * types apart. It should be the last 2 bytes on the page. This is more or
88 * less "free" due to alignment considerations.
90 * See comments above GinPageOpaqueData.
92#define SPGIST_PAGE_ID 0xFF82
95 * Each backend keeps a cache of last-used page info in its index->rd_amcache
96 * area. This is initialized from, and occasionally written back to,
97 * shared storage in the index metapage.
102 int freeSpace;
/* page's free space (could be obsolete!) */
105 /* Note: indexes in cachedPage[] match flag assignments for SpGistGetBuffer */
106#define SPGIST_CACHED_PAGES 8
122#define SPGIST_MAGIC_NUMBER (0xBA0BABEE)
124#define SpGistPageGetMeta(p) \
125 ((SpGistMetaPageData *) PageGetContents(p))
128 * Private state of index AM. SpGistState is common to both insert and
129 * search code; SpGistScanOpaque is for searches only.
134 /* Per-datatype info needed in SpGistState */
155 /* leafTupDesc typically points to index's tupdesc, but not always */
164 /* Item to be re-examined later during a search */
168 Datum value;
/* value reconstructed from parent, or
169 * leafValue if isLeaf */
172 int level;
/* level of items on this page */
174 bool isNull;
/* SearchItem is NULL item */
175 bool isLeaf;
/* SearchItem is heap item */
179 /* array with numberOfOrderBys entries */
183#define SizeOfSpGistSearchItem(n_distances) \
184 (offsetof(SpGistSearchItem, distances) + sizeof(double) * (n_distances))
187 * Private state of an index scan
196 /* Control flags showing whether to search nulls and/or non-nulls */
200 /* Index quals to be passed to opclass (null-related quals removed) */
205 * with non-NULL arguments */
209 * ordering keys in the original array */
212 /* Opclass defined functions: */
216 /* Pre-allocated workspace arrays: */
220 /* These fields are only used in amgetbitmap scans: */
222 int64 ntids;
/* number of TIDs passed to bitmap */
224 /* These fields are only used in amgettuple scans: */
227 int nPtrs;
/* number of TIDs found on current page */
228 int iPtr;
/* index for scanning through same */
235 /* distances (for recheck) */
239 * Note: using MaxIndexTuplesPerPage above is a bit hokey since
240 * SpGistLeafTuples aren't exactly IndexTuples; however, they are larger,
248 * This struct is what we actually keep in index->rd_amcache. It includes
249 * static configuration information as well as the lastUsedPages cache.
265 * SPGiST tuple types. Note: inner, leaf, and dead tuple structs
266 * must have the same tupstate field in the same position! Real inner and
267 * leaf tuples always have tupstate = LIVE; if the state is something else,
268 * use the SpGistDeadTuple struct to inspect the tuple.
271 /* values of tupstate (see README for more info) */
272 #define SPGIST_LIVE 0 /* normal live tuple (either inner or leaf) */
273 #define SPGIST_REDIRECT 1 /* temporary redirection placeholder */
274 #define SPGIST_DEAD 2 /* dead, cannot be removed because of links */
275#define SPGIST_PLACEHOLDER 3 /* placeholder, used to preserve offsets */
278 * SPGiST inner tuple: list of "nodes" that subdivide a set of tuples
280 * Inner tuple layout:
281 * header/optional prefix/array of nodes, which are SpGistNodeTuples
283 * size and prefixSize must be multiples of MAXALIGN
285 * If the prefix datum is of a pass-by-value type, it is stored in its
286 * Datum representation, that is its on-disk representation is of length
287 * sizeof(Datum). This is a fairly unfortunate choice, because in no other
288 * place does Postgres use Datum as an on-disk representation. Formerly it
289 * meant an unnecessary incompatibility between 32-bit and 64-bit builds, and
290 * as of v19 it instead creates a hazard for binary upgrades on 32-bit builds.
291 * Fortunately, that hazard seems mostly theoretical for lack of affected
292 * opclasses. Going forward, we will be using a fixed size of Datum so that
293 * there's no longer any pressing reason to change this.
297 unsigned int tupstate:2,
/* LIVE/REDIRECT/DEAD/PLACEHOLDER */
299 nNodes:13,
/* number of nodes within inner tuple */
302 /* On most machines there will be a couple of wasted bytes here */
303 /* prefix datum follows, then nodes */
308 /* these must match largest values that fit in bit fields declared above */
309 #define SGITMAXNNODES 0x1FFF
310 #define SGITMAXPREFIXSIZE 0xFFFF
311#define SGITMAXSIZE 0xFFFF
313 #define SGITHDRSZ MAXALIGN(sizeof(SpGistInnerTupleData))
314 #define _SGITDATA(x) (((char *) (x)) + SGITHDRSZ)
315 #define SGITDATAPTR(x) ((x)->prefixSize ? _SGITDATA(x) : NULL)
316#define SGITDATUM(x, s) ((x)->prefixSize ? \
317 ((s)->attPrefixType.attbyval ? \
318 *(Datum *) _SGITDATA(x) : \
319 PointerGetDatum(_SGITDATA(x))) \
321#define SGITNODEPTR(x) ((SpGistNodeTuple) (_SGITDATA(x) + (x)->prefixSize))
323 /* Macro for iterating through the nodes of an inner tuple */
324#define SGITITERATE(x, i, nt) \
325 for ((i) = 0, (nt) = SGITNODEPTR(x); \
327 (i)++, (nt) = (SpGistNodeTuple) (((char *) (nt)) + IndexTupleSize(nt)))
330 * SPGiST node tuple: one node within an inner tuple
332 * Node tuples use the same header as ordinary Postgres IndexTuples, but
333 * we do not use a null bitmap, because we know there is only one column
334 * so the INDEX_NULL_MASK bit suffices. Also, pass-by-value datums are
335 * stored in Datum form, the same convention as for inner tuple prefixes.
342 #define SGNTHDRSZ MAXALIGN(sizeof(SpGistNodeTupleData))
343 #define SGNTDATAPTR(x) (((char *) (x)) + SGNTHDRSZ)
344#define SGNTDATUM(x, s) ((s)->attLabelType.attbyval ? \
345 *(Datum *) SGNTDATAPTR(x) : \
346 PointerGetDatum(SGNTDATAPTR(x)))
349 * SPGiST leaf tuple: carries a leaf datum and a heap tuple TID,
350 * and optionally some "included" columns.
352 * In the simplest case, the leaf datum is the same as the indexed value;
353 * but it could also be a suffix or some other sort of delta that permits
354 * reconstruction given knowledge of the prefix path traversed to get here.
355 * Any included columns are stored without modification.
357 * A nulls bitmap is present if there are included columns AND any of the
358 * datums are NULL. We do not need a nulls bitmap for the case of a null
359 * leaf datum without included columns, as we can infer whether the leaf
360 * datum is null from whether the tuple is stored on a nulls page. (This
361 * provision is mostly for backwards compatibility, but it does save space
362 * on 32-bit machines.) As with other PG index tuple designs, if the nulls
363 * bitmap exists then it's of size INDEX_MAX_KEYS bits regardless of the
364 * actual number of attributes. For the usual choice of INDEX_MAX_KEYS,
365 * this costs nothing because of alignment considerations.
367 * The size field is wider than could possibly be needed for an on-disk leaf
368 * tuple, but this allows us to form leaf tuples even when the datum is too
369 * wide to be stored immediately, and it costs nothing because of alignment
372 * t_info holds the nextOffset field (14 bits wide, enough for supported
373 * page sizes) plus the has-nulls-bitmap flag bit; another flag bit is free.
375 * Normally, nextOffset links to the next tuple belonging to the same parent
376 * node (which must be on the same page), or it's 0 if there is no next tuple.
377 * But when the root page is a leaf page, we don't chain its tuples,
378 * so nextOffset is always 0 on the root.
380 * size must be a multiple of MAXALIGN; also, it must be at least SGDTSIZE
381 * so that the tuple can be converted to REDIRECT status later. (This
382 * restriction only adds bytes for a NULL leaf datum; otherwise alignment
383 * restrictions force it anyway.)
387 unsigned int tupstate:2,
/* LIVE/REDIRECT/DEAD/PLACEHOLDER */
388 size:30;
/* large enough for any palloc'able value */
389 uint16 t_info;
/* nextOffset, which links to the next tuple
390 * in chain, plus two flag bits */
392 /* nulls bitmap follows if the flag bit for it is set */
393 /* leaf datum, then any included datums, follows on a MAXALIGN boundary */
396 /* Macros to access nextOffset and bit fields inside t_info */
397#define SGLT_GET_NEXTOFFSET(spgLeafTuple) \
398 ((spgLeafTuple)->t_info & 0x3FFF)
399#define SGLT_GET_HASNULLMASK(spgLeafTuple) \
400 (((spgLeafTuple)->t_info & 0x8000) ? true : false)
401#define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber) \
402 ((spgLeafTuple)->t_info = \
403 ((spgLeafTuple)->t_info & 0xC000) | ((offsetNumber) & 0x3FFF))
404#define SGLT_SET_HASNULLMASK(spgLeafTuple, hasnulls) \
405 ((spgLeafTuple)->t_info = \
406 ((spgLeafTuple)->t_info & 0x7FFF) | ((hasnulls) ? 0x8000 : 0))
408#define SGLTHDRSZ(hasnulls) \
409 ((hasnulls) ? MAXALIGN(sizeof(SpGistLeafTupleData) + \
410 sizeof(IndexAttributeBitMapData)) : \
411 MAXALIGN(sizeof(SpGistLeafTupleData)))
412 #define SGLTDATAPTR(x) (((char *) (x)) + SGLTHDRSZ(SGLT_GET_HASNULLMASK(x)))
413#define SGLTDATUM(x, s) fetch_att(SGLTDATAPTR(x), \
414 (s)->attLeafType.attbyval, \
415 (s)->attLeafType.attlen)
418 * SPGiST dead tuple: declaration for examining non-live tuples
420 * The tupstate field of this struct must match those of regular inner and
421 * leaf tuples, and its size field must match a leaf tuple's.
422 * Also, the pointer field must be in the same place as a leaf tuple's heapPtr
423 * field, to satisfy some Asserts that we make when replacing a leaf tuple
425 * We don't use t_info, but it's needed to align the pointer field.
426 * pointer and xid are only valid when tupstate = REDIRECT, and in some
427 * cases xid can be InvalidTransactionId even then; see initSpGistState.
431 unsigned int tupstate:2,
/* LIVE/REDIRECT/DEAD/PLACEHOLDER */
440#define SGDTSIZE MAXALIGN(sizeof(SpGistDeadTupleData))
443 * Macros for doing free-space calculations. Note that when adding up the
444 * space needed for tuples, we always consider each tuple to need the tuple's
445 * size plus sizeof(ItemIdData) (for the line pointer). This works correctly
446 * so long as tuple sizes are always maxaligned.
449 /* Page capacity after allowing for fixed header and special space */
450#define SPGIST_PAGE_CAPACITY \
451 MAXALIGN_DOWN(BLCKSZ - \
452 SizeOfPageHeaderData - \
453 MAXALIGN(sizeof(SpGistPageOpaqueData)))
456 * Compute free space on page, assuming that up to n placeholders can be
457 * recycled if present (n should be the number of tuples to be inserted)
459#define SpGistPageGetFreeSpace(p, n) \
460 (PageGetExactFreeSpace(p) + \
461 Min(SpGistPageGetOpaque(p)->nPlaceholder, n) * \
462 (SGDTSIZE + sizeof(ItemIdData)))
468#define STORE_STATE(s, d) \
470 (d).redirectXid = (s)->redirectXid; \
471 (d).isBuild = (s)->isBuild; \
475 * The "flags" argument for SpGistGetBuffer should be either GBUF_LEAF to
476 * get a leaf page, or GBUF_INNER_PARITY(blockNumber) to get an inner
477 * page in the same triple-parity group as the specified block number.
478 * (Typically, this should be GBUF_INNER_PARITY(parentBlockNumber + 1)
479 * to follow the rule described in spgist/README.)
480 * In addition, GBUF_NULLS can be OR'd in to get a page for storage of
481 * null-valued tuples.
483 * Note: these flag values are used as indexes into lastUsedPages.
485 #define GBUF_LEAF 0x03
486 #define GBUF_INNER_PARITY(x) ((x) % 3)
487#define GBUF_NULLS 0x04
489 #define GBUF_PARITY_MASK 0x03
490 #define GBUF_REQ_LEAF(flags) (((flags) & GBUF_PARITY_MASK) == GBUF_LEAF)
491#define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS)
495 /* reloption parameters */
496 #define SPGIST_MIN_FILLFACTOR 10
497#define SPGIST_DEFAULT_FILLFACTOR 80
505 int needSpace,
bool *isNew);
512 const Datum *datums,
const bool *isnulls);
515 const Datum *datums,
const bool *isnulls);
519 bool hasPrefix,
Datum prefix,
524 Datum *datums,
bool *isnulls,
525 bool keyColumnIsNull);
534 bool *res,
bool *isnull);
541 int firststate,
int reststate,
548 ScanKey orderbys,
int norderbys);
551#endif /* SPGIST_PRIVATE_H */
#define FLEXIBLE_ARRAY_MEMBER
#define MaxIndexTuplesPerPage
BOX * box_copy(BOX *orig)
SpGistDeadTupleData * SpGistDeadTuple
Datum * spgExtractNodeLabels(SpGistState *state, SpGistInnerTuple innerTuple)
void initSpGistState(SpGistState *state, Relation index)
struct SpGistPageOpaqueData SpGistPageOpaqueData
void SpGistUpdateMetaPage(Relation index)
SpGistPageOpaqueData * SpGistPageOpaque
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Buffer SpGistNewBuffer(Relation index)
SpGistInnerTupleData * SpGistInnerTuple
struct SpGistDeadTupleData SpGistDeadTupleData
SpGistInnerTuple spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
SpGistLeafTuple spgFormLeafTuple(SpGistState *state, ItemPointer heapPtr, const Datum *datums, const bool *isnulls)
bool spgdoinsert(Relation index, SpGistState *state, ItemPointer heapPtr, Datum *datums, bool *isnulls)
SpGistCache * spgGetCache(Relation index)
void spgPageIndexMultiDelete(SpGistState *state, Page page, OffsetNumber *itemnos, int nitems, int firststate, int reststate, BlockNumber blkno, OffsetNumber offnum)
struct SpGistState SpGistState
void spgDeformLeafTuple(SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
IndexTupleData SpGistNodeTupleData
SpGistScanOpaqueData * SpGistScanOpaque
struct SpGistInnerTupleData SpGistInnerTupleData
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
struct SpGistOptions SpGistOptions
struct SpGistCache SpGistCache
void SpGistInitBuffer(Buffer b, uint16 f)
void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN, BlockNumber blkno, OffsetNumber offset)
double * spg_key_orderbys_distances(Datum key, bool isLeaf, ScanKey orderbys, int norderbys)
SpGistNodeTupleData * SpGistNodeTuple
struct SpGistMetaPageData SpGistMetaPageData
struct SpGistLastUsedPage SpGistLastUsedPage
Buffer SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
struct SpGistLeafTupleData * SpGistLeafTuple
#define SPGIST_CACHED_PAGES
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
struct SpGistScanOpaqueData SpGistScanOpaqueData
OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size, OffsetNumber *startOffset, bool errorOK)
struct SpGistTypeDesc SpGistTypeDesc
struct SpGistSearchItem SpGistSearchItem
Size SpGistGetLeafTupleSize(TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
void SpGistInitPage(Page page, uint16 f)
struct SpGistLeafTupleData SpGistLeafTupleData
SpGistNodeTuple spgFormNodeTuple(SpGistState *state, Datum label, bool isnull)
void SpGistInitMetapage(Page page)
struct SpGistLUPCache SpGistLUPCache
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistLUPCache lastUsedPages
SpGistTypeDesc attLabelType
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
SpGistLUPCache lastUsedPages
HeapTuple reconTups[MaxIndexTuplesPerPage]
ItemPointerData heapPtrs[MaxIndexTuplesPerPage]
MemoryContext traversalCxt
int * nonNullOrderByOffsets
int numberOfNonNullOrderBys
bool recheckDistances[MaxIndexTuplesPerPage]
FmgrInfo leafConsistentFn
IndexOrderByDistance * distances[MaxIndexTuplesPerPage]
bool recheck[MaxIndexTuplesPerPage]
FmgrInfo innerConsistentFn
SpGistLeafTuple leafTuple
double distances[FLEXIBLE_ARRAY_MEMBER]
SpGistTypeDesc attPrefixType
TransactionId redirectXid
SpGistTypeDesc attLabelType
SpGistTypeDesc attLeafType