diff options
Diffstat (limited to 'src/include/access/gin_private.h')
-rw-r--r-- | src/include/access/gin_private.h | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h new file mode 100644 index 0000000..670a40b --- /dev/null +++ b/src/include/access/gin_private.h @@ -0,0 +1,500 @@ +/*-------------------------------------------------------------------------- + * 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 && \ + 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 && \ + 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, TIDBitmap *tbm); + +/* 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 */ |