123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- /*-------------------------------------------------------------------------
- *
- * gist.h
- * The public API for GiST indexes. This API is exposed to
- * individuals implementing GiST indexes, so backward-incompatible
- * changes should be made with care.
- *
- *
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/access/gist.h
- *
- *-------------------------------------------------------------------------
- */
- #ifndef GIST_H
- #define GIST_H
- #include "access/itup.h"
- #include "access/transam.h"
- #include "access/xlog.h"
- #include "access/xlogdefs.h"
- #include "storage/block.h"
- #include "storage/bufpage.h"
- #include "utils/relcache.h"
- /*
- * amproc indexes for GiST indexes.
- */
- #define GIST_CONSISTENT_PROC 1
- #define GIST_UNION_PROC 2
- #define GIST_COMPRESS_PROC 3
- #define GIST_DECOMPRESS_PROC 4
- #define GIST_PENALTY_PROC 5
- #define GIST_PICKSPLIT_PROC 6
- #define GIST_EQUAL_PROC 7
- #define GIST_DISTANCE_PROC 8
- #define GIST_FETCH_PROC 9
- #define GIST_OPTIONS_PROC 10
- #define GIST_SORTSUPPORT_PROC 11
- #define GISTNProcs 11
- /*
- * Page opaque data in a GiST index page.
- */
- #define F_LEAF (1 << 0) /* leaf page */
- #define F_DELETED (1 << 1) /* the page has been deleted */
- #define F_TUPLES_DELETED (1 << 2) /* some tuples on the page were
- * deleted */
- #define F_FOLLOW_RIGHT (1 << 3) /* page to the right has no downlink */
- #define F_HAS_GARBAGE (1 << 4) /* some tuples on the page are dead,
- * but not deleted yet */
- /*
- * NSN (node sequence number) is a special-purpose LSN which is stored on each
- * index page in GISTPageOpaqueData and updated only during page splits. By
- * recording the parent's LSN in GISTSearchItem.parentlsn, it is possible to
- * detect concurrent child page splits by checking if parentlsn < child's NSN,
- * and handle them properly. The child page's LSN is insufficient for this
- * purpose since it is updated for every page change.
- */
- typedef XLogRecPtr GistNSN;
- /*
- * A fake LSN / NSN value used during index builds. Must be smaller than any
- * real or fake (unlogged) LSN generated after the index build completes so
- * that all splits are considered complete.
- */
- #define GistBuildLSN ((XLogRecPtr) 1)
- /*
- * For on-disk compatibility with pre-9.3 servers, NSN is stored as two
- * 32-bit fields on disk, same as LSNs.
- */
- typedef PageXLogRecPtr PageGistNSN;
- typedef struct GISTPageOpaqueData
- {
- PageGistNSN nsn; /* this value must change on page split */
- BlockNumber rightlink; /* next page if any */
- uint16 flags; /* see bit definitions above */
- uint16 gist_page_id; /* for identification of GiST indexes */
- } GISTPageOpaqueData;
- typedef GISTPageOpaqueData *GISTPageOpaque;
- /*
- * Maximum possible sizes for GiST index tuple and index key. Calculation is
- * based on assumption that GiST page should fit at least 4 tuples. In theory,
- * GiST index can be functional when page can fit 3 tuples. But that seems
- * rather inefficient, so we use a bit conservative estimate.
- *
- * The maximum size of index key is true for unicolumn index. Therefore, this
- * estimation should be used to figure out which maximum size of GiST index key
- * makes sense at all. For multicolumn indexes, user might be able to tune
- * key size using opclass parameters.
- */
- #define GISTMaxIndexTupleSize \
- MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
- 4 - sizeof(ItemIdData))
- #define GISTMaxIndexKeySize \
- (GISTMaxIndexTupleSize - MAXALIGN(sizeof(IndexTupleData)))
- /*
- * The page ID is for the convenience of pg_filedump and similar utilities,
- * which otherwise would have a hard time telling pages of different index
- * types apart. It should be the last 2 bytes on the page. This is more or
- * less "free" due to alignment considerations.
- */
- #define GIST_PAGE_ID 0xFF81
- /*
- * This is the Split Vector to be returned by the PickSplit method.
- * PickSplit should fill the indexes of tuples to go to the left side into
- * spl_left[], and those to go to the right into spl_right[] (note the method
- * is responsible for palloc'ing both of these arrays!). The tuple counts
- * go into spl_nleft/spl_nright, and spl_ldatum/spl_rdatum must be set to
- * the union keys for each side.
- *
- * If spl_ldatum_exists and spl_rdatum_exists are true, then we are performing
- * a "secondary split" using a non-first index column. In this case some
- * decisions have already been made about a page split, and the set of tuples
- * being passed to PickSplit is just the tuples about which we are undecided.
- * spl_ldatum/spl_rdatum then contain the union keys for the tuples already
- * chosen to go left or right. Ideally the PickSplit method should take those
- * keys into account while deciding what to do with the remaining tuples, ie
- * it should try to "build out" from those unions so as to minimally expand
- * them. If it does so, it should union the given tuples' keys into the
- * existing spl_ldatum/spl_rdatum values rather than just setting those values
- * from scratch, and then set spl_ldatum_exists/spl_rdatum_exists to false to
- * show it has done this.
- *
- * If the PickSplit method fails to clear spl_ldatum_exists/spl_rdatum_exists,
- * the core GiST code will make its own decision about how to merge the
- * secondary-split results with the previously-chosen tuples, and will then
- * recompute the union keys from scratch. This is a workable though often not
- * optimal approach.
- */
- typedef struct GIST_SPLITVEC
- {
- OffsetNumber *spl_left; /* array of entries that go left */
- int spl_nleft; /* size of this array */
- Datum spl_ldatum; /* Union of keys in spl_left */
- bool spl_ldatum_exists; /* true, if spl_ldatum already exists. */
- OffsetNumber *spl_right; /* array of entries that go right */
- int spl_nright; /* size of the array */
- Datum spl_rdatum; /* Union of keys in spl_right */
- bool spl_rdatum_exists; /* true, if spl_rdatum already exists. */
- } GIST_SPLITVEC;
- /*
- * An entry on a GiST node. Contains the key, as well as its own
- * location (rel,page,offset) which can supply the matching pointer.
- * leafkey is a flag to tell us if the entry is in a leaf node.
- */
- typedef struct GISTENTRY
- {
- Datum key;
- Relation rel;
- Page page;
- OffsetNumber offset;
- bool leafkey;
- } GISTENTRY;
- #define GistPageGetOpaque(page) ( (GISTPageOpaque) PageGetSpecialPointer(page) )
- #define GistPageIsLeaf(page) ( GistPageGetOpaque(page)->flags & F_LEAF)
- #define GIST_LEAF(entry) (GistPageIsLeaf((entry)->page))
- #define GistPageIsDeleted(page) ( GistPageGetOpaque(page)->flags & F_DELETED)
- #define GistTuplesDeleted(page) ( GistPageGetOpaque(page)->flags & F_TUPLES_DELETED)
- #define GistMarkTuplesDeleted(page) ( GistPageGetOpaque(page)->flags |= F_TUPLES_DELETED)
- #define GistClearTuplesDeleted(page) ( GistPageGetOpaque(page)->flags &= ~F_TUPLES_DELETED)
- #define GistPageHasGarbage(page) ( GistPageGetOpaque(page)->flags & F_HAS_GARBAGE)
- #define GistMarkPageHasGarbage(page) ( GistPageGetOpaque(page)->flags |= F_HAS_GARBAGE)
- #define GistClearPageHasGarbage(page) ( GistPageGetOpaque(page)->flags &= ~F_HAS_GARBAGE)
- #define GistFollowRight(page) ( GistPageGetOpaque(page)->flags & F_FOLLOW_RIGHT)
- #define GistMarkFollowRight(page) ( GistPageGetOpaque(page)->flags |= F_FOLLOW_RIGHT)
- #define GistClearFollowRight(page) ( GistPageGetOpaque(page)->flags &= ~F_FOLLOW_RIGHT)
- #define GistPageGetNSN(page) ( PageXLogRecPtrGet(GistPageGetOpaque(page)->nsn))
- #define GistPageSetNSN(page, val) ( PageXLogRecPtrSet(GistPageGetOpaque(page)->nsn, val))
- /*
- * On a deleted page, we store this struct. A deleted page doesn't contain any
- * tuples, so we don't use the normal page layout with line pointers. Instead,
- * this struct is stored right after the standard page header. pd_lower points
- * to the end of this struct. If we add fields to this struct in the future, we
- * can distinguish the old and new formats by pd_lower.
- */
- typedef struct GISTDeletedPageContents
- {
- /* last xid which could see the page in a scan */
- FullTransactionId deleteXid;
- } GISTDeletedPageContents;
- static inline void
- GistPageSetDeleted(Page page, FullTransactionId deletexid)
- {
- Assert(PageIsEmpty(page));
- GistPageGetOpaque(page)->flags |= F_DELETED;
- ((PageHeader) page)->pd_lower = MAXALIGN(SizeOfPageHeaderData) + sizeof(GISTDeletedPageContents);
- ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid = deletexid;
- }
- static inline FullTransactionId
- GistPageGetDeleteXid(Page page)
- {
- Assert(GistPageIsDeleted(page));
- /* Is the deleteXid field present? */
- if (((PageHeader) page)->pd_lower >= MAXALIGN(SizeOfPageHeaderData) +
- offsetof(GISTDeletedPageContents, deleteXid) + sizeof(FullTransactionId))
- {
- return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid;
- }
- else
- return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
- }
- /*
- * Vector of GISTENTRY structs; user-defined methods union and picksplit
- * take it as one of their arguments
- */
- typedef struct
- {
- int32 n; /* number of elements */
- GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER];
- } GistEntryVector;
- #define GEVHDRSZ (offsetof(GistEntryVector, vector))
- /*
- * macro to initialize a GISTENTRY
- */
- #define gistentryinit(e, k, r, pg, o, l) \
- do { (e).key = (k); (e).rel = (r); (e).page = (pg); \
- (e).offset = (o); (e).leafkey = (l); } while (0)
- #endif /* GIST_H */
|