/*------------------------------------------------------------------------- * * rum.h * Exported definitions for RUM index. * * Portions Copyright (c) 2015-2016, Postgres Professional * Portions Copyright (c) 2006-2016, PostgreSQL Global Development Group * *------------------------------------------------------------------------- */ #ifndef __RUM_H__ #define __RUM_H__ #include "access/amapi.h" #include "access/generic_xlog.h" #include "access/gin.h" #include "access/itup.h" #include "access/sdir.h" #include "lib/rbtree.h" #include "storage/bufmgr.h" #include "rumsort.h" /* RUM distance strategies */ #define RUM_DISTANCE 20 #define RUM_LEFT_DISTANCE 21 #define RUM_RIGHT_DISTANCE 22 /* * Page opaque data in a inverted index page. * * Note: RUM does not include a page ID word as do the other index types. * This is OK because the opaque data is only 8 bytes and so can be reliably * distinguished by size. Revisit this if the size ever increases. * Further note: as of 9.2, SP-GiST also uses 8-byte special space. This is * still OK, as long as RUM isn't using all of the high-order bits in its * flags word, because that way the flags word cannot match the page ID used * by SP-GiST. */ typedef struct RumPageOpaqueData { BlockNumber leftlink; /* prev page if any */ BlockNumber rightlink; /* next page if any */ OffsetNumber maxoff; /* number entries on RUM_DATA page: number of * heap ItemPointers on RUM_DATA|RUM_LEAF page * or number of PostingItems on RUM_DATA & * ~RUM_LEAF page. On RUM_LIST page, number of * heap tuples. */ OffsetNumber freespace; uint16 flags; /* see bit definitions below */ } RumPageOpaqueData; typedef RumPageOpaqueData *RumPageOpaque; #define RUM_DATA (1 << 0) #define RUM_LEAF (1 << 1) #define RUM_DELETED (1 << 2) #define RUM_META (1 << 3) #define RUM_LIST (1 << 4) #define RUM_LIST_FULLROW (1 << 5) /* makes sense only on RUM_LIST page */ /* Page numbers of fixed-location pages */ #define RUM_METAPAGE_BLKNO (0) #define RUM_ROOT_BLKNO (1) typedef struct RumMetaPageData { /* * RUM version number */ uint32 rumVersion; /* * Pointers to head and tail of pending list, which consists of RUM_LIST * pages. These store fast-inserted entries that haven't yet been moved * into the regular RUM structure. * XXX unused - pending list is removed. */ BlockNumber head; BlockNumber tail; /* * Free space in bytes in the pending list's tail page. */ uint32 tailFreeSize; /* * We store both number of pages and number of heap tuples that are in the * pending list. */ BlockNumber nPendingPages; int64 nPendingHeapTuples; /* * Statistics for planner use (accurate as of last VACUUM) */ BlockNumber nTotalPages; BlockNumber nEntryPages; BlockNumber nDataPages; int64 nEntries; } RumMetaPageData; #define RUM_CURRENT_VERSION (0xC0DE0002) #define RumPageGetMeta(p) \ ((RumMetaPageData *) PageGetContents(p)) /* * Macros for accessing a RUM index page's opaque data */ #define RumPageGetOpaque(page) ( (RumPageOpaque) PageGetSpecialPointer(page) ) #define RumPageIsLeaf(page) ( (RumPageGetOpaque(page)->flags & RUM_LEAF) != 0 ) #define RumPageSetLeaf(page) ( RumPageGetOpaque(page)->flags |= RUM_LEAF ) #define RumPageSetNonLeaf(page) ( RumPageGetOpaque(page)->flags &= ~RUM_LEAF ) #define RumPageIsData(page) ( (RumPageGetOpaque(page)->flags & RUM_DATA) != 0 ) #define RumPageSetData(page) ( RumPageGetOpaque(page)->flags |= RUM_DATA ) #define RumPageIsList(page) ( (RumPageGetOpaque(page)->flags & RUM_LIST) != 0 ) #define RumPageSetList(page) ( RumPageGetOpaque(page)->flags |= RUM_LIST ) #define RumPageHasFullRow(page) ( (RumPageGetOpaque(page)->flags & RUM_LIST_FULLROW) != 0 ) #define RumPageSetFullRow(page) ( RumPageGetOpaque(page)->flags |= RUM_LIST_FULLROW ) #define RumPageIsDeleted(page) ( (RumPageGetOpaque(page)->flags & RUM_DELETED) != 0 ) #define RumPageSetDeleted(page) ( RumPageGetOpaque(page)->flags |= RUM_DELETED) #define RumPageSetNonDeleted(page) ( RumPageGetOpaque(page)->flags &= ~RUM_DELETED) #define RumPageRightMost(page) ( RumPageGetOpaque(page)->rightlink == InvalidBlockNumber) #define RumPageLeftMost(page) ( RumPageGetOpaque(page)->leftlink == InvalidBlockNumber) /* * We use our own ItemPointerGet(BlockNumber|GetOffsetNumber) * to avoid Asserts, since sometimes the ip_posid isn't "valid" */ #define RumItemPointerGetBlockNumber(pointer) \ BlockIdGetBlockNumber(&(pointer)->ip_blkid) #define RumItemPointerGetOffsetNumber(pointer) \ ((pointer)->ip_posid) /* * Special-case item pointer values needed by the RUM search logic. * MIN: sorts less than any valid item pointer * MAX: sorts greater than any valid item pointer * LOSSY PAGE: indicates a whole heap page, sorts after normal item * pointers for that page * Note that these are all distinguishable from an "invalid" item pointer * (which is InvalidBlockNumber/0) as well as from all normal item * pointers (which have item numbers in the range 1..MaxHeapTuplesPerPage). */ #define ItemPointerSetMin(p) \ ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0) #define ItemPointerIsMin(p) \ (RumItemPointerGetOffsetNumber(p) == (OffsetNumber)0 && \ RumItemPointerGetBlockNumber(p) == (BlockNumber)0) #define ItemPointerSetMax(p) \ ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xfffe) #define ItemPointerIsMax(p) \ (RumItemPointerGetOffsetNumber(p) == (OffsetNumber)0xfffe && \ RumItemPointerGetBlockNumber(p) == InvalidBlockNumber) #define ItemPointerSetLossyPage(p, b) \ ItemPointerSet((p), (b), (OffsetNumber)0xffff) #define ItemPointerIsLossyPage(p) \ (RumItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \ RumItemPointerGetBlockNumber(p) != InvalidBlockNumber) typedef struct RumItem { ItemPointerData iptr; bool addInfoIsNull; Datum addInfo; } RumItem; #define RumItemSetMin(item) \ do { \ ItemPointerSetMin(&((item)->iptr)); \ (item)->addInfoIsNull = true; \ (item)->addInfo = (Datum) 0; \ } while (0) /* * Posting item in a non-leaf posting-tree page */ typedef struct { /* We use BlockIdData not BlockNumber to avoid padding space wastage */ BlockIdData child_blkno; RumItem item; } PostingItem; #define PostingItemGetBlockNumber(pointer) \ BlockIdGetBlockNumber(&(pointer)->child_blkno) #define PostingItemSetBlockNumber(pointer, blockNumber) \ BlockIdSet(&((pointer)->child_blkno), (blockNumber)) /* * Category codes to distinguish placeholder nulls from ordinary NULL keys. * Note that the datatype size and the first two code values are chosen to be * compatible with the usual usage of bool isNull flags. * * RUM_CAT_EMPTY_QUERY is never stored in the index; and notice that it is * chosen to sort before not after regular key values. */ typedef signed char RumNullCategory; #define RUM_CAT_NORM_KEY 0 /* normal, non-null key value */ #define RUM_CAT_NULL_KEY 1 /* null key value */ #define RUM_CAT_EMPTY_ITEM 2 /* placeholder for zero-key item */ #define RUM_CAT_NULL_ITEM 3 /* placeholder for null item */ #define RUM_CAT_EMPTY_QUERY (-1) /* placeholder for full-scan query */ /* * Access macros for null category byte in entry tuples */ #define RumCategoryOffset(itup,rumstate) \ (IndexInfoFindDataOffset((itup)->t_info) + \ ((rumstate)->oneCol ? 0 : sizeof(int16))) #define RumGetNullCategory(itup) \ (*((RumNullCategory *) ((char*)(itup) + IndexTupleSize(itup) - sizeof(RumNullCategory)))) #define RumSetNullCategory(itup,c) \ (*((RumNullCategory *) ((char*)(itup) + IndexTupleSize(itup) - sizeof(RumNullCategory))) = (c)) /* * Access macros for leaf-page entry tuples (see discussion in README) */ #define RumGetNPosting(itup) RumItemPointerGetOffsetNumber(&(itup)->t_tid) #define RumSetNPosting(itup,n) ItemPointerSetOffsetNumber(&(itup)->t_tid,n) #define RUM_TREE_POSTING ((OffsetNumber)0xffff) #define RumIsPostingTree(itup) (RumGetNPosting(itup) == RUM_TREE_POSTING) #define RumSetPostingTree(itup, blkno) \ (RumSetNPosting((itup),RUM_TREE_POSTING), \ ItemPointerSetBlockNumber(&(itup)->t_tid, blkno)) #define RumGetPostingTree(itup) RumItemPointerGetBlockNumber(&(itup)->t_tid) #define RumGetPostingOffset(itup) RumItemPointerGetBlockNumber(&(itup)->t_tid) #define RumSetPostingOffset(itup,n) ItemPointerSetBlockNumber(&(itup)->t_tid,n) #define RumGetPosting(itup) ((Pointer) ((char*)(itup) + RumGetPostingOffset(itup))) /* * Maximum size of an item on entry tree page. Make sure that we fit at least * three items on each page. (On regular B-tree indexes, we must fit at least * three items: two data items and the "high key". In RUM entry tree, we don't * currently store the high key explicitly, we just use the rightmost item on * the page, so it would actually be enough to fit two items.) */ #define RumMaxItemSize \ Min(INDEX_SIZE_MASK, \ MAXALIGN_DOWN(((BLCKSZ - \ MAXALIGN(SizeOfPageHeaderData + 3 * sizeof(ItemIdData)) - \ MAXALIGN(sizeof(RumPageOpaqueData))) / 3))) /* * Access macros for non-leaf entry tuples */ #define RumGetDownlink(itup) RumItemPointerGetBlockNumber(&(itup)->t_tid) #define RumSetDownlink(itup,blkno) ItemPointerSet(&(itup)->t_tid, blkno, InvalidOffsetNumber) /* * Data (posting tree) pages */ #define RumDataPageGetRightBound(page) ((RumItem*) PageGetContents(page)) #define RumDataPageGetData(page) \ (PageGetContents(page) + MAXALIGN(sizeof(RumItem))) #define RumDataPageGetItem(page,i) \ (RumDataPageGetData(page) + ((i)-1) * sizeof(PostingItem)) #define RumDataPageGetFreeSpace(page) \ (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \ - MAXALIGN(sizeof(RumItem)) /* right bound */ \ - RumPageGetOpaque(page)->maxoff * sizeof(PostingItem) \ - MAXALIGN(sizeof(RumPageOpaqueData))) #define RumMaxLeafDataItems \ ((BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \ MAXALIGN(sizeof(RumItem)) /* right bound */ - \ MAXALIGN(sizeof(RumPageOpaqueData))) \ / sizeof(ItemPointerData)) /* * List pages */ #define RumListPageSize \ ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(RumPageOpaqueData)) ) typedef struct { ItemPointerData iptr; OffsetNumber offsetNumer; uint16 pageOffset; Datum addInfo; /* optional */ } RumDataLeafItemIndex; #define RumDataLeafIndexCount 32 #define RumDataPageSize \ (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \ - MAXALIGN(sizeof(RumItem)) /* right bound */ \ - MAXALIGN(sizeof(RumPageOpaqueData)) \ - MAXALIGN(sizeof(RumDataLeafItemIndex) * RumDataLeafIndexCount)) #define RumDataPageFreeSpacePre(page,ptr) \ (RumDataPageSize \ - ((ptr) - RumDataPageGetData(page))) #define RumPageGetIndexes(page) \ ((RumDataLeafItemIndex *)(RumDataPageGetData(page) + RumDataPageSize)) /* * Storage type for RUM's reloptions */ typedef struct RumOptions { int32 vl_len_; /* varlena header (do not touch directly!) */ bool useAlternativeOrder; int attachColumn; int addToColumn; } RumOptions; #define ALT_ADD_INFO_NULL_FLAG (0x8000) /* Macros for buffer lock/unlock operations */ #define RUM_UNLOCK BUFFER_LOCK_UNLOCK #define RUM_SHARE BUFFER_LOCK_SHARE #define RUM_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE #define MAX_STRATEGIES (8) typedef struct RumConfig { Oid addInfoTypeOid; struct { StrategyNumber strategy; ScanDirection direction; } strategyInfo[MAX_STRATEGIES]; } RumConfig; /* * RumState: working data structure describing the index being worked on */ typedef struct RumState { Relation index; bool isBuild; bool oneCol; /* true if single-column index */ bool useAlternativeOrder; AttrNumber attrnAttachColumn; AttrNumber attrnAddToColumn; /* * 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]; RumConfig rumConfig[INDEX_MAX_KEYS]; Form_pg_attribute addAttrs[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 comparePartialFn[INDEX_MAX_KEYS]; /* optional method */ FmgrInfo configFn[INDEX_MAX_KEYS]; /* optional method */ FmgrInfo preConsistentFn[INDEX_MAX_KEYS]; /* optional method */ FmgrInfo orderingFn[INDEX_MAX_KEYS]; /* optional method */ FmgrInfo outerOrderingFn[INDEX_MAX_KEYS]; /* optional method */ FmgrInfo joinAddInfoFn[INDEX_MAX_KEYS]; /* optional method */ /* canPartialMatch[i] is true if comparePartialFn[i] is valid */ bool canPartialMatch[INDEX_MAX_KEYS]; /* canPreConsistent[i] is true if preConsistentFn[i] is valid */ bool canPreConsistent[INDEX_MAX_KEYS]; /* canOrdering[i] is true if orderingFn[i] is valid */ bool canOrdering[INDEX_MAX_KEYS]; bool canOuterOrdering[INDEX_MAX_KEYS]; bool canJoinAddInfo[INDEX_MAX_KEYS]; /* Collations to pass to the support functions */ Oid supportCollation[INDEX_MAX_KEYS]; } RumState; /* rumutil.c */ extern bytea *rumoptions(Datum reloptions, bool validate); extern Datum rumhandler(PG_FUNCTION_ARGS); extern void initRumState(RumState * state, Relation index); extern Buffer RumNewBuffer(Relation index); extern void RumInitBuffer(GenericXLogState *state, Buffer buffer, uint32 flags, bool isBuild); extern void RumInitPage(Page page, uint32 f, Size pageSize); extern void RumInitMetabuffer(GenericXLogState *state, Buffer metaBuffer, bool isBuild); extern int rumCompareEntries(RumState * rumstate, OffsetNumber attnum, Datum a, RumNullCategory categorya, Datum b, RumNullCategory categoryb); extern int rumCompareAttEntries(RumState * rumstate, OffsetNumber attnuma, Datum a, RumNullCategory categorya, OffsetNumber attnumb, Datum b, RumNullCategory categoryb); extern Datum *rumExtractEntries(RumState * rumstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, RumNullCategory ** categories, Datum **addInfo, bool **addInfoIsNull); extern OffsetNumber rumtuple_get_attrnum(RumState * rumstate, IndexTuple tuple); extern Datum rumtuple_get_key(RumState * rumstate, IndexTuple tuple, RumNullCategory * category); extern void rumGetStats(Relation index, GinStatsData *stats); extern void rumUpdateStats(Relation index, const GinStatsData *stats, bool isBuild); /* ruminsert.c */ extern IndexBuildResult *rumbuild(Relation heap, Relation index, struct IndexInfo *indexInfo); extern void rumbuildempty(Relation index); extern bool ruminsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique #if PG_VERSION_NUM >= 100000 , struct IndexInfo *indexInfo #endif ); extern void rumEntryInsert(RumState * rumstate, OffsetNumber attnum, Datum key, RumNullCategory category, RumItem * items, uint32 nitem, GinStatsData *buildStats); /* rumbtree.c */ typedef struct RumBtreeStack { BlockNumber blkno; Buffer buffer; OffsetNumber off; /* predictNumber contains predicted number of pages on current level */ uint32 predictNumber; struct RumBtreeStack *parent; } RumBtreeStack; typedef struct RumBtreeData *RumBtree; typedef struct RumBtreeData { /* search methods */ BlockNumber (*findChildPage) (RumBtree, RumBtreeStack *); bool (*isMoveRight) (RumBtree, Page); bool (*findItem) (RumBtree, RumBtreeStack *); /* insert methods */ OffsetNumber (*findChildPtr) (RumBtree, Page, BlockNumber, OffsetNumber); BlockNumber (*getLeftMostPage) (RumBtree, Page); bool (*isEnoughSpace) (RumBtree, Buffer, OffsetNumber); void (*placeToPage) (RumBtree, Page, OffsetNumber); Page (*splitPage) (RumBtree, Buffer, Buffer, Page, Page, OffsetNumber); void (*fillRoot) (RumBtree, Buffer, Buffer, Buffer, Page, Page, Page); bool isData; bool searchMode; Relation index; RumState *rumstate; bool fullScan; ScanDirection scanDirection; BlockNumber rightblkno; AttrNumber entryAttnum; /* Entry options */ Datum entryKey; RumNullCategory entryCategory; IndexTuple entry; bool isDelete; /* Data (posting tree) options */ RumItem *items; uint32 nitem; uint32 curitem; PostingItem pitem; } RumBtreeData; extern RumBtreeStack *rumPrepareFindLeafPage(RumBtree btree, BlockNumber blkno); extern RumBtreeStack *rumFindLeafPage(RumBtree btree, RumBtreeStack * stack); extern RumBtreeStack *rumReFindLeafPage(RumBtree btree, RumBtreeStack * stack); extern Buffer rumStep(Buffer buffer, Relation index, int lockmode, ScanDirection scanDirection); extern void freeRumBtreeStack(RumBtreeStack * stack); extern void rumInsertValue(Relation index, RumBtree btree, RumBtreeStack * stack, GinStatsData *buildStats); extern void rumFindParents(RumBtree btree, RumBtreeStack * stack, BlockNumber rootBlkno); /* rumentrypage.c */ extern void rumPrepareEntryScan(RumBtree btree, OffsetNumber attnum, Datum key, RumNullCategory category, RumState * rumstate); extern void rumEntryFillRoot(RumBtree btree, Buffer root, Buffer lbuf, Buffer rbuf, Page page, Page lpage, Page rpage); extern IndexTuple rumPageGetLinkItup(RumBtree btree, Buffer buf, Page page); extern void rumReadTuple(RumState * rumstate, OffsetNumber attnum, IndexTuple itup, RumItem * items); extern void rumReadTuplePointers(RumState * rumstate, OffsetNumber attnum, IndexTuple itup, ItemPointerData *ipd); extern void updateItemIndexes(Page page, OffsetNumber attnum, RumState * rumstate); extern void checkLeafDataPage(RumState * rumstate, AttrNumber attrnum, Page page); /* rumdatapage.c */ extern int rumCompareItemPointers(const ItemPointerData *a, const ItemPointerData *b); extern int compareRumItem(RumState * state, const AttrNumber attno, const RumItem * a, const RumItem * b); extern void convertIndexToKey(RumDataLeafItemIndex *src, RumItem *dst); extern Pointer rumPlaceToDataPageLeaf(Pointer ptr, OffsetNumber attnum, RumItem * item, ItemPointer prev, RumState * rumstate); extern Size rumCheckPlaceToDataPageLeaf(OffsetNumber attnum, RumItem * item, ItemPointer prev, RumState * rumstate, Size size); extern uint32 rumMergeRumItems(RumState * rumstate, AttrNumber attno, RumItem * dst, RumItem * a, uint32 na, RumItem * b, uint32 nb); extern void RumDataPageAddItem(Page page, void *data, OffsetNumber offset); extern void RumPageDeletePostingItem(Page page, OffsetNumber offset); typedef struct { RumBtreeData btree; RumBtreeStack *stack; } RumPostingTreeScan; extern RumPostingTreeScan *rumPrepareScanPostingTree(Relation index, BlockNumber rootBlkno, bool searchMode, ScanDirection scanDirection, OffsetNumber attnum, RumState * rumstate); extern void rumInsertItemPointers(RumState * rumstate, OffsetNumber attnum, RumPostingTreeScan * gdi, RumItem * items, uint32 nitem, GinStatsData *buildStats); extern Buffer rumScanBeginPostingTree(RumPostingTreeScan * gdi, RumItem *item); extern void rumDataFillRoot(RumBtree btree, Buffer root, Buffer lbuf, Buffer rbuf, Page page, Page lpage, Page rpage); extern void rumPrepareDataScan(RumBtree btree, Relation index, OffsetNumber attnum, RumState * rumstate); /* rumscan.c */ /* * RumScanKeyData describes a single RUM index qualifier expression. * * From each qual expression, we extract one or more specific index search * conditions, which are represented by RumScanEntryData. 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 RumScanEntry --- this is particularly important for efficiency * when dealing with full-index-scan entries. So there can be multiple * RumScanKeyData.scanEntry pointers to the same RumScanEntryData. * * In each RumScanKeyData, 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 RumScanKeyData *RumScanKey; typedef struct RumScanEntryData *RumScanEntry; typedef struct RumScanKeyData { /* Real number of entries in scanEntry[] (always > 0) */ uint32 nentries; /* Number of entries that extractQueryFn and consistentFn know about */ uint32 nuserentries; /* array of RumScanEntry pointers, one per extracted search condition */ RumScanEntry *scanEntry; /* array of check flags, reported to consistentFn */ bool *entryRes; /* array of additional information, used in consistentFn and orderingFn */ Datum *addInfo; bool *addInfoIsNull; /* additional information, used in outerOrderingFn */ bool useAddToColumn; Datum outerAddInfo; bool outerAddInfoIsNull; /* other data needed for calling consistentFn */ Datum query; /* NB: these three arrays have only nuserentries elements! */ Datum *queryValues; RumNullCategory *queryCategories; Pointer *extra_data; StrategyNumber strategy; int32 searchMode; OffsetNumber attnum; OffsetNumber attnumOrig; /* * 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. */ RumItem curItem; bool curItemMatches; bool recheckCurItem; bool isFinished; bool orderBy; bool willSort; /* just a copy of RumScanOpaqueData.willSort */ ScanDirection scanDirection; /* array of keys, used to scan using additional information as keys */ RumScanKey *addInfoKeys; int addInfoNKeys; } RumScanKeyData; typedef struct RumScanEntryData { /* query key and other information from extractQueryFn */ Datum queryKey; RumNullCategory queryCategory; bool isPartialMatch; Pointer extra_data; StrategyNumber strategy; int32 searchMode; OffsetNumber attnum; OffsetNumber attnumOrig; /* Current page in posting tree */ Buffer buffer; /* current ItemPointer to heap */ RumItem curItem; /* * For a partial-match or full-scan query, we accumulate all TIDs and * and additional information here */ Tuplesortstate *matchSortstate; RumItem collectRumItem; /* for full-scan query with order-by */ RumBtreeStack *stack; bool scanWithAddInfo; /* used for Posting list and one page in Posting tree */ RumItem *list; int16 nlist; int16 offset; ScanDirection scanDirection; bool isFinished; bool reduceResult; uint32 predictNumberResult; /* used to scan posting tree */ RumPostingTreeScan *gdi; /* used in fast scan in addition to preConsistentFn */ bool preValue; /* Find by AddInfo */ bool useMarkAddInfo; RumItem markAddInfo; } RumScanEntryData; typedef struct { ItemPointerData iptr; float8 distance; bool recheck; } RumOrderingItem; typedef enum { RumFastScan, RumRegularScan, RumFullScan } RumScanType; typedef struct RumScanOpaqueData { /* tempCtx is used to hold consistent and ordering functions data */ MemoryContext tempCtx; /* keyCtx is used to hold key and entry data */ MemoryContext keyCtx; RumState rumstate; RumScanKey *keys; /* one per scan qualifier expr */ uint32 nkeys; RumScanEntry *entries; /* one per index search condition */ RumScanEntry *sortedEntries; /* Sorted entries. Used in fast scan */ int entriesIncrIndex; /* used in fast scan */ uint32 totalentries; uint32 allocentries; /* allocated length of entries[] and sortedEntries[] */ Tuplesortstate *sortstate; int norderbys; /* Number of columns in ordering. Will be assigned to sortstate->nKeys */ RumItem item; /* current item used in index scan */ bool firstCall; bool isVoidRes; /* true if query is unsatisfiable */ bool willSort; /* is there any columns in ordering */ RumScanType scanType; ScanDirection naturalOrder; bool secondPass; } RumScanOpaqueData; typedef RumScanOpaqueData *RumScanOpaque; extern IndexScanDesc rumbeginscan(Relation rel, int nkeys, int norderbys); extern void rumendscan(IndexScanDesc scan); extern void rumrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys); extern Datum rummarkpos(PG_FUNCTION_ARGS); extern Datum rumrestrpos(PG_FUNCTION_ARGS); extern void rumNewScanKey(IndexScanDesc scan); extern void freeScanKeys(RumScanOpaque so); /* rumget.c */ extern int64 rumgetbitmap(IndexScanDesc scan, TIDBitmap *tbm); extern bool rumgettuple(IndexScanDesc scan, ScanDirection direction); /* rumvacuum.c */ extern IndexBulkDeleteResult *rumbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state); extern IndexBulkDeleteResult *rumvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); /* rumvalidate.c */ extern bool rumvalidate(Oid opclassoid); /* rumbulk.c */ typedef struct RumEntryAccumulator { RBNode rbnode; Datum key; RumNullCategory category; OffsetNumber attnum; bool shouldSort; RumItem *list; uint32 maxcount; /* allocated size of list[] */ uint32 count; /* current number of list[] entries */ } RumEntryAccumulator; typedef struct { RumState *rumstate; long allocatedMemory; RumEntryAccumulator *entryallocator; uint32 eas_used; RBTree *tree; #if PG_VERSION_NUM >= 100000 RBTreeIterator tree_walk; #endif RumItem *sortSpace; uint32 sortSpaceN; } BuildAccumulator; extern void rumInitBA(BuildAccumulator *accum); extern void rumInsertBAEntries(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, Datum *addInfo, bool *addInfoIsNull, RumNullCategory * categories, int32 nentries); extern void rumBeginBAScan(BuildAccumulator *accum); extern RumItem *rumGetBAEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *key, RumNullCategory * category, uint32 *n); /* rum_ts_utils.c */ #define RUM_CONFIG_PROC 6 #define RUM_PRE_CONSISTENT_PROC 7 #define RUM_ORDERING_PROC 8 #define RUM_OUTER_ORDERING_PROC 9 #define RUM_ADDINFO_JOIN 10 #define RUMNProcs 10 extern Datum rum_extract_tsvector(PG_FUNCTION_ARGS); extern Datum rum_extract_tsquery(PG_FUNCTION_ARGS); extern Datum rum_tsvector_config(PG_FUNCTION_ARGS); extern Datum rum_tsquery_pre_consistent(PG_FUNCTION_ARGS); extern Datum rum_tsquery_distance(PG_FUNCTION_ARGS); extern Datum rum_ts_distance_tt(PG_FUNCTION_ARGS); extern Datum rum_ts_distance_ttf(PG_FUNCTION_ARGS); extern Datum rum_ts_distance_td(PG_FUNCTION_ARGS); extern Datum tsquery_to_distance_query(PG_FUNCTION_ARGS); /* GUC parameters */ extern PGDLLIMPORT int RumFuzzySearchLimit; /* * Functions for reading ItemPointers with additional information. Used in * various .c files and have to be inline for being fast. */ #define SEVENTHBIT (0x40) #define SIXMASK (0x3F) /* * Read next item pointer from leaf data page. Replaces current item pointer * with the next one. Zero item pointer should be passed in order to read the * first item pointer. Also reads value of addInfoIsNull flag which is stored * with item pointer. */ static inline char * rumDataPageLeafReadItemPointer(char *ptr, ItemPointer iptr, bool *addInfoIsNull) { uint32 blockNumberIncr = 0; uint16 offset = 0; int i; uint8 v; i = 0; do { v = *ptr; ptr++; blockNumberIncr |= (v & (~HIGHBIT)) << i; Assert(i < 28 || ((i == 28) && ((v & (~HIGHBIT)) < (1 << 4)))); i += 7; } while (v & HIGHBIT); Assert((uint64) iptr->ip_blkid.bi_lo + ((uint64) iptr->ip_blkid.bi_hi << 16) + (uint64) blockNumberIncr < ((uint64) 1 << 32)); blockNumberIncr += iptr->ip_blkid.bi_lo + (iptr->ip_blkid.bi_hi << 16); iptr->ip_blkid.bi_lo = blockNumberIncr & 0xFFFF; iptr->ip_blkid.bi_hi = (blockNumberIncr >> 16) & 0xFFFF; i = 0; while (true) { v = *ptr; ptr++; Assert(i < 14 || ((i == 14) && ((v & SIXMASK) < (1 << 2)))); if (v & HIGHBIT) { offset |= (v & (~HIGHBIT)) << i; } else { offset |= (v & SIXMASK) << i; if (addInfoIsNull) *addInfoIsNull = (v & SEVENTHBIT) ? true : false; break; } i += 7; } Assert(OffsetNumberIsValid(offset)); iptr->ip_posid = offset; return ptr; } /* * Reads next item pointer and additional information from leaf data page. * Replaces current item pointer with the next one. Zero item pointer should be * passed in order to read the first item pointer. */ static inline Pointer rumDataPageLeafRead(Pointer ptr, OffsetNumber attnum, RumItem * item, RumState * rumstate) { Form_pg_attribute attr; if (rumstate->useAlternativeOrder) { memcpy(&item->iptr, ptr, sizeof(ItemPointerData)); ptr += sizeof(ItemPointerData); if (item->iptr.ip_posid & ALT_ADD_INFO_NULL_FLAG) { item->iptr.ip_posid &= ~ALT_ADD_INFO_NULL_FLAG; item->addInfoIsNull = true; } else { item->addInfoIsNull = false; } } else ptr = rumDataPageLeafReadItemPointer(ptr, &item->iptr, &item->addInfoIsNull); Assert(item->iptr.ip_posid != InvalidOffsetNumber); if (!item->addInfoIsNull) { attr = rumstate->addAttrs[attnum - 1]; Assert(attr); if (attr->attbyval) { /* do not use aligment for pass-by-value types */ union { int16 i16; int32 i32; } u; switch (attr->attlen) { case sizeof(char): item->addInfo = Int8GetDatum(*ptr); break; case sizeof(int16): memcpy(&u.i16, ptr, sizeof(int16)); item->addInfo = Int16GetDatum(u.i16); break; case sizeof(int32): memcpy(&u.i32, ptr, sizeof(int32)); item->addInfo = Int32GetDatum(u.i32); break; #if SIZEOF_DATUM == 8 case sizeof(Datum): memcpy(&item->addInfo, ptr, sizeof(Datum)); break; #endif default: elog(ERROR, "unsupported byval length: %d", (int) (attr->attlen)); } } else { ptr = (Pointer) att_align_pointer(ptr, attr->attalign, attr->attlen, ptr); item->addInfo = fetch_att(ptr, attr->attbyval, attr->attlen); } ptr = (Pointer) att_addlength_pointer(ptr, attr->attlen, ptr); } return ptr; } /* * Reads next item pointer from leaf data page. * Replaces current item pointer with the next one. Zero item pointer should be * passed in order to read the first item pointer. */ static inline Pointer rumDataPageLeafReadPointer(Pointer ptr, OffsetNumber attnum, RumItem * item, RumState * rumstate) { Form_pg_attribute attr; if (rumstate->useAlternativeOrder) { memcpy(&item->iptr, ptr, sizeof(ItemPointerData)); ptr += sizeof(ItemPointerData); if (item->iptr.ip_posid & ALT_ADD_INFO_NULL_FLAG) { item->iptr.ip_posid &= ~ALT_ADD_INFO_NULL_FLAG; item->addInfoIsNull = true; } else { item->addInfoIsNull = false; } } else ptr = rumDataPageLeafReadItemPointer(ptr, &item->iptr, &item->addInfoIsNull); Assert(item->iptr.ip_posid != InvalidOffsetNumber); if (!item->addInfoIsNull) { attr = rumstate->addAttrs[attnum - 1]; Assert(attr); if (!attr->attbyval) ptr = (Pointer) att_align_pointer(ptr, attr->attalign, attr->attlen, ptr); ptr = (Pointer) att_addlength_pointer(ptr, attr->attlen, ptr); } return ptr; } extern Datum FunctionCall10Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5, Datum arg6, Datum arg7, Datum arg8, Datum arg9, Datum arg10); #endif /* __RUM_H__ */