| /*-------------------------------------------------------------------------- |
| * gin_private.h |
| * header file for postgres inverted index access method implementation. |
| * |
| * Copyright (c) 2006-2021, PostgreSQL Global Development Group |
| * |
| * src/include/access/gin_private.h |
| *-------------------------------------------------------------------------- |
| */ |
| #ifndef GIN_PRIVATE_H |
| #define GIN_PRIVATE_H |
| |
| #include "access/amapi.h" |
| #include "access/gin.h" |
| #include "access/ginblock.h" |
| #include "access/itup.h" |
| #include "catalog/pg_am_d.h" |
| #include "fmgr.h" |
| #include "lib/rbtree.h" |
| #include "storage/bufmgr.h" |
| |
| /* |
| * Storage type for GIN's reloptions |
| */ |
| typedef struct GinOptions |
| { |
| int32 vl_len_; /* varlena header (do not touch directly!) */ |
| bool useFastUpdate; /* use fast updates? */ |
| int pendingListCleanupSize; /* maximum size of pending list */ |
| } GinOptions; |
| |
| #define GIN_DEFAULT_USE_FASTUPDATE true |
| #define GinGetUseFastUpdate(relation) \ |
| (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ |
| IsIndexAccessMethod(relation->rd_rel->relam, GIN_AM_OID)), \ |
| (relation)->rd_options ? \ |
| ((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE) |
| #define GinGetPendingListCleanupSize(relation) \ |
| (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ |
| IsIndexAccessMethod(relation->rd_rel->relam, GIN_AM_OID)), \ |
| (relation)->rd_options && \ |
| ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize != -1 ? \ |
| ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize : \ |
| gin_pending_list_limit) |
| |
| |
| /* Macros for buffer lock/unlock operations */ |
| #define GIN_UNLOCK BUFFER_LOCK_UNLOCK |
| #define GIN_SHARE BUFFER_LOCK_SHARE |
| #define GIN_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE |
| |
| |
| /* |
| * GinState: working data structure describing the index being worked on |
| */ |
| typedef struct GinState |
| { |
| Relation index; |
| bool oneCol; /* true if single-column index */ |
| |
| /* |
| * origTupdesc is the nominal tuple descriptor of the index, ie, the i'th |
| * attribute shows the key type (not the input data type!) of the i'th |
| * index column. In a single-column index this describes the actual leaf |
| * index tuples. In a multi-column index, the actual leaf tuples contain |
| * a smallint column number followed by a key datum of the appropriate |
| * type for that column. We set up tupdesc[i] to describe the actual |
| * rowtype of the index tuples for the i'th column, ie, (int2, keytype). |
| * Note that in any case, leaf tuples contain more data than is known to |
| * the TupleDesc; see access/gin/README for details. |
| */ |
| TupleDesc origTupdesc; |
| TupleDesc tupdesc[INDEX_MAX_KEYS]; |
| |
| /* |
| * Per-index-column opclass support functions |
| */ |
| FmgrInfo compareFn[INDEX_MAX_KEYS]; |
| FmgrInfo extractValueFn[INDEX_MAX_KEYS]; |
| FmgrInfo extractQueryFn[INDEX_MAX_KEYS]; |
| FmgrInfo consistentFn[INDEX_MAX_KEYS]; |
| FmgrInfo triConsistentFn[INDEX_MAX_KEYS]; |
| FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */ |
| /* canPartialMatch[i] is true if comparePartialFn[i] is valid */ |
| bool canPartialMatch[INDEX_MAX_KEYS]; |
| /* Collations to pass to the support functions */ |
| Oid supportCollation[INDEX_MAX_KEYS]; |
| } GinState; |
| |
| |
| /* ginutil.c */ |
| extern bytea *ginoptions(Datum reloptions, bool validate); |
| extern void initGinState(GinState *state, Relation index); |
| extern Buffer GinNewBuffer(Relation index); |
| extern void GinInitBuffer(Buffer b, uint32 f); |
| extern void GinInitPage(Page page, uint32 f, Size pageSize); |
| extern void GinInitMetabuffer(Buffer b); |
| extern int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, |
| Datum a, GinNullCategory categorya, |
| Datum b, GinNullCategory categoryb); |
| extern int ginCompareAttEntries(GinState *ginstate, |
| OffsetNumber attnuma, Datum a, GinNullCategory categorya, |
| OffsetNumber attnumb, Datum b, GinNullCategory categoryb); |
| extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum, |
| Datum value, bool isNull, |
| int32 *nentries, GinNullCategory **categories); |
| |
| extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple); |
| extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, |
| GinNullCategory *category); |
| |
| /* gininsert.c */ |
| extern IndexBuildResult *ginbuild(Relation heap, Relation index, |
| struct IndexInfo *indexInfo); |
| extern void ginbuildempty(Relation index); |
| extern bool gininsert(Relation index, Datum *values, bool *isnull, |
| ItemPointer ht_ctid, Relation heapRel, |
| IndexUniqueCheck checkUnique, |
| bool indexUnchanged, |
| struct IndexInfo *indexInfo); |
| extern void ginEntryInsert(GinState *ginstate, |
| OffsetNumber attnum, Datum key, GinNullCategory category, |
| ItemPointerData *items, uint32 nitem, |
| GinStatsData *buildStats); |
| |
| /* ginbtree.c */ |
| |
| typedef struct GinBtreeStack |
| { |
| BlockNumber blkno; |
| Buffer buffer; |
| OffsetNumber off; |
| ItemPointerData iptr; |
| /* predictNumber contains predicted number of pages on current level */ |
| uint32 predictNumber; |
| struct GinBtreeStack *parent; |
| } GinBtreeStack; |
| |
| typedef struct GinBtreeData *GinBtree; |
| |
| /* Return codes for GinBtreeData.beginPlaceToPage method */ |
| typedef enum |
| { |
| GPTP_NO_WORK, |
| GPTP_INSERT, |
| GPTP_SPLIT |
| } GinPlaceToPageRC; |
| |
| typedef struct GinBtreeData |
| { |
| /* search methods */ |
| BlockNumber (*findChildPage) (GinBtree, GinBtreeStack *); |
| BlockNumber (*getLeftMostChild) (GinBtree, Page); |
| bool (*isMoveRight) (GinBtree, Page); |
| bool (*findItem) (GinBtree, GinBtreeStack *); |
| |
| /* insert methods */ |
| OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber); |
| GinPlaceToPageRC (*beginPlaceToPage) (GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void **, Page *, Page *); |
| void (*execPlaceToPage) (GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void *); |
| void *(*prepareDownlink) (GinBtree, Buffer); |
| void (*fillRoot) (GinBtree, Page, BlockNumber, Page, BlockNumber, Page); |
| |
| bool isData; |
| |
| Relation index; |
| BlockNumber rootBlkno; |
| GinState *ginstate; /* not valid in a data scan */ |
| bool fullScan; |
| bool isBuild; |
| |
| /* Search key for Entry tree */ |
| OffsetNumber entryAttnum; |
| Datum entryKey; |
| GinNullCategory entryCategory; |
| |
| /* Search key for data tree (posting tree) */ |
| ItemPointerData itemptr; |
| } GinBtreeData; |
| |
| /* This represents a tuple to be inserted to entry tree. */ |
| typedef struct |
| { |
| IndexTuple entry; /* tuple to insert */ |
| bool isDelete; /* delete old tuple at same offset? */ |
| } GinBtreeEntryInsertData; |
| |
| /* |
| * This represents an itempointer, or many itempointers, to be inserted to |
| * a data (posting tree) leaf page |
| */ |
| typedef struct |
| { |
| ItemPointerData *items; |
| uint32 nitem; |
| uint32 curitem; |
| } GinBtreeDataLeafInsertData; |
| |
| /* |
| * For internal data (posting tree) pages, the insertion payload is a |
| * PostingItem |
| */ |
| |
| extern GinBtreeStack *ginFindLeafPage(GinBtree btree, bool searchMode, |
| bool rootConflictCheck, Snapshot snapshot); |
| extern Buffer ginStepRight(Buffer buffer, Relation index, int lockmode); |
| extern void freeGinBtreeStack(GinBtreeStack *stack); |
| extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack, |
| void *insertdata, GinStatsData *buildStats); |
| |
| /* ginentrypage.c */ |
| extern IndexTuple GinFormTuple(GinState *ginstate, |
| OffsetNumber attnum, Datum key, GinNullCategory category, |
| Pointer data, Size dataSize, int nipd, bool errorTooBig); |
| extern void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum, |
| Datum key, GinNullCategory category, |
| GinState *ginstate); |
| extern void ginEntryFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage); |
| extern ItemPointer ginReadTuple(GinState *ginstate, OffsetNumber attnum, |
| IndexTuple itup, int *nitems); |
| |
| /* gindatapage.c */ |
| extern ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast); |
| extern int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm); |
| extern BlockNumber createPostingTree(Relation index, |
| ItemPointerData *items, uint32 nitems, |
| GinStatsData *buildStats, Buffer entrybuffer); |
| extern void GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset); |
| extern void GinPageDeletePostingItem(Page page, OffsetNumber offset); |
| extern void ginInsertItemPointers(Relation index, BlockNumber rootBlkno, |
| ItemPointerData *items, uint32 nitem, |
| GinStatsData *buildStats); |
| extern GinBtreeStack *ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno, Snapshot snapshot); |
| extern void ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage); |
| |
| /* |
| * This is declared in ginvacuum.c, but is passed between ginVacuumItemPointers |
| * and ginVacuumPostingTreeLeaf and as an opaque struct, so we need a forward |
| * declaration for it. |
| */ |
| typedef struct GinVacuumState GinVacuumState; |
| |
| extern void ginVacuumPostingTreeLeaf(Relation rel, Buffer buf, GinVacuumState *gvs); |
| |
| /* ginscan.c */ |
| |
| /* |
| * GinScanKeyData describes a single GIN index qualifier expression. |
| * |
| * From each qual expression, we extract one or more specific index search |
| * conditions, which are represented by GinScanEntryData. It's quite |
| * possible for identical search conditions to be requested by more than |
| * one qual expression, in which case we merge such conditions to have just |
| * one unique GinScanEntry --- this is particularly important for efficiency |
| * when dealing with full-index-scan entries. So there can be multiple |
| * GinScanKeyData.scanEntry pointers to the same GinScanEntryData. |
| * |
| * In each GinScanKeyData, nentries is the true number of entries, while |
| * nuserentries is the number that extractQueryFn returned (which is what |
| * we report to consistentFn). The "user" entries must come first. |
| */ |
| typedef struct GinScanKeyData *GinScanKey; |
| |
| typedef struct GinScanEntryData *GinScanEntry; |
| |
| typedef struct GinScanKeyData |
| { |
| /* Real number of entries in scanEntry[] (always > 0) */ |
| uint32 nentries; |
| /* Number of entries that extractQueryFn and consistentFn know about */ |
| uint32 nuserentries; |
| |
| /* array of GinScanEntry pointers, one per extracted search condition */ |
| GinScanEntry *scanEntry; |
| |
| /* |
| * At least one of the entries in requiredEntries must be present for a |
| * tuple to match the overall qual. |
| * |
| * additionalEntries contains entries that are needed by the consistent |
| * function to decide if an item matches, but are not sufficient to |
| * satisfy the qual without entries from requiredEntries. |
| */ |
| GinScanEntry *requiredEntries; |
| int nrequired; |
| GinScanEntry *additionalEntries; |
| int nadditional; |
| |
| /* array of check flags, reported to consistentFn */ |
| GinTernaryValue *entryRes; |
| bool (*boolConsistentFn) (GinScanKey key); |
| GinTernaryValue (*triConsistentFn) (GinScanKey key); |
| FmgrInfo *consistentFmgrInfo; |
| FmgrInfo *triConsistentFmgrInfo; |
| Oid collation; |
| |
| /* other data needed for calling consistentFn */ |
| Datum query; |
| /* NB: these three arrays have only nuserentries elements! */ |
| Datum *queryValues; |
| GinNullCategory *queryCategories; |
| Pointer *extra_data; |
| StrategyNumber strategy; |
| int32 searchMode; |
| OffsetNumber attnum; |
| |
| /* |
| * An excludeOnly scan key is not able to enumerate all matching tuples. |
| * That is, to be semantically correct on its own, it would need to have a |
| * GIN_CAT_EMPTY_QUERY scanEntry, but it doesn't. Such a key can still be |
| * used to filter tuples returned by other scan keys, so we will get the |
| * right answers as long as there's at least one non-excludeOnly scan key |
| * for each index attribute considered by the search. For efficiency |
| * reasons we don't want to have unnecessary GIN_CAT_EMPTY_QUERY entries, |
| * so we will convert an excludeOnly scan key to non-excludeOnly (by |
| * adding a GIN_CAT_EMPTY_QUERY scanEntry) only if there are no other |
| * non-excludeOnly scan keys. |
| */ |
| bool excludeOnly; |
| |
| /* |
| * Match status data. curItem is the TID most recently tested (could be a |
| * lossy-page pointer). curItemMatches is true if it passes the |
| * consistentFn test; if so, recheckCurItem is the recheck flag. |
| * isFinished means that all the input entry streams are finished, so this |
| * key cannot succeed for any later TIDs. |
| */ |
| ItemPointerData curItem; |
| bool curItemMatches; |
| bool recheckCurItem; |
| bool isFinished; |
| } GinScanKeyData; |
| |
| typedef struct GinScanEntryData |
| { |
| /* query key and other information from extractQueryFn */ |
| Datum queryKey; |
| GinNullCategory queryCategory; |
| bool isPartialMatch; |
| Pointer extra_data; |
| StrategyNumber strategy; |
| int32 searchMode; |
| OffsetNumber attnum; |
| |
| /* Current page in posting tree */ |
| Buffer buffer; |
| |
| /* current ItemPointer to heap */ |
| ItemPointerData curItem; |
| |
| /* for a partial-match or full-scan query, we accumulate all TIDs here */ |
| TIDBitmap *matchBitmap; |
| TBMIterator *matchIterator; |
| TBMIterateResult *matchResult; |
| |
| /* used for Posting list and one page in Posting tree */ |
| ItemPointerData *list; |
| int nlist; |
| OffsetNumber offset; |
| |
| bool isFinished; |
| bool reduceResult; |
| uint32 predictNumberResult; |
| GinBtreeData btree; |
| } GinScanEntryData; |
| |
| typedef struct GinScanOpaqueData |
| { |
| MemoryContext tempCtx; |
| GinState ginstate; |
| |
| GinScanKey keys; /* one per scan qualifier expr */ |
| uint32 nkeys; |
| |
| GinScanEntry *entries; /* one per index search condition */ |
| uint32 totalentries; |
| uint32 allocentries; /* allocated length of entries[] */ |
| |
| MemoryContext keyCtx; /* used to hold key and entry data */ |
| |
| bool isVoidRes; /* true if query is unsatisfiable */ |
| } GinScanOpaqueData; |
| |
| typedef GinScanOpaqueData *GinScanOpaque; |
| |
| extern IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys); |
| extern void ginendscan(IndexScanDesc scan); |
| extern void ginrescan(IndexScanDesc scan, ScanKey key, int nscankeys, |
| ScanKey orderbys, int norderbys); |
| extern void ginNewScanKey(IndexScanDesc scan); |
| extern void ginFreeScanKeys(GinScanOpaque so); |
| |
| /* ginget.c */ |
| extern int64 gingetbitmap(IndexScanDesc scan, Node **bmNodeP); |
| |
| /* ginlogic.c */ |
| extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key); |
| |
| /* ginvacuum.c */ |
| extern IndexBulkDeleteResult *ginbulkdelete(IndexVacuumInfo *info, |
| IndexBulkDeleteResult *stats, |
| IndexBulkDeleteCallback callback, |
| void *callback_state); |
| extern IndexBulkDeleteResult *ginvacuumcleanup(IndexVacuumInfo *info, |
| IndexBulkDeleteResult *stats); |
| extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs, |
| ItemPointerData *items, int nitem, int *nremaining); |
| |
| /* ginvalidate.c */ |
| extern bool ginvalidate(Oid opclassoid); |
| extern void ginadjustmembers(Oid opfamilyoid, |
| Oid opclassoid, |
| List *operators, |
| List *functions); |
| |
| /* ginbulk.c */ |
| typedef struct GinEntryAccumulator |
| { |
| RBTNode rbtnode; |
| Datum key; |
| GinNullCategory category; |
| OffsetNumber attnum; |
| bool shouldSort; |
| ItemPointerData *list; |
| uint32 maxcount; /* allocated size of list[] */ |
| uint32 count; /* current number of list[] entries */ |
| } GinEntryAccumulator; |
| |
| typedef struct |
| { |
| GinState *ginstate; |
| Size allocatedMemory; |
| GinEntryAccumulator *entryallocator; |
| uint32 eas_used; |
| RBTree *tree; |
| RBTreeIterator tree_walk; |
| } BuildAccumulator; |
| |
| extern void ginInitBA(BuildAccumulator *accum); |
| extern void ginInsertBAEntries(BuildAccumulator *accum, |
| ItemPointer heapptr, OffsetNumber attnum, |
| Datum *entries, GinNullCategory *categories, |
| int32 nentries); |
| extern void ginBeginBAScan(BuildAccumulator *accum); |
| extern ItemPointerData *ginGetBAEntry(BuildAccumulator *accum, |
| OffsetNumber *attnum, Datum *key, GinNullCategory *category, |
| uint32 *n); |
| |
| /* ginfast.c */ |
| |
| typedef struct GinTupleCollector |
| { |
| IndexTuple *tuples; |
| uint32 ntuples; |
| uint32 lentuples; |
| uint32 sumsize; |
| } GinTupleCollector; |
| |
| extern void ginHeapTupleFastInsert(GinState *ginstate, |
| GinTupleCollector *collector); |
| extern void ginHeapTupleFastCollect(GinState *ginstate, |
| GinTupleCollector *collector, |
| OffsetNumber attnum, Datum value, bool isNull, |
| ItemPointer ht_ctid); |
| extern void ginInsertCleanup(GinState *ginstate, bool full_clean, |
| bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats); |
| |
| /* ginpostinglist.c */ |
| |
| extern GinPostingList *ginCompressPostingList(const ItemPointer ipd, int nipd, |
| int maxsize, int *nwritten); |
| extern int ginPostingListDecodeAllSegmentsToTbm(GinPostingList *ptr, int totalsize, TIDBitmap *tbm); |
| |
| extern ItemPointer ginPostingListDecodeAllSegments(GinPostingList *ptr, int len, int *ndecoded); |
| extern ItemPointer ginPostingListDecode(GinPostingList *ptr, int *ndecoded); |
| extern ItemPointer ginMergeItemPointers(ItemPointerData *a, uint32 na, |
| ItemPointerData *b, uint32 nb, |
| int *nmerged); |
| |
| /* |
| * Merging the results of several gin scans compares item pointers a lot, |
| * so we want this to be inlined. |
| */ |
| static inline int |
| ginCompareItemPointers(ItemPointer a, ItemPointer b) |
| { |
| uint64 ia = (uint64) GinItemPointerGetBlockNumber(a) << 32 | GinItemPointerGetOffsetNumber(a); |
| uint64 ib = (uint64) GinItemPointerGetBlockNumber(b) << 32 | GinItemPointerGetOffsetNumber(b); |
| |
| if (ia == ib) |
| return 0; |
| else if (ia > ib) |
| return 1; |
| else |
| return -1; |
| } |
| |
| extern int ginTraverseLock(Buffer buffer, bool searchMode); |
| |
| #endif /* GIN_PRIVATE_H */ |