text: /*--------------------------------------------------------------------------
   * gin_private.h
   *	  header file for postgres inverted index access method implementation.
   *
   *	Copyright (c) 2006-2018, 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 "fmgr.h"
  #include "storage/bufmgr.h"
  #include "lib/rbtree.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) \
  	((relation)->rd_options ? \
  	 ((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
  #define GinGetPendingListCleanupSize(relation) \
  	((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);
  extern void GinCheckForSerializableConflictIn(Relation relation,
  				 HeapTuple tuple, Buffer buffer);
  
  /* 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,
  		  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, 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);
  extern void ginPrepareDataScan(GinBtree btree, Relation index, BlockNumber rootBlkno);
  
  /*
   * 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;
  
  	/*
  	 * 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);
  
  /* ginbulk.c */
  typedef struct GinEntryAccumulator
  {
  	RBNode		rbnode;
  	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 ptrs, int nptrs,
  					   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 */
  