Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions

gin_private.h File Reference

#include "access/genam.h"
#include "access/gin.h"
#include "access/itup.h"
#include "fmgr.h"
#include "storage/bufmgr.h"
#include "utils/rbtree.h"
Include dependency graph for gin_private.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  GinPageOpaqueData
struct  GinMetaPageData
struct  PostingItem
struct  GinOptions
struct  GinState
struct  ginxlogCreatePostingTree
struct  ginxlogInsert
struct  ginxlogSplit
struct  ginxlogVacuumPage
struct  ginxlogDeletePage
struct  ginxlogUpdateMeta
struct  ginxlogInsertListPage
struct  ginxlogDeleteListPages
struct  GinBtreeStack
struct  GinBtreeData
struct  GinPostingTreeScan
struct  GinScanKeyData
struct  GinScanEntryData
struct  GinScanOpaqueData
struct  GinEntryAccumulator
struct  BuildAccumulator
struct  GinTupleCollector

Defines

#define GIN_DATA   (1 << 0)
#define GIN_LEAF   (1 << 1)
#define GIN_DELETED   (1 << 2)
#define GIN_META   (1 << 3)
#define GIN_LIST   (1 << 4)
#define GIN_LIST_FULLROW   (1 << 5)
#define GIN_METAPAGE_BLKNO   (0)
#define GIN_ROOT_BLKNO   (1)
#define GIN_CURRENT_VERSION   1
#define GinPageGetMeta(p)   ((GinMetaPageData *) PageGetContents(p))
#define GinPageGetOpaque(page)   ( (GinPageOpaque) PageGetSpecialPointer(page) )
#define GinPageIsLeaf(page)   ( GinPageGetOpaque(page)->flags & GIN_LEAF )
#define GinPageSetLeaf(page)   ( GinPageGetOpaque(page)->flags |= GIN_LEAF )
#define GinPageSetNonLeaf(page)   ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )
#define GinPageIsData(page)   ( GinPageGetOpaque(page)->flags & GIN_DATA )
#define GinPageSetData(page)   ( GinPageGetOpaque(page)->flags |= GIN_DATA )
#define GinPageIsList(page)   ( GinPageGetOpaque(page)->flags & GIN_LIST )
#define GinPageSetList(page)   ( GinPageGetOpaque(page)->flags |= GIN_LIST )
#define GinPageHasFullRow(page)   ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )
#define GinPageSetFullRow(page)   ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )
#define GinPageIsDeleted(page)   ( GinPageGetOpaque(page)->flags & GIN_DELETED)
#define GinPageSetDeleted(page)   ( GinPageGetOpaque(page)->flags |= GIN_DELETED)
#define GinPageSetNonDeleted(page)   ( GinPageGetOpaque(page)->flags &= ~GIN_DELETED)
#define GinPageRightMost(page)   ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber)
#define GinItemPointerGetBlockNumber(pointer)   BlockIdGetBlockNumber(&(pointer)->ip_blkid)
#define GinItemPointerGetOffsetNumber(pointer)   ((pointer)->ip_posid)
#define ItemPointerSetMin(p)   ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0)
#define ItemPointerIsMin(p)
#define ItemPointerSetMax(p)   ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff)
#define ItemPointerIsMax(p)
#define ItemPointerSetLossyPage(p, b)   ItemPointerSet((p), (b), (OffsetNumber)0xffff)
#define ItemPointerIsLossyPage(p)
#define PostingItemGetBlockNumber(pointer)   BlockIdGetBlockNumber(&(pointer)->child_blkno)
#define PostingItemSetBlockNumber(pointer, blockNumber)   BlockIdSet(&((pointer)->child_blkno), (blockNumber))
#define GIN_CAT_NORM_KEY   0
#define GIN_CAT_NULL_KEY   1
#define GIN_CAT_EMPTY_ITEM   2
#define GIN_CAT_NULL_ITEM   3
#define GIN_CAT_EMPTY_QUERY   (-1)
#define GinCategoryOffset(itup, ginstate)
#define GinGetNullCategory(itup, ginstate)   (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))))
#define GinSetNullCategory(itup, ginstate, c)   (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))) = (c))
#define GinGetNPosting(itup)   GinItemPointerGetOffsetNumber(&(itup)->t_tid)
#define GinSetNPosting(itup, n)   ItemPointerSetOffsetNumber(&(itup)->t_tid,n)
#define GIN_TREE_POSTING   ((OffsetNumber)0xffff)
#define GinIsPostingTree(itup)   (GinGetNPosting(itup) == GIN_TREE_POSTING)
#define GinSetPostingTree(itup, blkno)   ( GinSetNPosting((itup),GIN_TREE_POSTING), ItemPointerSetBlockNumber(&(itup)->t_tid, blkno) )
#define GinGetPostingTree(itup)   GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinGetPostingOffset(itup)   GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetPostingOffset(itup, n)   ItemPointerSetBlockNumber(&(itup)->t_tid,n)
#define GinGetPosting(itup)   ((ItemPointer) ((char*)(itup) + GinGetPostingOffset(itup)))
#define GinMaxItemSize
#define GinGetDownlink(itup)   GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetDownlink(itup, blkno)   ItemPointerSet(&(itup)->t_tid, blkno, InvalidOffsetNumber)
#define GinDataPageGetRightBound(page)   ((ItemPointer) PageGetContents(page))
#define GinDataPageGetData(page)   (PageGetContents(page) + MAXALIGN(sizeof(ItemPointerData)))
#define GinSizeOfDataPageItem(page)   (GinPageIsLeaf(page) ? sizeof(ItemPointerData) : sizeof(PostingItem))
#define GinDataPageGetItem(page, i)   (GinDataPageGetData(page) + ((i)-1) * GinSizeOfDataPageItem(page))
#define GinDataPageGetFreeSpace(page)
#define GinMaxLeafDataItems
#define GinListPageSize   ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
#define GIN_DEFAULT_USE_FASTUPDATE   true
#define GinGetUseFastUpdate(relation)
#define GIN_UNLOCK   BUFFER_LOCK_UNLOCK
#define GIN_SHARE   BUFFER_LOCK_SHARE
#define GIN_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE
#define XLOG_GIN_CREATE_INDEX   0x00
#define XLOG_GIN_CREATE_PTREE   0x10
#define XLOG_GIN_INSERT   0x20
#define XLOG_GIN_SPLIT   0x30
#define XLOG_GIN_VACUUM_PAGE   0x40
#define XLOG_GIN_DELETE_PAGE   0x50
#define XLOG_GIN_UPDATE_META_PAGE   0x60
#define XLOG_GIN_INSERT_LISTPAGE   0x70
#define XLOG_GIN_DELETE_LISTPAGE   0x80
#define GIN_NDELETE_AT_ONCE   16

Typedefs

typedef struct GinPageOpaqueData GinPageOpaqueData
typedef GinPageOpaqueDataGinPageOpaque
typedef struct GinMetaPageData GinMetaPageData
typedef signed char GinNullCategory
typedef struct GinOptions GinOptions
typedef struct GinState GinState
typedef struct
ginxlogCreatePostingTree 
ginxlogCreatePostingTree
typedef struct ginxlogInsert ginxlogInsert
typedef struct ginxlogSplit ginxlogSplit
typedef struct ginxlogVacuumPage ginxlogVacuumPage
typedef struct ginxlogDeletePage ginxlogDeletePage
typedef struct ginxlogUpdateMeta ginxlogUpdateMeta
typedef struct
ginxlogInsertListPage 
ginxlogInsertListPage
typedef struct
ginxlogDeleteListPages 
ginxlogDeleteListPages
typedef struct GinBtreeStack GinBtreeStack
typedef struct GinBtreeDataGinBtree
typedef struct GinBtreeData GinBtreeData
typedef struct GinScanKeyDataGinScanKey
typedef struct GinScanEntryDataGinScanEntry
typedef struct GinScanKeyData GinScanKeyData
typedef struct GinScanEntryData GinScanEntryData
typedef struct GinScanOpaqueData GinScanOpaqueData
typedef GinScanOpaqueDataGinScanOpaque
typedef struct GinEntryAccumulator GinEntryAccumulator
typedef struct GinTupleCollector GinTupleCollector

Functions

Datum ginoptions (PG_FUNCTION_ARGS)
void initGinState (GinState *state, Relation index)
Buffer GinNewBuffer (Relation index)
void GinInitBuffer (Buffer b, uint32 f)
void GinInitPage (Page page, uint32 f, Size pageSize)
void GinInitMetabuffer (Buffer b)
int ginCompareEntries (GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
int ginCompareAttEntries (GinState *ginstate, OffsetNumber attnuma, Datum a, GinNullCategory categorya, OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
DatumginExtractEntries (GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
OffsetNumber gintuple_get_attrnum (GinState *ginstate, IndexTuple tuple)
Datum gintuple_get_key (GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Datum ginbuild (PG_FUNCTION_ARGS)
Datum ginbuildempty (PG_FUNCTION_ARGS)
Datum gininsert (PG_FUNCTION_ARGS)
void ginEntryInsert (GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
GinBtreeStackginPrepareFindLeafPage (GinBtree btree, BlockNumber blkno)
GinBtreeStackginFindLeafPage (GinBtree btree, GinBtreeStack *stack)
void freeGinBtreeStack (GinBtreeStack *stack)
void ginInsertValue (GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
void ginFindParents (GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno)
IndexTuple GinFormTuple (GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *ipd, uint32 nipd, bool errorTooBig)
void GinShortenTuple (IndexTuple itup, uint32 nipd)
void ginPrepareEntryScan (GinBtree btree, OffsetNumber attnum, Datum key, GinNullCategory category, GinState *ginstate)
void ginEntryFillRoot (GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
IndexTuple ginPageGetLinkItup (Buffer buf)
int ginCompareItemPointers (ItemPointer a, ItemPointer b)
uint32 ginMergeItemPointers (ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb)
void GinDataPageAddItem (Page page, void *data, OffsetNumber offset)
void GinPageDeletePostingItem (Page page, OffsetNumber offset)
GinPostingTreeScanginPrepareScanPostingTree (Relation index, BlockNumber rootBlkno, bool searchMode)
void ginInsertItemPointers (GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
Buffer ginScanBeginPostingTree (GinPostingTreeScan *gdi)
void ginDataFillRoot (GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
void ginPrepareDataScan (GinBtree btree, Relation index)
Datum ginbeginscan (PG_FUNCTION_ARGS)
Datum ginendscan (PG_FUNCTION_ARGS)
Datum ginrescan (PG_FUNCTION_ARGS)
Datum ginmarkpos (PG_FUNCTION_ARGS)
Datum ginrestrpos (PG_FUNCTION_ARGS)
void ginNewScanKey (IndexScanDesc scan)
Datum gingetbitmap (PG_FUNCTION_ARGS)
Datum ginbulkdelete (PG_FUNCTION_ARGS)
Datum ginvacuumcleanup (PG_FUNCTION_ARGS)
void ginInitBA (BuildAccumulator *accum)
void ginInsertBAEntries (BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, GinNullCategory *categories, int32 nentries)
void ginBeginBAScan (BuildAccumulator *accum)
ItemPointerDataginGetBAEntry (BuildAccumulator *accum, OffsetNumber *attnum, Datum *key, GinNullCategory *category, uint32 *n)
void ginHeapTupleFastInsert (GinState *ginstate, GinTupleCollector *collector)
void ginHeapTupleFastCollect (GinState *ginstate, GinTupleCollector *collector, OffsetNumber attnum, Datum value, bool isNull, ItemPointer ht_ctid)
void ginInsertCleanup (GinState *ginstate, bool vac_delay, IndexBulkDeleteResult *stats)

Define Documentation

#define GIN_CAT_EMPTY_ITEM   2

Definition at line 189 of file gin_private.h.

#define GIN_CAT_EMPTY_QUERY   (-1)

Definition at line 191 of file gin_private.h.

Referenced by collectMatchesForHeapRow(), and startScanEntry().

#define GIN_CAT_NORM_KEY   0
#define GIN_CAT_NULL_ITEM   3

Definition at line 190 of file gin_private.h.

Referenced by collectMatchBitmap(), and collectMatchesForHeapRow().

#define GIN_CAT_NULL_KEY   1

Definition at line 188 of file gin_private.h.

Referenced by ginContinueSplit().

#define GIN_CURRENT_VERSION   1

Definition at line 99 of file gin_private.h.

#define GIN_DATA   (1 << 0)

Definition at line 45 of file gin_private.h.

Referenced by createPostingTree(), and ginRedoCreatePTree().

#define GIN_DEFAULT_USE_FASTUPDATE   true

Definition at line 267 of file gin_private.h.

#define GIN_DELETED   (1 << 2)

Definition at line 47 of file gin_private.h.

Referenced by findItemInPostingPage(), and scanPostingTree().

#define GIN_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE
#define GIN_LEAF   (1 << 1)
#define GIN_LIST   (1 << 4)

Definition at line 49 of file gin_private.h.

Referenced by ginRedoInsertListPage(), and writeListPage().

#define GIN_LIST_FULLROW   (1 << 5)

Definition at line 50 of file gin_private.h.

#define GIN_META   (1 << 3)

Definition at line 48 of file gin_private.h.

Referenced by GinInitMetabuffer().

#define GIN_METAPAGE_BLKNO   (0)
#define GIN_NDELETE_AT_ONCE   16

Definition at line 420 of file gin_private.h.

Referenced by shiftList().

#define GIN_ROOT_BLKNO   (1)
#define GIN_SHARE   BUFFER_LOCK_SHARE
#define GIN_TREE_POSTING   ((OffsetNumber)0xffff)

Definition at line 209 of file gin_private.h.

#define GIN_UNLOCK   BUFFER_LOCK_UNLOCK
#define GinCategoryOffset (   itup,
  ginstate 
)
Value:
(IndexInfoFindDataOffset((itup)->t_info) + \
     ((ginstate)->oneCol ? 0 : sizeof(int16)))

Definition at line 196 of file gin_private.h.

Referenced by GinFormTuple().

#define GinDataPageGetData (   page  )     (PageGetContents(page) + MAXALIGN(sizeof(ItemPointerData)))
#define GinDataPageGetFreeSpace (   page  ) 
Value:

Definition at line 240 of file gin_private.h.

Referenced by dataIsEnoughSpace(), and dataSplitPage().

#define GinDataPageGetItem (   page,
  i 
)    (GinDataPageGetData(page) + ((i)-1) * GinSizeOfDataPageItem(page))
#define GinDataPageGetRightBound (   page  )     ((ItemPointer) PageGetContents(page))

Definition at line 232 of file gin_private.h.

Referenced by dataIsMoveRight(), dataSplitPage(), ginDataFillRoot(), and ginRedoSplit().

#define GinGetDownlink (   itup  )     GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinGetNPosting (   itup  )     GinItemPointerGetOffsetNumber(&(itup)->t_tid)
#define GinGetNullCategory (   itup,
  ginstate 
)    (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))))

Definition at line 199 of file gin_private.h.

Referenced by gintuple_get_key().

#define GinGetPosting (   itup  )     ((ItemPointer) ((char*)(itup) + GinGetPostingOffset(itup)))
#define GinGetPostingOffset (   itup  )     GinItemPointerGetBlockNumber(&(itup)->t_tid)

Definition at line 214 of file gin_private.h.

Referenced by GinFormInteriorTuple(), and GinShortenTuple().

#define GinGetPostingTree (   itup  )     GinItemPointerGetBlockNumber(&(itup)->t_tid)

Definition at line 212 of file gin_private.h.

Referenced by collectMatchBitmap(), ginEntryInsert(), and startScanEntry().

#define GinGetUseFastUpdate (   relation  ) 
Value:
((relation)->rd_options ? \
     ((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)

Definition at line 268 of file gin_private.h.

Referenced by gininsert().

#define GinIsPostingTree (   itup  )     (GinGetNPosting(itup) == GIN_TREE_POSTING)
#define GinItemPointerGetBlockNumber (   pointer  )     BlockIdGetBlockNumber(&(pointer)->ip_blkid)

Definition at line 129 of file gin_private.h.

Referenced by ginCompareItemPointers(), keyGetItem(), and scanGetItem().

#define GinItemPointerGetOffsetNumber (   pointer  )     ((pointer)->ip_posid)

Definition at line 132 of file gin_private.h.

Referenced by ginCompareItemPointers().

#define GinListPageSize   ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )

Definition at line 255 of file gin_private.h.

Referenced by ginHeapTupleFastInsert(), and makeSublist().

#define GinMaxItemSize
Value:
MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
        MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData)))

Definition at line 218 of file gin_private.h.

Referenced by GinFormTuple().

#define GinMaxLeafDataItems
Value:

Definition at line 246 of file gin_private.h.

Referenced by buildFreshLeafTuple(), and createPostingTree().

#define GinPageGetMeta (   p  )     ((GinMetaPageData *) PageGetContents(p))
#define GinPageGetOpaque (   page  )     ( (GinPageOpaque) PageGetSpecialPointer(page) )
#define GinPageHasFullRow (   page  )     ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )

Definition at line 116 of file gin_private.h.

Referenced by collectMatchesForHeapRow(), ginInsertCleanup(), and scanGetCandidate().

#define GinPageIsData (   page  )     ( GinPageGetOpaque(page)->flags & GIN_DATA )
#define GinPageIsDeleted (   page  )     ( GinPageGetOpaque(page)->flags & GIN_DELETED)

Definition at line 119 of file gin_private.h.

Referenced by ginInsertCleanup(), GinNewBuffer(), ginvacuumcleanup(), and shiftList().

#define GinPageIsLeaf (   page  )     ( GinPageGetOpaque(page)->flags & GIN_LEAF )
#define GinPageIsList (   page  )     ( GinPageGetOpaque(page)->flags & GIN_LIST )

Definition at line 114 of file gin_private.h.

Referenced by ginvacuumcleanup().

#define GinPageRightMost (   page  )     ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber)
#define GinPageSetData (   page  )     ( GinPageGetOpaque(page)->flags |= GIN_DATA )

Definition at line 113 of file gin_private.h.

#define GinPageSetDeleted (   page  )     ( GinPageGetOpaque(page)->flags |= GIN_DELETED)

Definition at line 120 of file gin_private.h.

#define GinPageSetFullRow (   page  )     ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )

Definition at line 117 of file gin_private.h.

Referenced by ginRedoInsertListPage(), and writeListPage().

#define GinPageSetLeaf (   page  )     ( GinPageGetOpaque(page)->flags |= GIN_LEAF )

Definition at line 110 of file gin_private.h.

#define GinPageSetList (   page  )     ( GinPageGetOpaque(page)->flags |= GIN_LIST )

Definition at line 115 of file gin_private.h.

#define GinPageSetNonDeleted (   page  )     ( GinPageGetOpaque(page)->flags &= ~GIN_DELETED)

Definition at line 121 of file gin_private.h.

#define GinPageSetNonLeaf (   page  )     ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )

Definition at line 111 of file gin_private.h.

#define GinSetDownlink (   itup,
  blkno 
)    ItemPointerSet(&(itup)->t_tid, blkno, InvalidOffsetNumber)

Definition at line 226 of file gin_private.h.

Referenced by entryPreparePage(), GinFormInteriorTuple(), and ginRedoInsert().

#define GinSetNPosting (   itup,
  n 
)    ItemPointerSetOffsetNumber(&(itup)->t_tid,n)

Definition at line 208 of file gin_private.h.

Referenced by GinFormTuple(), and GinShortenTuple().

#define GinSetNullCategory (   itup,
  ginstate,
  c 
)    (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))) = (c))

Definition at line 201 of file gin_private.h.

Referenced by GinFormTuple().

#define GinSetPostingOffset (   itup,
  n 
)    ItemPointerSetBlockNumber(&(itup)->t_tid,n)

Definition at line 215 of file gin_private.h.

Referenced by GinFormTuple().

#define GinSetPostingTree (   itup,
  blkno 
)    ( GinSetNPosting((itup),GIN_TREE_POSTING), ItemPointerSetBlockNumber(&(itup)->t_tid, blkno) )

Definition at line 211 of file gin_private.h.

Referenced by addItemPointersToLeafTuple(), and buildFreshLeafTuple().

#define GinSizeOfDataPageItem (   page  )     (GinPageIsLeaf(page) ? sizeof(ItemPointerData) : sizeof(PostingItem))
#define ItemPointerIsLossyPage (   p  ) 
Value:
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
     GinItemPointerGetBlockNumber(p) != InvalidBlockNumber)

Definition at line 157 of file gin_private.h.

Referenced by gingetbitmap(), and scanGetItem().

#define ItemPointerIsMax (   p  ) 
Value:
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
     GinItemPointerGetBlockNumber(p) == InvalidBlockNumber)

Definition at line 152 of file gin_private.h.

Referenced by keyGetItem(), and scanGetItem().

#define ItemPointerIsMin (   p  ) 
Value:
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0 && \
     GinItemPointerGetBlockNumber(p) == (BlockNumber)0)

Definition at line 147 of file gin_private.h.

#define ItemPointerSetLossyPage (   p,
  b 
)    ItemPointerSet((p), (b), (OffsetNumber)0xffff)

Definition at line 155 of file gin_private.h.

Referenced by entryGetItem(), and keyGetItem().

#define ItemPointerSetMax (   p  )     ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff)

Definition at line 150 of file gin_private.h.

Referenced by keyGetItem(), and scanGetItem().

#define ItemPointerSetMin (   p  )     ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0)
#define PostingItemGetBlockNumber (   pointer  )     BlockIdGetBlockNumber(&(pointer)->child_blkno)
#define PostingItemSetBlockNumber (   pointer,
  blockNumber 
)    BlockIdSet(&((pointer)->child_blkno), (blockNumber))
#define XLOG_GIN_CREATE_INDEX   0x00

Definition at line 317 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and ginbuild().

#define XLOG_GIN_CREATE_PTREE   0x10

Definition at line 319 of file gin_private.h.

Referenced by createPostingTree(), gin_desc(), and gin_redo().

#define XLOG_GIN_DELETE_LISTPAGE   0x80

Definition at line 418 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and shiftList().

#define XLOG_GIN_DELETE_PAGE   0x50

Definition at line 381 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and ginDeletePage().

#define XLOG_GIN_INSERT   0x20

Definition at line 329 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and ginInsertValue().

#define XLOG_GIN_INSERT_LISTPAGE   0x70

Definition at line 407 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and writeListPage().

#define XLOG_GIN_SPLIT   0x30

Definition at line 348 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and ginInsertValue().

#define XLOG_GIN_UPDATE_META_PAGE   0x60

Definition at line 393 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), ginHeapTupleFastInsert(), and ginUpdateStats().

#define XLOG_GIN_VACUUM_PAGE   0x40

Definition at line 371 of file gin_private.h.

Referenced by gin_desc(), gin_redo(), and xlogVacuumPage().


Typedef Documentation

typedef struct GinBtreeData* GinBtree

Definition at line 472 of file gin_private.h.

typedef struct GinBtreeData GinBtreeData
typedef struct GinBtreeStack GinBtreeStack
typedef signed char GinNullCategory

Definition at line 185 of file gin_private.h.

typedef struct GinOptions GinOptions

Definition at line 43 of file gin_private.h.

typedef struct GinScanEntryData* GinScanEntry

Definition at line 575 of file gin_private.h.

typedef struct GinScanKeyData* GinScanKey

Definition at line 573 of file gin_private.h.

Definition at line 660 of file gin_private.h.

typedef struct GinState GinState
typedef struct ginxlogInsert ginxlogInsert
typedef struct ginxlogSplit ginxlogSplit

Function Documentation

void freeGinBtreeStack ( GinBtreeStack stack  ) 

Definition at line 152 of file ginbtree.c.

References GinBtreeStack::buffer, InvalidBuffer, GinBtreeStack::parent, pfree(), and ReleaseBuffer().

Referenced by ginEntryInsert(), ginInsertItemPointers(), ginInsertValue(), scanPostingTree(), and startScanEntry().

{
    while (stack)
    {
        GinBtreeStack *tmp = stack->parent;

        if (stack->buffer != InvalidBuffer)
            ReleaseBuffer(stack->buffer);

        pfree(stack);
        stack = tmp;
    }
}

void ginBeginBAScan ( BuildAccumulator accum  ) 
Datum ginbeginscan ( PG_FUNCTION_ARGS   ) 
Datum ginbuild ( PG_FUNCTION_ARGS   ) 

Definition at line 381 of file gininsert.c.

References GinBuildState::accum, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), XLogRecData::buffer, BufferGetPage, GinBuildState::buildStats, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, XLogRecData::data, elog, END_CRIT_SECTION, ERROR, GinBuildState::funcCtx, GIN_LEAF, ginBeginBAScan(), ginBuildCallback(), ginEntryInsert(), ginGetBAEntry(), ginInitBA(), GinInitBuffer(), GinInitMetabuffer(), GinNewBuffer(), BuildAccumulator::ginstate, GinBuildState::ginstate, ginUpdateStats(), IndexBuildResult::heap_tuples, IndexBuildResult::index_tuples, IndexBuildHeapScan(), GinBuildState::indtuples, initGinState(), XLogRecData::len, sort-test::list, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), GinStatsData::nEntryPages, XLogRecData::next, NULL, PageSetLSN, palloc(), PG_GETARG_POINTER, PG_RETURN_POINTER, RelationData::rd_node, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, GinBuildState::tmpCtx, UnlockReleaseBuffer(), XLOG_GIN_CREATE_INDEX, and XLogInsert().

{
    Relation    heap = (Relation) PG_GETARG_POINTER(0);
    Relation    index = (Relation) PG_GETARG_POINTER(1);
    IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
    IndexBuildResult *result;
    double      reltuples;
    GinBuildState buildstate;
    Buffer      RootBuffer,
                MetaBuffer;
    ItemPointerData *list;
    Datum       key;
    GinNullCategory category;
    uint32      nlist;
    MemoryContext oldCtx;
    OffsetNumber attnum;

    if (RelationGetNumberOfBlocks(index) != 0)
        elog(ERROR, "index \"%s\" already contains data",
             RelationGetRelationName(index));

    initGinState(&buildstate.ginstate, index);
    buildstate.indtuples = 0;
    memset(&buildstate.buildStats, 0, sizeof(GinStatsData));

    /* initialize the meta page */
    MetaBuffer = GinNewBuffer(index);

    /* initialize the root page */
    RootBuffer = GinNewBuffer(index);

    START_CRIT_SECTION();
    GinInitMetabuffer(MetaBuffer);
    MarkBufferDirty(MetaBuffer);
    GinInitBuffer(RootBuffer, GIN_LEAF);
    MarkBufferDirty(RootBuffer);

    if (RelationNeedsWAL(index))
    {
        XLogRecPtr  recptr;
        XLogRecData rdata;
        Page        page;

        rdata.buffer = InvalidBuffer;
        rdata.data = (char *) &(index->rd_node);
        rdata.len = sizeof(RelFileNode);
        rdata.next = NULL;

        recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX, &rdata);

        page = BufferGetPage(RootBuffer);
        PageSetLSN(page, recptr);

        page = BufferGetPage(MetaBuffer);
        PageSetLSN(page, recptr);
    }

    UnlockReleaseBuffer(MetaBuffer);
    UnlockReleaseBuffer(RootBuffer);
    END_CRIT_SECTION();

    /* count the root as first entry page */
    buildstate.buildStats.nEntryPages++;

    /*
     * create a temporary memory context that is reset once for each tuple
     * inserted into the index
     */
    buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
                                              "Gin build temporary context",
                                              ALLOCSET_DEFAULT_MINSIZE,
                                              ALLOCSET_DEFAULT_INITSIZE,
                                              ALLOCSET_DEFAULT_MAXSIZE);

    buildstate.funcCtx = AllocSetContextCreate(buildstate.tmpCtx,
                     "Gin build temporary context for user-defined function",
                                               ALLOCSET_DEFAULT_MINSIZE,
                                               ALLOCSET_DEFAULT_INITSIZE,
                                               ALLOCSET_DEFAULT_MAXSIZE);

    buildstate.accum.ginstate = &buildstate.ginstate;
    ginInitBA(&buildstate.accum);

    /*
     * Do the heap scan.  We disallow sync scan here because dataPlaceToPage
     * prefers to receive tuples in TID order.
     */
    reltuples = IndexBuildHeapScan(heap, index, indexInfo, false,
                                   ginBuildCallback, (void *) &buildstate);

    /* dump remaining entries to the index */
    oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
    ginBeginBAScan(&buildstate.accum);
    while ((list = ginGetBAEntry(&buildstate.accum,
                                 &attnum, &key, &category, &nlist)) != NULL)
    {
        /* there could be many entries, so be willing to abort here */
        CHECK_FOR_INTERRUPTS();
        ginEntryInsert(&buildstate.ginstate, attnum, key, category,
                       list, nlist, &buildstate.buildStats);
    }
    MemoryContextSwitchTo(oldCtx);

    MemoryContextDelete(buildstate.tmpCtx);

    /*
     * Update metapage stats
     */
    buildstate.buildStats.nTotalPages = RelationGetNumberOfBlocks(index);
    ginUpdateStats(index, &buildstate.buildStats);

    /*
     * Return statistics
     */
    result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));

    result->heap_tuples = reltuples;
    result->index_tuples = buildstate.indtuples;

    PG_RETURN_POINTER(result);
}

Datum ginbuildempty ( PG_FUNCTION_ARGS   ) 

Definition at line 507 of file gininsert.c.

References BUFFER_LOCK_EXCLUSIVE, END_CRIT_SECTION, GIN_LEAF, GinInitBuffer(), GinInitMetabuffer(), INIT_FORKNUM, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), NULL, P_NEW, PG_GETARG_POINTER, PG_RETURN_VOID, RBM_NORMAL, ReadBufferExtended(), START_CRIT_SECTION, and UnlockReleaseBuffer().

{
    Relation    index = (Relation) PG_GETARG_POINTER(0);
    Buffer      RootBuffer,
                MetaBuffer;

    /* An empty GIN index has two pages. */
    MetaBuffer =
        ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
    LockBuffer(MetaBuffer, BUFFER_LOCK_EXCLUSIVE);
    RootBuffer =
        ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
    LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE);

    /* Initialize and xlog metabuffer and root buffer. */
    START_CRIT_SECTION();
    GinInitMetabuffer(MetaBuffer);
    MarkBufferDirty(MetaBuffer);
    log_newpage_buffer(MetaBuffer);
    GinInitBuffer(RootBuffer, GIN_LEAF);
    MarkBufferDirty(RootBuffer);
    log_newpage_buffer(RootBuffer);
    END_CRIT_SECTION();

    /* Unlock and release the buffers. */
    UnlockReleaseBuffer(MetaBuffer);
    UnlockReleaseBuffer(RootBuffer);

    PG_RETURN_VOID();
}

Datum ginbulkdelete ( PG_FUNCTION_ARGS   ) 

Definition at line 577 of file ginvacuum.c.

References Assert, DataPageDeleteStack::blkno, BufferGetPage, GinVacuumState::callback, callback(), GinVacuumState::callback_state, END_CRIT_SECTION, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_ROOT_BLKNO, GIN_SHARE, GIN_UNLOCK, GinGetDownlink, ginInsertCleanup(), GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, GinVacuumState::ginstate, ginVacuumEntryPage(), ginVacuumPostingTree(), i, GinVacuumState::index, IndexVacuumInfo::index, initGinState(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, MarkBufferDirty(), NULL, IndexBulkDeleteResult::num_index_tuples, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageRestoreTempPage(), palloc0(), PG_GETARG_POINTER, PG_RETURN_POINTER, RBM_NORMAL, ReadBufferExtended(), GinVacuumState::result, START_CRIT_SECTION, IndexVacuumInfo::strategy, GinVacuumState::strategy, UnlockReleaseBuffer(), vacuum_delay_point(), and xlogVacuumPage().

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
    void       *callback_state = (void *) PG_GETARG_POINTER(3);
    Relation    index = info->index;
    BlockNumber blkno = GIN_ROOT_BLKNO;
    GinVacuumState gvs;
    Buffer      buffer;
    BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
    uint32      nRoot;

    gvs.index = index;
    gvs.callback = callback;
    gvs.callback_state = callback_state;
    gvs.strategy = info->strategy;
    initGinState(&gvs.ginstate, index);

    /* first time through? */
    if (stats == NULL)
    {
        /* Yes, so initialize stats to zeroes */
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
        /* and cleanup any pending inserts */
        ginInsertCleanup(&gvs.ginstate, true, stats);
    }

    /* we'll re-count the tuples each time */
    stats->num_index_tuples = 0;
    gvs.result = stats;

    buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                RBM_NORMAL, info->strategy);

    /* find leaf page */
    for (;;)
    {
        Page        page = BufferGetPage(buffer);
        IndexTuple  itup;

        LockBuffer(buffer, GIN_SHARE);

        Assert(!GinPageIsData(page));

        if (GinPageIsLeaf(page))
        {
            LockBuffer(buffer, GIN_UNLOCK);
            LockBuffer(buffer, GIN_EXCLUSIVE);

            if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
            {
                LockBuffer(buffer, GIN_UNLOCK);
                continue;       /* check it one more */
            }
            break;
        }

        Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);

        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
        blkno = GinGetDownlink(itup);
        Assert(blkno != InvalidBlockNumber);

        UnlockReleaseBuffer(buffer);
        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
    }

    /* right now we found leftmost page in entry's BTree */

    for (;;)
    {
        Page        page = BufferGetPage(buffer);
        Page        resPage;
        uint32      i;

        Assert(!GinPageIsData(page));

        resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);

        blkno = GinPageGetOpaque(page)->rightlink;

        if (resPage)
        {
            START_CRIT_SECTION();
            PageRestoreTempPage(resPage, page);
            MarkBufferDirty(buffer);
            xlogVacuumPage(gvs.index, buffer);
            UnlockReleaseBuffer(buffer);
            END_CRIT_SECTION();
        }
        else
        {
            UnlockReleaseBuffer(buffer);
        }

        vacuum_delay_point();

        for (i = 0; i < nRoot; i++)
        {
            ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
            vacuum_delay_point();
        }

        if (blkno == InvalidBlockNumber)        /* rightmost page */
            break;

        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
        LockBuffer(buffer, GIN_EXCLUSIVE);
    }

    PG_RETURN_POINTER(gvs.result);
}

int ginCompareAttEntries ( GinState ginstate,
OffsetNumber  attnuma,
Datum  a,
GinNullCategory  categorya,
OffsetNumber  attnumb,
Datum  b,
GinNullCategory  categoryb 
)

Definition at line 302 of file ginutil.c.

References ginCompareEntries().

Referenced by cmpEntryAccumulator(), entryIsMoveRight(), entryLocateEntry(), and entryLocateLeafEntry().

{
    /* attribute number is the first sort key */
    if (attnuma != attnumb)
        return (attnuma < attnumb) ? -1 : 1;

    return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
}

int ginCompareEntries ( GinState ginstate,
OffsetNumber  attnum,
Datum  a,
GinNullCategory  categorya,
Datum  b,
GinNullCategory  categoryb 
)

Definition at line 280 of file ginutil.c.

References GinState::compareFn, DatumGetInt32, FunctionCall2Coll(), GIN_CAT_NORM_KEY, and GinState::supportCollation.

Referenced by collectMatchBitmap(), collectMatchesForHeapRow(), ginCompareAttEntries(), and ginFillScanEntry().

{
    /* if not of same null category, sort by that first */
    if (categorya != categoryb)
        return (categorya < categoryb) ? -1 : 1;

    /* all null items in same category are equal */
    if (categorya != GIN_CAT_NORM_KEY)
        return 0;

    /* both not null, so safe to call the compareFn */
    return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
                                      ginstate->supportCollation[attnum - 1],
                                           a, b));
}

int ginCompareItemPointers ( ItemPointer  a,
ItemPointer  b 
)
void ginDataFillRoot ( GinBtree  btree,
Buffer  root,
Buffer  lbuf,
Buffer  rbuf 
)
void GinDataPageAddItem ( Page  page,
void *  data,
OffsetNumber  offset 
)

Definition at line 282 of file gindatapage.c.

References GinDataPageGetItem, GinPageGetOpaque, GinSizeOfDataPageItem, InvalidOffsetNumber, and memmove.

Referenced by dataPlaceToPage(), ginDataFillRoot(), ginRedoInsert(), and ginRedoSplit().

{
    OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
    char       *ptr;

    if (offset == InvalidOffsetNumber)
    {
        ptr = GinDataPageGetItem(page, maxoff + 1);
    }
    else
    {
        ptr = GinDataPageGetItem(page, offset);
        if (maxoff + 1 - offset != 0)
            memmove(ptr + GinSizeOfDataPageItem(page),
                    ptr,
                    (maxoff - offset + 1) * GinSizeOfDataPageItem(page));
    }
    memcpy(ptr, data, GinSizeOfDataPageItem(page));

    GinPageGetOpaque(page)->maxoff++;
}

Datum ginendscan ( PG_FUNCTION_ARGS   ) 
void ginEntryFillRoot ( GinBtree  btree,
Buffer  root,
Buffer  lbuf,
Buffer  rbuf 
)

Definition at line 682 of file ginentrypage.c.

References BufferGetPage, elog, ERROR, ginPageGetLinkItup(), IndexTupleSize, InvalidOffsetNumber, PageAddItem(), and pfree().

Referenced by ginRedoSplit().

{
    Page        page;
    IndexTuple  itup;

    page = BufferGetPage(root);

    itup = ginPageGetLinkItup(lbuf);
    if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
        elog(ERROR, "failed to add item to index root page");
    pfree(itup);

    itup = ginPageGetLinkItup(rbuf);
    if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
        elog(ERROR, "failed to add item to index root page");
    pfree(itup);
}

void ginEntryInsert ( GinState ginstate,
OffsetNumber  attnum,
Datum  key,
GinNullCategory  category,
ItemPointerData items,
uint32  nitem,
GinStatsData buildStats 
)

Definition at line 247 of file gininsert.c.

References addItemPointersToLeafTuple(), GinPostingTreeScan::btree, GinBtreeStack::buffer, BufferGetPage, buildFreshLeafTuple(), GinBtreeData::entry, FALSE, GinBtreeData::findItem, freeGinBtreeStack(), GIN_UNLOCK, ginFindLeafPage(), GinGetPostingTree, ginInsertItemPointers(), ginInsertValue(), GinIsPostingTree, ginPrepareEntryScan(), ginPrepareScanPostingTree(), GinState::index, GinBtreeData::isBuild, GinBtreeData::isDelete, LockBuffer(), GinStatsData::nEntries, NULL, GinBtreeStack::off, PageGetItem, PageGetItemId, and pfree().

Referenced by ginbuild(), ginBuildCallback(), ginHeapTupleInsert(), and ginInsertCleanup().

{
    GinBtreeData btree;
    GinBtreeStack *stack;
    IndexTuple  itup;
    Page        page;

    /* During index build, count the to-be-inserted entry */
    if (buildStats)
        buildStats->nEntries++;

    ginPrepareEntryScan(&btree, attnum, key, category, ginstate);

    stack = ginFindLeafPage(&btree, NULL);
    page = BufferGetPage(stack->buffer);

    if (btree.findItem(&btree, stack))
    {
        /* found pre-existing entry */
        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));

        if (GinIsPostingTree(itup))
        {
            /* add entries to existing posting tree */
            BlockNumber rootPostingTree = GinGetPostingTree(itup);
            GinPostingTreeScan *gdi;

            /* release all stack */
            LockBuffer(stack->buffer, GIN_UNLOCK);
            freeGinBtreeStack(stack);

            /* insert into posting tree */
            gdi = ginPrepareScanPostingTree(ginstate->index, rootPostingTree, FALSE);
            gdi->btree.isBuild = (buildStats != NULL);
            ginInsertItemPointers(gdi, items, nitem, buildStats);
            pfree(gdi);

            return;
        }

        /* modify an existing leaf entry */
        itup = addItemPointersToLeafTuple(ginstate, itup,
                                          items, nitem, buildStats);

        btree.isDelete = TRUE;
    }
    else
    {
        /* no match, so construct a new leaf entry */
        itup = buildFreshLeafTuple(ginstate, attnum, key, category,
                                   items, nitem, buildStats);
    }

    /* Insert the new or modified leaf tuple */
    btree.entry = itup;
    ginInsertValue(&btree, stack, buildStats);
    pfree(itup);
}

Datum* ginExtractEntries ( GinState ginstate,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
int32 nentries,
GinNullCategory **  categories 
)

Definition at line 375 of file ginutil.c.

References arg, cmpEntriesArg::cmpDatumFunc, cmpEntries(), cmpEntriesArg::collation, GinState::compareFn, keyEntryData::datum, DatumGetPointer, GinState::extractValueFn, FunctionCall3Coll(), cmpEntriesArg::haveDups, i, keyEntryData::isnull, NULL, palloc(), palloc0(), pfree(), PointerGetDatum, qsort_arg(), and GinState::supportCollation.

Referenced by ginHeapTupleBulkInsert(), ginHeapTupleFastCollect(), and ginHeapTupleInsert().

{
    Datum      *entries;
    bool       *nullFlags;
    int32       i;

    /*
     * We don't call the extractValueFn on a null item.  Instead generate a
     * placeholder.
     */
    if (isNull)
    {
        *nentries = 1;
        entries = (Datum *) palloc(sizeof(Datum));
        entries[0] = (Datum) 0;
        *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
        (*categories)[0] = GIN_CAT_NULL_ITEM;
        return entries;
    }

    /* OK, call the opclass's extractValueFn */
    nullFlags = NULL;           /* in case extractValue doesn't set it */
    entries = (Datum *)
        DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
                                      ginstate->supportCollation[attnum - 1],
                                          value,
                                          PointerGetDatum(nentries),
                                          PointerGetDatum(&nullFlags)));

    /*
     * Generate a placeholder if the item contained no keys.
     */
    if (entries == NULL || *nentries <= 0)
    {
        *nentries = 1;
        entries = (Datum *) palloc(sizeof(Datum));
        entries[0] = (Datum) 0;
        *categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
        (*categories)[0] = GIN_CAT_EMPTY_ITEM;
        return entries;
    }

    /*
     * If the extractValueFn didn't create a nullFlags array, create one,
     * assuming that everything's non-null.  Otherwise, run through the array
     * and make sure each value is exactly 0 or 1; this ensures binary
     * compatibility with the GinNullCategory representation.
     */
    if (nullFlags == NULL)
        nullFlags = (bool *) palloc0(*nentries * sizeof(bool));
    else
    {
        for (i = 0; i < *nentries; i++)
            nullFlags[i] = (nullFlags[i] ? true : false);
    }
    /* now we can use the nullFlags as category codes */
    *categories = (GinNullCategory *) nullFlags;

    /*
     * If there's more than one key, sort and unique-ify.
     *
     * XXX Using qsort here is notationally painful, and the overhead is
     * pretty bad too.  For small numbers of keys it'd likely be better to use
     * a simple insertion sort.
     */
    if (*nentries > 1)
    {
        keyEntryData *keydata;
        cmpEntriesArg arg;

        keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData));
        for (i = 0; i < *nentries; i++)
        {
            keydata[i].datum = entries[i];
            keydata[i].isnull = nullFlags[i];
        }

        arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
        arg.collation = ginstate->supportCollation[attnum - 1];
        arg.haveDups = false;
        qsort_arg(keydata, *nentries, sizeof(keyEntryData),
                  cmpEntries, (void *) &arg);

        if (arg.haveDups)
        {
            /* there are duplicates, must get rid of 'em */
            int32       j;

            entries[0] = keydata[0].datum;
            nullFlags[0] = keydata[0].isnull;
            j = 1;
            for (i = 1; i < *nentries; i++)
            {
                if (cmpEntries(&keydata[i - 1], &keydata[i], &arg) != 0)
                {
                    entries[j] = keydata[i].datum;
                    nullFlags[j] = keydata[i].isnull;
                    j++;
                }
            }
            *nentries = j;
        }
        else
        {
            /* easy, no duplicates */
            for (i = 0; i < *nentries; i++)
            {
                entries[i] = keydata[i].datum;
                nullFlags[i] = keydata[i].isnull;
            }
        }

        pfree(keydata);
    }

    return entries;
}

GinBtreeStack* ginFindLeafPage ( GinBtree  btree,
GinBtreeStack stack 
)

Definition at line 74 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetPage, FALSE, GinBtreeData::findChildPage, GinBtreeData::fullScan, GIN_ROOT_BLKNO, GIN_UNLOCK, GinPageGetOpaque, GinPageIsLeaf, ginPrepareFindLeafPage(), ginTraverseLock(), GinBtreeData::index, InvalidBlockNumber, GinBtreeData::isMoveRight, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, GinBtreeStack::predictNumber, ReadBuffer(), ReleaseAndReadBuffer(), and GinBtreeData::searchMode.

Referenced by ginEntryInsert(), ginInsertItemPointers(), ginScanBeginPostingTree(), and startScanEntry().

{
    bool        isfirst = TRUE;
    BlockNumber rootBlkno;

    if (!stack)
        stack = ginPrepareFindLeafPage(btree, GIN_ROOT_BLKNO);
    rootBlkno = stack->blkno;

    for (;;)
    {
        Page        page;
        BlockNumber child;
        int         access = GIN_SHARE;

        stack->off = InvalidOffsetNumber;

        page = BufferGetPage(stack->buffer);

        if (isfirst)
        {
            if (GinPageIsLeaf(page) && !btree->searchMode)
                access = GIN_EXCLUSIVE;
            isfirst = FALSE;
        }
        else
            access = ginTraverseLock(stack->buffer, btree->searchMode);

        /*
         * ok, page is correctly locked, we should check to move right ..,
         * root never has a right link, so small optimization
         */
        while (btree->fullScan == FALSE && stack->blkno != rootBlkno &&
               btree->isMoveRight(btree, page))
        {
            BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;

            if (rightlink == InvalidBlockNumber)
                /* rightmost page */
                break;

            stack->blkno = rightlink;
            LockBuffer(stack->buffer, GIN_UNLOCK);
            stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
            LockBuffer(stack->buffer, access);
            page = BufferGetPage(stack->buffer);
        }

        if (GinPageIsLeaf(page))    /* we found, return locked page */
            return stack;

        /* now we have correct buffer, try to find child */
        child = btree->findChildPage(btree, stack);

        LockBuffer(stack->buffer, GIN_UNLOCK);
        Assert(child != InvalidBlockNumber);
        Assert(stack->blkno != child);

        if (btree->searchMode)
        {
            /* in search mode we may forget path to leaf */
            stack->blkno = child;
            stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
        }
        else
        {
            GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));

            ptr->parent = stack;
            stack = ptr;
            stack->blkno = child;
            stack->buffer = ReadBuffer(btree->index, stack->blkno);
            stack->predictNumber = 1;
        }
    }
}

void ginFindParents ( GinBtree  btree,
GinBtreeStack stack,
BlockNumber  rootBlkno 
)

Definition at line 173 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage, elog, ERROR, GinBtreeData::findChildPtr, GinBtreeData::getLeftMostPage, GIN_EXCLUSIVE, GIN_UNLOCK, GinPageGetOpaque, GinPageIsLeaf, GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, ReadBuffer(), and ReleaseBuffer().

Referenced by ginContinueSplit(), and ginInsertValue().

{

    Page        page;
    Buffer      buffer;
    BlockNumber blkno,
                leftmostBlkno;
    OffsetNumber offset;
    GinBtreeStack *root = stack->parent;
    GinBtreeStack *ptr;

    if (!root)
    {
        /* XLog mode... */
        root = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
        root->blkno = rootBlkno;
        root->buffer = ReadBuffer(btree->index, rootBlkno);
        LockBuffer(root->buffer, GIN_EXCLUSIVE);
        root->parent = NULL;
    }
    else
    {
        /*
         * find root, we should not release root page until update is
         * finished!!
         */
        while (root->parent)
        {
            ReleaseBuffer(root->buffer);
            root = root->parent;
        }

        Assert(root->blkno == rootBlkno);
        Assert(BufferGetBlockNumber(root->buffer) == rootBlkno);
        LockBuffer(root->buffer, GIN_EXCLUSIVE);
    }
    root->off = InvalidOffsetNumber;

    page = BufferGetPage(root->buffer);
    Assert(!GinPageIsLeaf(page));

    /* check trivial case */
    if ((root->off = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) != InvalidOffsetNumber)
    {
        stack->parent = root;
        return;
    }

    leftmostBlkno = blkno = btree->getLeftMostPage(btree, page);
    LockBuffer(root->buffer, GIN_UNLOCK);
    Assert(blkno != InvalidBlockNumber);

    for (;;)
    {
        buffer = ReadBuffer(btree->index, blkno);
        LockBuffer(buffer, GIN_EXCLUSIVE);
        page = BufferGetPage(buffer);
        if (GinPageIsLeaf(page))
            elog(ERROR, "Lost path");

        leftmostBlkno = btree->getLeftMostPage(btree, page);

        while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber)
        {
            blkno = GinPageGetOpaque(page)->rightlink;
            LockBuffer(buffer, GIN_UNLOCK);
            ReleaseBuffer(buffer);
            if (blkno == InvalidBlockNumber)
                break;
            buffer = ReadBuffer(btree->index, blkno);
            LockBuffer(buffer, GIN_EXCLUSIVE);
            page = BufferGetPage(buffer);
        }

        if (blkno != InvalidBlockNumber)
        {
            ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
            ptr->blkno = blkno;
            ptr->buffer = buffer;
            ptr->parent = root; /* it's may be wrong, but in next call we will
                                 * correct */
            ptr->off = offset;
            stack->parent = ptr;
            return;
        }

        blkno = leftmostBlkno;
    }
}

IndexTuple GinFormTuple ( GinState ginstate,
OffsetNumber  attnum,
Datum  key,
GinNullCategory  category,
ItemPointerData ipd,
uint32  nipd,
bool  errorTooBig 
)

Definition at line 36 of file ginentrypage.c.

References Assert, ereport, errcode(), errmsg(), ERROR, GIN_CAT_NORM_KEY, GinCategoryOffset, GinGetPosting, GinMaxItemSize, GinSetNPosting, GinSetNullCategory, GinSetPostingOffset, GinState::index, index_form_tuple(), INDEX_SIZE_MASK, IndexTupleHasNulls, IndexTupleSize, Max, MAXALIGN, Min, GinState::oneCol, pfree(), RelationGetRelationName, repalloc(), SHORTALIGN, IndexTupleData::t_info, GinState::tupdesc, and UInt16GetDatum.

Referenced by addItemPointersToLeafTuple(), buildFreshLeafTuple(), ginHeapTupleFastCollect(), and ginVacuumEntryPage().

{
    Datum       datums[2];
    bool        isnull[2];
    IndexTuple  itup;
    uint32      newsize;

    /* Build the basic tuple: optional column number, plus key datum */
    if (ginstate->oneCol)
    {
        datums[0] = key;
        isnull[0] = (category != GIN_CAT_NORM_KEY);
    }
    else
    {
        datums[0] = UInt16GetDatum(attnum);
        isnull[0] = false;
        datums[1] = key;
        isnull[1] = (category != GIN_CAT_NORM_KEY);
    }

    itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);

    /*
     * Determine and store offset to the posting list, making sure there is
     * room for the category byte if needed.
     *
     * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
     * be some wasted pad space.  Is it worth recomputing the data length to
     * prevent that?  That would also allow us to Assert that the real data
     * doesn't overlap the GinNullCategory byte, which this code currently
     * takes on faith.
     */
    newsize = IndexTupleSize(itup);

    if (IndexTupleHasNulls(itup))
    {
        uint32      minsize;

        Assert(category != GIN_CAT_NORM_KEY);
        minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
        newsize = Max(newsize, minsize);
    }

    newsize = SHORTALIGN(newsize);

    GinSetPostingOffset(itup, newsize);

    GinSetNPosting(itup, nipd);

    /*
     * Add space needed for posting list, if any.  Then check that the tuple
     * won't be too big to store.
     */
    newsize += sizeof(ItemPointerData) * nipd;
    newsize = MAXALIGN(newsize);
    if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
    {
        if (errorTooBig)
            ereport(ERROR,
                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
            errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
                   (unsigned long) newsize,
                   (unsigned long) Min(INDEX_SIZE_MASK,
                                       GinMaxItemSize),
                   RelationGetRelationName(ginstate->index))));
        pfree(itup);
        return NULL;
    }

    /*
     * Resize tuple if needed
     */
    if (newsize != IndexTupleSize(itup))
    {
        itup = repalloc(itup, newsize);

        /* set new size in tuple header */
        itup->t_info &= ~INDEX_SIZE_MASK;
        itup->t_info |= newsize;
    }

    /*
     * Insert category byte, if needed
     */
    if (category != GIN_CAT_NORM_KEY)
    {
        Assert(IndexTupleHasNulls(itup));
        GinSetNullCategory(itup, ginstate, category);
    }

    /*
     * Copy in the posting list, if provided
     */
    if (ipd)
        memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);

    return itup;
}

ItemPointerData* ginGetBAEntry ( BuildAccumulator accum,
OffsetNumber attnum,
Datum key,
GinNullCategory category,
uint32 n 
)

Definition at line 259 of file ginbulk.c.

References Assert, GinEntryAccumulator::attnum, GinEntryAccumulator::category, GinEntryAccumulator::count, GinEntryAccumulator::key, GinEntryAccumulator::list, sort-test::list, NULL, qsort, qsortCompareItemPointers(), rb_iterate(), GinEntryAccumulator::shouldSort, and BuildAccumulator::tree.

Referenced by ginbuild(), ginBuildCallback(), and ginInsertCleanup().

{
    GinEntryAccumulator *entry;
    ItemPointerData *list;

    entry = (GinEntryAccumulator *) rb_iterate(accum->tree);

    if (entry == NULL)
        return NULL;            /* no more entries */

    *attnum = entry->attnum;
    *key = entry->key;
    *category = entry->category;
    list = entry->list;
    *n = entry->count;

    Assert(list != NULL && entry->count > 0);

    if (entry->shouldSort && entry->count > 1)
        qsort(list, entry->count, sizeof(ItemPointerData),
              qsortCompareItemPointers);

    return list;
}

Datum gingetbitmap ( PG_FUNCTION_ARGS   ) 

Definition at line 1505 of file ginget.c.

References CHECK_FOR_INTERRUPTS, GinIsNewKey, GinIsVoidRes, ginNewScanKey(), ItemPointerGetBlockNumber, ItemPointerIsLossyPage, ItemPointerSetMin, PG_GETARG_POINTER, PG_RETURN_INT64, scanGetItem(), scanPendingInsert(), startScan(), tbm_add_page(), and tbm_add_tuples().

{
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
    int64       ntids;
    ItemPointerData iptr;
    bool        recheck;

    /*
     * Set up the scan keys, and check for unsatisfiable query.
     */
    if (GinIsNewKey(scan))
        ginNewScanKey(scan);

    if (GinIsVoidRes(scan))
        PG_RETURN_INT64(0);

    ntids = 0;

    /*
     * First, scan the pending list and collect any matching entries into the
     * bitmap.  After we scan a pending item, some other backend could post it
     * into the main index, and so we might visit it a second time during the
     * main scan.  This is okay because we'll just re-set the same bit in the
     * bitmap.  (The possibility of duplicate visits is a major reason why GIN
     * can't support the amgettuple API, however.) Note that it would not do
     * to scan the main index before the pending list, since concurrent
     * cleanup could then make us miss entries entirely.
     */
    scanPendingInsert(scan, tbm, &ntids);

    /*
     * Now scan the main index.
     */
    startScan(scan);

    ItemPointerSetMin(&iptr);

    for (;;)
    {
        CHECK_FOR_INTERRUPTS();

        if (!scanGetItem(scan, &iptr, &iptr, &recheck))
            break;

        if (ItemPointerIsLossyPage(&iptr))
            tbm_add_page(tbm, ItemPointerGetBlockNumber(&iptr));
        else
            tbm_add_tuples(tbm, &iptr, 1, recheck);
        ntids++;
    }

    PG_RETURN_INT64(ntids);
}

void ginHeapTupleFastCollect ( GinState ginstate,
GinTupleCollector collector,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
ItemPointer  ht_ctid 
)

Definition at line 449 of file ginfast.c.

References ginExtractEntries(), GinFormTuple(), i, IndexTupleSize, GinTupleCollector::lentuples, tupleDesc::natts, GinTupleCollector::ntuples, NULL, GinState::origTupdesc, palloc(), repalloc(), GinTupleCollector::sumsize, IndexTupleData::t_tid, and GinTupleCollector::tuples.

Referenced by gininsert().

{
    Datum      *entries;
    GinNullCategory *categories;
    int32       i,
                nentries;

    /*
     * Extract the key values that need to be inserted in the index
     */
    entries = ginExtractEntries(ginstate, attnum, value, isNull,
                                &nentries, &categories);

    /*
     * Allocate/reallocate memory for storing collected tuples
     */
    if (collector->tuples == NULL)
    {
        collector->lentuples = nentries * ginstate->origTupdesc->natts;
        collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
    }

    while (collector->ntuples + nentries > collector->lentuples)
    {
        collector->lentuples *= 2;
        collector->tuples = (IndexTuple *) repalloc(collector->tuples,
                                  sizeof(IndexTuple) * collector->lentuples);
    }

    /*
     * Build an index tuple for each key value, and add to array.  In pending
     * tuples we just stick the heap TID into t_tid.
     */
    for (i = 0; i < nentries; i++)
    {
        IndexTuple  itup;

        itup = GinFormTuple(ginstate, attnum, entries[i], categories[i],
                            NULL, 0, true);
        itup->t_tid = *ht_ctid;
        collector->tuples[collector->ntuples++] = itup;
        collector->sumsize += IndexTupleSize(itup);
    }
}

void ginHeapTupleFastInsert ( GinState ginstate,
GinTupleCollector collector 
)

Definition at line 219 of file ginfast.c.

References Assert, XLogRecData::buffer, XLogRecData::buffer_std, BufferGetPage, XLogRecData::data, elog, END_CRIT_SECTION, ERROR, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GIN_PAGE_FREESIZE, GIN_UNLOCK, ginInsertCleanup(), GinListPageSize, GinPageGetMeta, GinPageGetOpaque, GinMetaPageData::head, i, GinState::index, IndexTupleSize, InvalidBlockNumber, InvalidBuffer, InvalidOffsetNumber, XLogRecData::len, LockBuffer(), makeSublist(), MarkBufferDirty(), ginxlogUpdateMeta::metadata, ginxlogUpdateMeta::newRightlink, XLogRecData::next, ginxlogUpdateMeta::node, GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, ginxlogUpdateMeta::ntuples, GinTupleCollector::ntuples, NULL, OffsetNumberNext, PageAddItem(), PageGetExactFreeSpace(), PageGetMaxOffsetNumber, PageIsEmpty, PageSetLSN, palloc(), ginxlogUpdateMeta::prevTail, RelationData::rd_node, ReadBuffer(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, GinTupleCollector::sumsize, GinMetaPageData::tail, GinMetaPageData::tailFreeSize, GinTupleCollector::tuples, UnlockReleaseBuffer(), work_mem, XLOG_GIN_UPDATE_META_PAGE, and XLogInsert().

Referenced by gininsert().

{
    Relation    index = ginstate->index;
    Buffer      metabuffer;
    Page        metapage;
    GinMetaPageData *metadata = NULL;
    XLogRecData rdata[2];
    Buffer      buffer = InvalidBuffer;
    Page        page = NULL;
    ginxlogUpdateMeta data;
    bool        separateList = false;
    bool        needCleanup = false;

    if (collector->ntuples == 0)
        return;

    data.node = index->rd_node;
    data.ntuples = 0;
    data.newRightlink = data.prevTail = InvalidBlockNumber;

    rdata[0].buffer = InvalidBuffer;
    rdata[0].data = (char *) &data;
    rdata[0].len = sizeof(ginxlogUpdateMeta);
    rdata[0].next = NULL;

    metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
    metapage = BufferGetPage(metabuffer);

    if (collector->sumsize + collector->ntuples * sizeof(ItemIdData) > GinListPageSize)
    {
        /*
         * Total size is greater than one page => make sublist
         */
        separateList = true;
    }
    else
    {
        LockBuffer(metabuffer, GIN_EXCLUSIVE);
        metadata = GinPageGetMeta(metapage);

        if (metadata->head == InvalidBlockNumber ||
            collector->sumsize + collector->ntuples * sizeof(ItemIdData) > metadata->tailFreeSize)
        {
            /*
             * Pending list is empty or total size is greater than freespace
             * on tail page => make sublist
             *
             * We unlock metabuffer to keep high concurrency
             */
            separateList = true;
            LockBuffer(metabuffer, GIN_UNLOCK);
        }
    }

    if (separateList)
    {
        /*
         * We should make sublist separately and append it to the tail
         */
        GinMetaPageData sublist;

        memset(&sublist, 0, sizeof(GinMetaPageData));
        makeSublist(index, collector->tuples, collector->ntuples, &sublist);

        /*
         * metapage was unlocked, see above
         */
        LockBuffer(metabuffer, GIN_EXCLUSIVE);
        metadata = GinPageGetMeta(metapage);

        if (metadata->head == InvalidBlockNumber)
        {
            /*
             * Main list is empty, so just insert sublist as main list
             */
            START_CRIT_SECTION();

            metadata->head = sublist.head;
            metadata->tail = sublist.tail;
            metadata->tailFreeSize = sublist.tailFreeSize;

            metadata->nPendingPages = sublist.nPendingPages;
            metadata->nPendingHeapTuples = sublist.nPendingHeapTuples;
        }
        else
        {
            /*
             * Merge lists
             */
            data.prevTail = metadata->tail;
            data.newRightlink = sublist.head;

            buffer = ReadBuffer(index, metadata->tail);
            LockBuffer(buffer, GIN_EXCLUSIVE);
            page = BufferGetPage(buffer);

            rdata[0].next = rdata + 1;

            rdata[1].buffer = buffer;
            rdata[1].buffer_std = true;
            rdata[1].data = NULL;
            rdata[1].len = 0;
            rdata[1].next = NULL;

            Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber);

            START_CRIT_SECTION();

            GinPageGetOpaque(page)->rightlink = sublist.head;

            MarkBufferDirty(buffer);

            metadata->tail = sublist.tail;
            metadata->tailFreeSize = sublist.tailFreeSize;

            metadata->nPendingPages += sublist.nPendingPages;
            metadata->nPendingHeapTuples += sublist.nPendingHeapTuples;
        }
    }
    else
    {
        /*
         * Insert into tail page.  Metapage is already locked
         */
        OffsetNumber l,
                    off;
        int         i,
                    tupsize;
        char       *ptr;

        buffer = ReadBuffer(index, metadata->tail);
        LockBuffer(buffer, GIN_EXCLUSIVE);
        page = BufferGetPage(buffer);

        off = (PageIsEmpty(page)) ? FirstOffsetNumber :
            OffsetNumberNext(PageGetMaxOffsetNumber(page));

        rdata[0].next = rdata + 1;

        rdata[1].buffer = buffer;
        rdata[1].buffer_std = true;
        ptr = rdata[1].data = (char *) palloc(collector->sumsize);
        rdata[1].len = collector->sumsize;
        rdata[1].next = NULL;

        data.ntuples = collector->ntuples;

        START_CRIT_SECTION();

        /*
         * Increase counter of heap tuples
         */
        Assert(GinPageGetOpaque(page)->maxoff <= metadata->nPendingHeapTuples);
        GinPageGetOpaque(page)->maxoff++;
        metadata->nPendingHeapTuples++;

        for (i = 0; i < collector->ntuples; i++)
        {
            tupsize = IndexTupleSize(collector->tuples[i]);
            l = PageAddItem(page, (Item) collector->tuples[i], tupsize, off, false, false);

            if (l == InvalidOffsetNumber)
                elog(ERROR, "failed to add item to index page in \"%s\"",
                     RelationGetRelationName(index));

            memcpy(ptr, collector->tuples[i], tupsize);
            ptr += tupsize;

            off++;
        }

        Assert((ptr - rdata[1].data) <= collector->sumsize);

        metadata->tailFreeSize = PageGetExactFreeSpace(page);

        MarkBufferDirty(buffer);
    }

    /*
     * Write metabuffer, make xlog entry
     */
    MarkBufferDirty(metabuffer);

    if (RelationNeedsWAL(index))
    {
        XLogRecPtr  recptr;

        memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));

        recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE, rdata);
        PageSetLSN(metapage, recptr);

        if (buffer != InvalidBuffer)
        {
            PageSetLSN(page, recptr);
        }
    }

    if (buffer != InvalidBuffer)
        UnlockReleaseBuffer(buffer);

    /*
     * Force pending list cleanup when it becomes too long. And,
     * ginInsertCleanup could take significant amount of time, so we prefer to
     * call it when it can do all the work in a single collection cycle. In
     * non-vacuum mode, it shouldn't require maintenance_work_mem, so fire it
     * while pending list is still small enough to fit into work_mem.
     *
     * ginInsertCleanup() should not be called inside our CRIT_SECTION.
     */
    if (metadata->nPendingPages * GIN_PAGE_FREESIZE > work_mem * 1024L)
        needCleanup = true;

    UnlockReleaseBuffer(metabuffer);

    END_CRIT_SECTION();

    if (needCleanup)
        ginInsertCleanup(ginstate, false, NULL);
}

void ginInitBA ( BuildAccumulator accum  ) 

Definition at line 101 of file ginbulk.c.

References BuildAccumulator::allocatedMemory, cmpEntryAccumulator(), BuildAccumulator::eas_used, BuildAccumulator::entryallocator, ginAllocEntryAccumulator(), ginCombineData(), NULL, rb_create(), and BuildAccumulator::tree.

Referenced by ginbuild(), ginBuildCallback(), and ginInsertCleanup().

{
    /* accum->ginstate is intentionally not set here */
    accum->allocatedMemory = 0;
    accum->entryallocator = NULL;
    accum->eas_used = 0;
    accum->tree = rb_create(sizeof(GinEntryAccumulator),
                            cmpEntryAccumulator,
                            ginCombineData,
                            ginAllocEntryAccumulator,
                            NULL,       /* no freefunc needed */
                            (void *) accum);
}

void GinInitBuffer ( Buffer  b,
uint32  f 
)
void GinInitMetabuffer ( Buffer  b  ) 
void GinInitPage ( Page  page,
uint32  f,
Size  pageSize 
)

Definition at line 237 of file ginutil.c.

References GinPageOpaqueData::flags, GinPageGetOpaque, PageInit(), and GinPageOpaqueData::rightlink.

Referenced by dataSplitPage(), entrySplitPage(), GinInitBuffer(), and GinInitMetabuffer().

{
    GinPageOpaque opaque;

    PageInit(page, pageSize, sizeof(GinPageOpaqueData));

    opaque = GinPageGetOpaque(page);
    memset(opaque, 0, sizeof(GinPageOpaqueData));
    opaque->flags = f;
    opaque->rightlink = InvalidBlockNumber;
}

Datum gininsert ( PG_FUNCTION_ARGS   ) 

Definition at line 561 of file gininsert.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), CurrentMemoryContext, GinGetUseFastUpdate, ginHeapTupleFastCollect(), ginHeapTupleFastInsert(), ginHeapTupleInsert(), i, initGinState(), MemoryContextDelete(), MemoryContextSwitchTo(), tupleDesc::natts, GinState::origTupdesc, PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_BOOL, and values.

{
    Relation    index = (Relation) PG_GETARG_POINTER(0);
    Datum      *values = (Datum *) PG_GETARG_POINTER(1);
    bool       *isnull = (bool *) PG_GETARG_POINTER(2);
    ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);

#ifdef NOT_USED
    Relation    heapRel = (Relation) PG_GETARG_POINTER(4);
    IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
#endif
    GinState    ginstate;
    MemoryContext oldCtx;
    MemoryContext insertCtx;
    int         i;

    insertCtx = AllocSetContextCreate(CurrentMemoryContext,
                                      "Gin insert temporary context",
                                      ALLOCSET_DEFAULT_MINSIZE,
                                      ALLOCSET_DEFAULT_INITSIZE,
                                      ALLOCSET_DEFAULT_MAXSIZE);

    oldCtx = MemoryContextSwitchTo(insertCtx);

    initGinState(&ginstate, index);

    if (GinGetUseFastUpdate(index))
    {
        GinTupleCollector collector;

        memset(&collector, 0, sizeof(GinTupleCollector));

        for (i = 0; i < ginstate.origTupdesc->natts; i++)
            ginHeapTupleFastCollect(&ginstate, &collector,
                                    (OffsetNumber) (i + 1),
                                    values[i], isnull[i],
                                    ht_ctid);

        ginHeapTupleFastInsert(&ginstate, &collector);
    }
    else
    {
        for (i = 0; i < ginstate.origTupdesc->natts; i++)
            ginHeapTupleInsert(&ginstate, (OffsetNumber) (i + 1),
                               values[i], isnull[i],
                               ht_ctid);
    }

    MemoryContextSwitchTo(oldCtx);
    MemoryContextDelete(insertCtx);

    PG_RETURN_BOOL(false);
}

void ginInsertBAEntries ( BuildAccumulator accum,
ItemPointer  heapptr,
OffsetNumber  attnum,
Datum entries,
GinNullCategory categories,
int32  nentries 
)

Definition at line 201 of file ginbulk.c.

References Assert, FirstOffsetNumber, ginInsertBAEntry(), i, and ItemPointerIsValid.

Referenced by ginHeapTupleBulkInsert(), and processPendingPage().

{
    uint32      step = nentries;

    if (nentries <= 0)
        return;

    Assert(ItemPointerIsValid(heapptr) && attnum >= FirstOffsetNumber);

    /*
     * step will contain largest power of 2 and <= nentries
     */
    step |= (step >> 1);
    step |= (step >> 2);
    step |= (step >> 4);
    step |= (step >> 8);
    step |= (step >> 16);
    step >>= 1;
    step++;

    while (step > 0)
    {
        int         i;

        for (i = step - 1; i < nentries && i >= 0; i += step << 1 /* *2 */ )
            ginInsertBAEntry(accum, heapptr, attnum,
                             entries[i], categories[i]);

        step >>= 1;             /* /2 */
    }
}

void ginInsertCleanup ( GinState ginstate,
bool  vac_delay,
IndexBulkDeleteResult stats 
)

Definition at line 727 of file ginfast.c.

References BuildAccumulator::allocatedMemory, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, BufferGetPage, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GIN_SHARE, GIN_UNLOCK, ginBeginBAScan(), ginEntryInsert(), ginGetBAEntry(), ginInitBA(), GinPageGetMeta, GinPageGetOpaque, GinPageHasFullRow, GinPageIsDeleted, BuildAccumulator::ginstate, GinMetaPageData::head, GinState::index, initKeyArray(), InvalidBlockNumber, sort-test::list, LockBuffer(), maintenance_work_mem, KeyArray::maxvalues, MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NULL, opCtx, PageGetMaxOffsetNumber, processPendingPage(), ReadBuffer(), ReleaseBuffer(), shiftList(), UnlockReleaseBuffer(), and vacuum_delay_point().

Referenced by ginbulkdelete(), ginHeapTupleFastInsert(), and ginvacuumcleanup().

{
    Relation    index = ginstate->index;
    Buffer      metabuffer,
                buffer;
    Page        metapage,
                page;
    GinMetaPageData *metadata;
    MemoryContext opCtx,
                oldCtx;
    BuildAccumulator accum;
    KeyArray    datums;
    BlockNumber blkno;

    metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
    LockBuffer(metabuffer, GIN_SHARE);
    metapage = BufferGetPage(metabuffer);
    metadata = GinPageGetMeta(metapage);

    if (metadata->head == InvalidBlockNumber)
    {
        /* Nothing to do */
        UnlockReleaseBuffer(metabuffer);
        return;
    }

    /*
     * Read and lock head of pending list
     */
    blkno = metadata->head;
    buffer = ReadBuffer(index, blkno);
    LockBuffer(buffer, GIN_SHARE);
    page = BufferGetPage(buffer);

    LockBuffer(metabuffer, GIN_UNLOCK);

    /*
     * Initialize.  All temporary space will be in opCtx
     */
    opCtx = AllocSetContextCreate(CurrentMemoryContext,
                                  "GIN insert cleanup temporary context",
                                  ALLOCSET_DEFAULT_MINSIZE,
                                  ALLOCSET_DEFAULT_INITSIZE,
                                  ALLOCSET_DEFAULT_MAXSIZE);

    oldCtx = MemoryContextSwitchTo(opCtx);

    initKeyArray(&datums, 128);
    ginInitBA(&accum);
    accum.ginstate = ginstate;

    /*
     * At the top of this loop, we have pin and lock on the current page of
     * the pending list.  However, we'll release that before exiting the loop.
     * Note we also have pin but not lock on the metapage.
     */
    for (;;)
    {
        if (GinPageIsDeleted(page))
        {
            /* another cleanup process is running concurrently */
            UnlockReleaseBuffer(buffer);
            break;
        }

        /*
         * read page's datums into accum
         */
        processPendingPage(&accum, &datums, page, FirstOffsetNumber);

        if (vac_delay)
            vacuum_delay_point();

        /*
         * Is it time to flush memory to disk?  Flush if we are at the end of
         * the pending list, or if we have a full row and memory is getting
         * full.
         *
         * XXX using up maintenance_work_mem here is probably unreasonably
         * much, since vacuum might already be using that much.
         */
        if (GinPageGetOpaque(page)->rightlink == InvalidBlockNumber ||
            (GinPageHasFullRow(page) &&
             (accum.allocatedMemory >= maintenance_work_mem * 1024L)))
        {
            ItemPointerData *list;
            uint32      nlist;
            Datum       key;
            GinNullCategory category;
            OffsetNumber maxoff,
                        attnum;

            /*
             * Unlock current page to increase performance. Changes of page
             * will be checked later by comparing maxoff after completion of
             * memory flush.
             */
            maxoff = PageGetMaxOffsetNumber(page);
            LockBuffer(buffer, GIN_UNLOCK);

            /*
             * Moving collected data into regular structure can take
             * significant amount of time - so, run it without locking pending
             * list.
             */
            ginBeginBAScan(&accum);
            while ((list = ginGetBAEntry(&accum,
                                  &attnum, &key, &category, &nlist)) != NULL)
            {
                ginEntryInsert(ginstate, attnum, key, category,
                               list, nlist, NULL);
                if (vac_delay)
                    vacuum_delay_point();
            }

            /*
             * Lock the whole list to remove pages
             */
            LockBuffer(metabuffer, GIN_EXCLUSIVE);
            LockBuffer(buffer, GIN_SHARE);

            if (GinPageIsDeleted(page))
            {
                /* another cleanup process is running concurrently */
                UnlockReleaseBuffer(buffer);
                LockBuffer(metabuffer, GIN_UNLOCK);
                break;
            }

            /*
             * While we left the page unlocked, more stuff might have gotten
             * added to it.  If so, process those entries immediately.  There
             * shouldn't be very many, so we don't worry about the fact that
             * we're doing this with exclusive lock. Insertion algorithm
             * guarantees that inserted row(s) will not continue on next page.
             * NOTE: intentionally no vacuum_delay_point in this loop.
             */
            if (PageGetMaxOffsetNumber(page) != maxoff)
            {
                ginInitBA(&accum);
                processPendingPage(&accum, &datums, page, maxoff + 1);

                ginBeginBAScan(&accum);
                while ((list = ginGetBAEntry(&accum,
                                  &attnum, &key, &category, &nlist)) != NULL)
                    ginEntryInsert(ginstate, attnum, key, category,
                                   list, nlist, NULL);
            }

            /*
             * Remember next page - it will become the new list head
             */
            blkno = GinPageGetOpaque(page)->rightlink;
            UnlockReleaseBuffer(buffer);        /* shiftList will do exclusive
                                                 * locking */

            /*
             * remove readed pages from pending list, at this point all
             * content of readed pages is in regular structure
             */
            if (shiftList(index, metabuffer, blkno, stats))
            {
                /* another cleanup process is running concurrently */
                LockBuffer(metabuffer, GIN_UNLOCK);
                break;
            }

            Assert(blkno == metadata->head);
            LockBuffer(metabuffer, GIN_UNLOCK);

            /*
             * if we removed the whole pending list just exit
             */
            if (blkno == InvalidBlockNumber)
                break;

            /*
             * release memory used so far and reinit state
             */
            MemoryContextReset(opCtx);
            initKeyArray(&datums, datums.maxvalues);
            ginInitBA(&accum);
        }
        else
        {
            blkno = GinPageGetOpaque(page)->rightlink;
            UnlockReleaseBuffer(buffer);
        }

        /*
         * Read next page in pending list
         */
        CHECK_FOR_INTERRUPTS();
        buffer = ReadBuffer(index, blkno);
        LockBuffer(buffer, GIN_SHARE);
        page = BufferGetPage(buffer);
    }

    ReleaseBuffer(metabuffer);

    /* Clean up temporary space */
    MemoryContextSwitchTo(oldCtx);
    MemoryContextDelete(opCtx);
}

void ginInsertItemPointers ( GinPostingTreeScan gdi,
ItemPointerData items,
uint32  nitem,
GinStatsData buildStats 
)

Definition at line 648 of file gindatapage.c.

References GinBtreeStack::blkno, GinPostingTreeScan::btree, GinBtreeStack::buffer, GinBtreeData::curitem, GinBtreeData::findItem, freeGinBtreeStack(), GIN_UNLOCK, ginFindLeafPage(), ginInsertValue(), ginPrepareFindLeafPage(), GinBtreeData::items, LockBuffer(), GinBtreeData::nitem, and GinPostingTreeScan::stack.

Referenced by addItemPointersToLeafTuple(), buildFreshLeafTuple(), and ginEntryInsert().

{
    BlockNumber rootBlkno = gdi->stack->blkno;

    gdi->btree.items = items;
    gdi->btree.nitem = nitem;
    gdi->btree.curitem = 0;

    while (gdi->btree.curitem < gdi->btree.nitem)
    {
        if (!gdi->stack)
            gdi->stack = ginPrepareFindLeafPage(&gdi->btree, rootBlkno);

        gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);

        if (gdi->btree.findItem(&(gdi->btree), gdi->stack))
        {
            /*
             * gdi->btree.items[gdi->btree.curitem] already exists in index
             */
            gdi->btree.curitem++;
            LockBuffer(gdi->stack->buffer, GIN_UNLOCK);
            freeGinBtreeStack(gdi->stack);
        }
        else
            ginInsertValue(&(gdi->btree), gdi->stack, buildStats);

        gdi->stack = NULL;
    }
}

void ginInsertValue ( GinBtree  btree,
GinBtreeStack stack,
GinStatsData buildStats 
)

Definition at line 273 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, BlockNumberIsValid, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage, XLogRecData::data, END_CRIT_SECTION, FALSE, GinBtreeData::fillRoot, GinBtreeData::findChildPtr, freeGinBtreeStack(), GIN_EXCLUSIVE, GIN_LEAF, GIN_UNLOCK, ginFindParents(), GinInitBuffer(), GinNewBuffer(), GinPageGetOpaque, GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, GinBtreeData::isData, GinBtreeData::isDelete, GinBtreeData::isEnoughSpace, LockBuffer(), MarkBufferDirty(), GinStatsData::nDataPages, GinStatsData::nEntryPages, NULL, GinBtreeStack::off, PageRestoreTempPage(), PageSetLSN, GinBtreeStack::parent, pfree(), GinBtreeData::placeToPage, RelationNeedsWAL, ReleaseAndReadBuffer(), GinBtreeData::splitPage, START_CRIT_SECTION, TRUE, UnlockReleaseBuffer(), XLOG_GIN_INSERT, XLOG_GIN_SPLIT, and XLogInsert().

Referenced by ginContinueSplit(), ginEntryInsert(), and ginInsertItemPointers().

{
    GinBtreeStack *parent;
    BlockNumber rootBlkno;
    Page        page,
                rpage,
                lpage;

    /* extract root BlockNumber from stack */
    Assert(stack != NULL);
    parent = stack;
    while (parent->parent)
        parent = parent->parent;
    rootBlkno = parent->blkno;
    Assert(BlockNumberIsValid(rootBlkno));

    /* this loop crawls up the stack until the insertion is complete */
    for (;;)
    {
        XLogRecData *rdata;
        BlockNumber savedRightLink;

        page = BufferGetPage(stack->buffer);
        savedRightLink = GinPageGetOpaque(page)->rightlink;

        if (btree->isEnoughSpace(btree, stack->buffer, stack->off))
        {
            START_CRIT_SECTION();
            btree->placeToPage(btree, stack->buffer, stack->off, &rdata);

            MarkBufferDirty(stack->buffer);

            if (RelationNeedsWAL(btree->index))
            {
                XLogRecPtr  recptr;

                recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT, rdata);
                PageSetLSN(page, recptr);
            }

            LockBuffer(stack->buffer, GIN_UNLOCK);
            END_CRIT_SECTION();

            freeGinBtreeStack(stack);

            return;
        }
        else
        {
            Buffer      rbuffer = GinNewBuffer(btree->index);
            Page        newlpage;

            /*
             * newlpage is a pointer to memory page, it doesn't associate with
             * buffer, stack->buffer should be untouched
             */
            newlpage = btree->splitPage(btree, stack->buffer, rbuffer, stack->off, &rdata);

            ((ginxlogSplit *) (rdata->data))->rootBlkno = rootBlkno;

            /* During index build, count the newly-split page */
            if (buildStats)
            {
                if (btree->isData)
                    buildStats->nDataPages++;
                else
                    buildStats->nEntryPages++;
            }

            parent = stack->parent;

            if (parent == NULL)
            {
                /*
                 * split root, so we need to allocate new left page and place
                 * pointer on root to left and right page
                 */
                Buffer      lbuffer = GinNewBuffer(btree->index);

                ((ginxlogSplit *) (rdata->data))->isRootSplit = TRUE;
                ((ginxlogSplit *) (rdata->data))->rrlink = InvalidBlockNumber;

                page = BufferGetPage(stack->buffer);
                lpage = BufferGetPage(lbuffer);
                rpage = BufferGetPage(rbuffer);

                GinPageGetOpaque(rpage)->rightlink = InvalidBlockNumber;
                GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
                ((ginxlogSplit *) (rdata->data))->lblkno = BufferGetBlockNumber(lbuffer);

                START_CRIT_SECTION();

                GinInitBuffer(stack->buffer, GinPageGetOpaque(newlpage)->flags & ~GIN_LEAF);
                PageRestoreTempPage(newlpage, lpage);
                btree->fillRoot(btree, stack->buffer, lbuffer, rbuffer);

                MarkBufferDirty(rbuffer);
                MarkBufferDirty(lbuffer);
                MarkBufferDirty(stack->buffer);

                if (RelationNeedsWAL(btree->index))
                {
                    XLogRecPtr  recptr;

                    recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
                    PageSetLSN(page, recptr);
                    PageSetLSN(lpage, recptr);
                    PageSetLSN(rpage, recptr);
                }

                UnlockReleaseBuffer(rbuffer);
                UnlockReleaseBuffer(lbuffer);
                LockBuffer(stack->buffer, GIN_UNLOCK);
                END_CRIT_SECTION();

                freeGinBtreeStack(stack);

                /* During index build, count the newly-added root page */
                if (buildStats)
                {
                    if (btree->isData)
                        buildStats->nDataPages++;
                    else
                        buildStats->nEntryPages++;
                }

                return;
            }
            else
            {
                /* split non-root page */
                ((ginxlogSplit *) (rdata->data))->isRootSplit = FALSE;
                ((ginxlogSplit *) (rdata->data))->rrlink = savedRightLink;

                lpage = BufferGetPage(stack->buffer);
                rpage = BufferGetPage(rbuffer);

                GinPageGetOpaque(rpage)->rightlink = savedRightLink;
                GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);

                START_CRIT_SECTION();
                PageRestoreTempPage(newlpage, lpage);

                MarkBufferDirty(rbuffer);
                MarkBufferDirty(stack->buffer);

                if (RelationNeedsWAL(btree->index))
                {
                    XLogRecPtr  recptr;

                    recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
                    PageSetLSN(lpage, recptr);
                    PageSetLSN(rpage, recptr);
                }
                UnlockReleaseBuffer(rbuffer);
                END_CRIT_SECTION();
            }
        }

        btree->isDelete = FALSE;

        /* search parent to lock */
        LockBuffer(parent->buffer, GIN_EXCLUSIVE);

        /* move right if it's needed */
        page = BufferGetPage(parent->buffer);
        while ((parent->off = btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber)
        {
            BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;

            LockBuffer(parent->buffer, GIN_UNLOCK);

            if (rightlink == InvalidBlockNumber)
            {
                /*
                 * rightmost page, but we don't find parent, we should use
                 * plain search...
                 */
                ginFindParents(btree, stack, rootBlkno);
                parent = stack->parent;
                Assert(parent != NULL);
                break;
            }

            parent->blkno = rightlink;
            parent->buffer = ReleaseAndReadBuffer(parent->buffer, btree->index, parent->blkno);
            LockBuffer(parent->buffer, GIN_EXCLUSIVE);
            page = BufferGetPage(parent->buffer);
        }

        UnlockReleaseBuffer(stack->buffer);
        pfree(stack);
        stack = parent;
    }
}

Datum ginmarkpos ( PG_FUNCTION_ARGS   ) 

Definition at line 445 of file ginscan.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "GIN does not support mark/restore");
    PG_RETURN_VOID();
}

uint32 ginMergeItemPointers ( ItemPointerData dst,
ItemPointerData a,
uint32  na,
ItemPointerData b,
uint32  nb 
)

Definition at line 45 of file gindatapage.c.

References cmp(), and ginCompareItemPointers().

Referenced by addItemPointersToLeafTuple().

{
    ItemPointerData *dptr = dst;
    ItemPointerData *aptr = a,
               *bptr = b;

    while (aptr - a < na && bptr - b < nb)
    {
        int         cmp = ginCompareItemPointers(aptr, bptr);

        if (cmp > 0)
            *dptr++ = *bptr++;
        else if (cmp == 0)
        {
            /* we want only one copy of the identical items */
            *dptr++ = *bptr++;
            aptr++;
        }
        else
            *dptr++ = *aptr++;
    }

    while (aptr - a < na)
        *dptr++ = *aptr++;

    while (bptr - b < nb)
        *dptr++ = *bptr++;

    return dptr - dst;
}

Buffer GinNewBuffer ( Relation  index  ) 

Definition at line 186 of file ginutil.c.

References BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), GIN_EXCLUSIVE, GIN_UNLOCK, GinPageIsDeleted, InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), and UnlockRelationForExtension().

Referenced by createPostingTree(), ginbuild(), ginInsertValue(), and makeSublist().

{
    Buffer      buffer;
    bool        needLock;

    /* First, try to get a page from FSM */
    for (;;)
    {
        BlockNumber blkno = GetFreeIndexPage(index);

        if (blkno == InvalidBlockNumber)
            break;

        buffer = ReadBuffer(index, blkno);

        /*
         * We have to guard against the possibility that someone else already
         * recycled this page; the buffer may be locked if so.
         */
        if (ConditionalLockBuffer(buffer))
        {
            Page        page = BufferGetPage(buffer);

            if (PageIsNew(page))
                return buffer;  /* OK to use, if never initialized */

            if (GinPageIsDeleted(page))
                return buffer;  /* OK to use */

            LockBuffer(buffer, GIN_UNLOCK);
        }

        /* Can't use it, so release buffer and try again */
        ReleaseBuffer(buffer);
    }

    /* Must extend the file */
    needLock = !RELATION_IS_LOCAL(index);
    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);

    buffer = ReadBuffer(index, P_NEW);
    LockBuffer(buffer, GIN_EXCLUSIVE);

    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    return buffer;
}

void ginNewScanKey ( IndexScanDesc  scan  ) 

Definition at line 265 of file ginscan.c.

References GinScanOpaqueData::allocentries, DatumGetPointer, GinScanOpaqueData::entries, ereport, errcode(), errhint(), errmsg(), ERROR, GinState::extractQueryFn, FirstOffsetNumber, FunctionCall7Coll(), GIN_SEARCH_MODE_ALL, GIN_SEARCH_MODE_DEFAULT, GIN_SEARCH_MODE_EVERYTHING, ginFillScanKey(), ginGetStats(), GinScanOpaqueData::ginstate, GinStatsData::ginVersion, i, IndexScanDescData::indexRelation, InvalidStrategy, GinScanOpaqueData::isVoidRes, IndexScanDescData::keyData, GinScanOpaqueData::keys, Max, GinScanOpaqueData::nkeys, NULL, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, palloc(), palloc0(), pgstat_count_index_scan, PointerGetDatum, RelationGetRelationName, ScanKeyData::sk_argument, ScanKeyData::sk_attno, ScanKeyData::sk_flags, SK_ISNULL, ScanKeyData::sk_strategy, GinState::supportCollation, GinScanOpaqueData::totalentries, and UInt16GetDatum.

Referenced by gingetbitmap().

{
    ScanKey     scankey = scan->keyData;
    GinScanOpaque so = (GinScanOpaque) scan->opaque;
    int         i;
    bool        hasNullQuery = false;

    /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
    so->keys = (GinScanKey)
        palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
    so->nkeys = 0;

    /* initialize expansible array of GinScanEntry pointers */
    so->totalentries = 0;
    so->allocentries = 32;
    so->entries = (GinScanEntry *)
        palloc0(so->allocentries * sizeof(GinScanEntry));

    so->isVoidRes = false;

    for (i = 0; i < scan->numberOfKeys; i++)
    {
        ScanKey     skey = &scankey[i];
        Datum      *queryValues;
        int32       nQueryValues = 0;
        bool       *partial_matches = NULL;
        Pointer    *extra_data = NULL;
        bool       *nullFlags = NULL;
        int32       searchMode = GIN_SEARCH_MODE_DEFAULT;

        /*
         * We assume that GIN-indexable operators are strict, so a null query
         * argument means an unsatisfiable query.
         */
        if (skey->sk_flags & SK_ISNULL)
        {
            so->isVoidRes = true;
            break;
        }

        /* OK to call the extractQueryFn */
        queryValues = (Datum *)
            DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
                           so->ginstate.supportCollation[skey->sk_attno - 1],
                                              skey->sk_argument,
                                              PointerGetDatum(&nQueryValues),
                                           UInt16GetDatum(skey->sk_strategy),
                                           PointerGetDatum(&partial_matches),
                                              PointerGetDatum(&extra_data),
                                              PointerGetDatum(&nullFlags),
                                              PointerGetDatum(&searchMode)));

        /*
         * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
         * in particular we don't allow extractQueryFn to select
         * GIN_SEARCH_MODE_EVERYTHING.
         */
        if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
            searchMode > GIN_SEARCH_MODE_ALL)
            searchMode = GIN_SEARCH_MODE_ALL;

        /* Non-default modes require the index to have placeholders */
        if (searchMode != GIN_SEARCH_MODE_DEFAULT)
            hasNullQuery = true;

        /*
         * In default mode, no keys means an unsatisfiable query.
         */
        if (queryValues == NULL || nQueryValues <= 0)
        {
            if (searchMode == GIN_SEARCH_MODE_DEFAULT)
            {
                so->isVoidRes = true;
                break;
            }
            nQueryValues = 0;   /* ensure sane value */
        }

        /*
         * If the extractQueryFn didn't create a nullFlags array, create one,
         * assuming that everything's non-null.  Otherwise, run through the
         * array and make sure each value is exactly 0 or 1; this ensures
         * binary compatibility with the GinNullCategory representation. While
         * at it, detect whether any null keys are present.
         */
        if (nullFlags == NULL)
            nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
        else
        {
            int32       j;

            for (j = 0; j < nQueryValues; j++)
            {
                if (nullFlags[j])
                {
                    nullFlags[j] = true;        /* not any other nonzero value */
                    hasNullQuery = true;
                }
            }
        }
        /* now we can use the nullFlags as category codes */

        ginFillScanKey(so, skey->sk_attno,
                       skey->sk_strategy, searchMode,
                       skey->sk_argument, nQueryValues,
                       queryValues, (GinNullCategory *) nullFlags,
                       partial_matches, extra_data);
    }

    /*
     * If there are no regular scan keys, generate an EVERYTHING scankey to
     * drive a full-index scan.
     */
    if (so->nkeys == 0 && !so->isVoidRes)
    {
        hasNullQuery = true;
        ginFillScanKey(so, FirstOffsetNumber,
                       InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING,
                       (Datum) 0, 0,
                       NULL, NULL, NULL, NULL);
    }

    /*
     * If the index is version 0, it may be missing null and placeholder
     * entries, which would render searches for nulls and full-index scans
     * unreliable.  Throw an error if so.
     */
    if (hasNullQuery && !so->isVoidRes)
    {
        GinStatsData ginStats;

        ginGetStats(scan->indexRelation, &ginStats);
        if (ginStats.ginVersion < 1)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
                     errhint("To fix this, do REINDEX INDEX \"%s\".",
                             RelationGetRelationName(scan->indexRelation))));
    }

    pgstat_count_index_scan(scan->indexRelation);
}

Datum ginoptions ( PG_FUNCTION_ARGS   ) 

Definition at line 496 of file ginutil.c.

References allocateReloptStruct(), fillRelOptions(), lengthof, offsetof, parseRelOptions(), pfree(), PG_GETARG_BOOL, PG_GETARG_DATUM, PG_RETURN_BYTEA_P, PG_RETURN_NULL, and RELOPT_KIND_GIN.

{
    Datum       reloptions = PG_GETARG_DATUM(0);
    bool        validate = PG_GETARG_BOOL(1);
    relopt_value *options;
    GinOptions *rdopts;
    int         numoptions;
    static const relopt_parse_elt tab[] = {
        {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
    };

    options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
                              &numoptions);

    /* if none set, we're done */
    if (numoptions == 0)
        PG_RETURN_NULL();

    rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);

    fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
                   validate, tab, lengthof(tab));

    pfree(options);

    PG_RETURN_BYTEA_P(rdopts);
}

void GinPageDeletePostingItem ( Page  page,
OffsetNumber  offset 
)

Definition at line 308 of file gindatapage.c.

References Assert, FirstOffsetNumber, GinDataPageGetItem, GinPageGetOpaque, GinPageIsLeaf, and memmove.

Referenced by ginDeletePage(), and ginRedoDeletePage().

{
    OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;

    Assert(!GinPageIsLeaf(page));
    Assert(offset >= FirstOffsetNumber && offset <= maxoff);

    if (offset != maxoff)
        memmove(GinDataPageGetItem(page, offset), GinDataPageGetItem(page, offset + 1),
                sizeof(PostingItem) * (maxoff - offset));

    GinPageGetOpaque(page)->maxoff--;
}

IndexTuple ginPageGetLinkItup ( Buffer  buf  ) 

Definition at line 665 of file ginentrypage.c.

References BufferGetBlockNumber(), BufferGetPage, getRightMostTuple(), and GinFormInteriorTuple().

Referenced by ginContinueSplit(), and ginEntryFillRoot().

{
    IndexTuple  itup,
                nitup;
    Page        page = BufferGetPage(buf);

    itup = getRightMostTuple(page);
    nitup = GinFormInteriorTuple(itup, page, BufferGetBlockNumber(buf));

    return nitup;
}

void ginPrepareDataScan ( GinBtree  btree,
Relation  index 
)

Definition at line 606 of file gindatapage.c.

References GinBtreeData::fillRoot, GinBtreeData::findChildPage, GinBtreeData::findChildPtr, GinBtreeData::findItem, GinBtreeData::fullScan, GinBtreeData::getLeftMostPage, GinBtreeData::index, GinBtreeData::isBuild, GinBtreeData::isData, GinBtreeData::isDelete, GinBtreeData::isEnoughSpace, GinBtreeData::isMoveRight, GinBtreeData::placeToPage, GinBtreeData::searchMode, and GinBtreeData::splitPage.

Referenced by ginContinueSplit(), and ginPrepareScanPostingTree().

{
    memset(btree, 0, sizeof(GinBtreeData));

    btree->index = index;

    btree->findChildPage = dataLocateItem;
    btree->isMoveRight = dataIsMoveRight;
    btree->findItem = dataLocateLeafItem;
    btree->findChildPtr = dataFindChildPtr;
    btree->getLeftMostPage = dataGetLeftMostPage;
    btree->isEnoughSpace = dataIsEnoughSpace;
    btree->placeToPage = dataPlaceToPage;
    btree->splitPage = dataSplitPage;
    btree->fillRoot = ginDataFillRoot;

    btree->isData = TRUE;
    btree->searchMode = FALSE;
    btree->isDelete = FALSE;
    btree->fullScan = FALSE;
    btree->isBuild = FALSE;
}

void ginPrepareEntryScan ( GinBtree  btree,
OffsetNumber  attnum,
Datum  key,
GinNullCategory  category,
GinState ginstate 
)

Definition at line 707 of file ginentrypage.c.

References GinBtreeData::entryAttnum, GinBtreeData::entryCategory, GinBtreeData::entryKey, GinBtreeData::fillRoot, GinBtreeData::findChildPage, GinBtreeData::findChildPtr, GinBtreeData::findItem, GinBtreeData::fullScan, GinBtreeData::getLeftMostPage, GinBtreeData::ginstate, GinState::index, GinBtreeData::index, GinBtreeData::isBuild, GinBtreeData::isData, GinBtreeData::isDelete, GinBtreeData::isEnoughSpace, GinBtreeData::isMoveRight, GinBtreeData::placeToPage, GinBtreeData::searchMode, and GinBtreeData::splitPage.

Referenced by ginContinueSplit(), ginEntryInsert(), and startScanEntry().

{
    memset(btree, 0, sizeof(GinBtreeData));

    btree->index = ginstate->index;
    btree->ginstate = ginstate;

    btree->findChildPage = entryLocateEntry;
    btree->isMoveRight = entryIsMoveRight;
    btree->findItem = entryLocateLeafEntry;
    btree->findChildPtr = entryFindChildPtr;
    btree->getLeftMostPage = entryGetLeftMostPage;
    btree->isEnoughSpace = entryIsEnoughSpace;
    btree->placeToPage = entryPlaceToPage;
    btree->splitPage = entrySplitPage;
    btree->fillRoot = ginEntryFillRoot;

    btree->isData = FALSE;
    btree->searchMode = FALSE;
    btree->fullScan = FALSE;
    btree->isBuild = FALSE;

    btree->entryAttnum = attnum;
    btree->entryKey = key;
    btree->entryCategory = category;
    btree->isDelete = FALSE;
}

GinBtreeStack* ginPrepareFindLeafPage ( GinBtree  btree,
BlockNumber  blkno 
)
GinPostingTreeScan* ginPrepareScanPostingTree ( Relation  index,
BlockNumber  rootBlkno,
bool  searchMode 
)
Datum ginrescan ( PG_FUNCTION_ARGS   ) 

Definition at line 409 of file ginscan.c.

References freeScanKeys(), IndexScanDescData::keyData, memmove, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, PG_GETARG_POINTER, and PG_RETURN_VOID.

{
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    ScanKey     scankey = (ScanKey) PG_GETARG_POINTER(1);

    /* remaining arguments are ignored */
    GinScanOpaque so = (GinScanOpaque) scan->opaque;

    freeScanKeys(so);

    if (scankey && scan->numberOfKeys > 0)
    {
        memmove(scan->keyData, scankey,
                scan->numberOfKeys * sizeof(ScanKeyData));
    }

    PG_RETURN_VOID();
}

Datum ginrestrpos ( PG_FUNCTION_ARGS   ) 

Definition at line 452 of file ginscan.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "GIN does not support mark/restore");
    PG_RETURN_VOID();
}

Buffer ginScanBeginPostingTree ( GinPostingTreeScan gdi  ) 

Definition at line 682 of file gindatapage.c.

References GinPostingTreeScan::btree, GinBtreeStack::buffer, ginFindLeafPage(), and GinPostingTreeScan::stack.

Referenced by scanPostingTree(), and startScanEntry().

{
    gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
    return gdi->stack->buffer;
}

void GinShortenTuple ( IndexTuple  itup,
uint32  nipd 
)

Definition at line 145 of file ginentrypage.c.

References Assert, GinGetNPosting, GinGetPostingOffset, GinSetNPosting, INDEX_SIZE_MASK, MAXALIGN, and IndexTupleData::t_info.

Referenced by addItemPointersToLeafTuple().

{
    uint32      newsize;

    Assert(nipd <= GinGetNPosting(itup));

    newsize = GinGetPostingOffset(itup) + sizeof(ItemPointerData) * nipd;
    newsize = MAXALIGN(newsize);

    Assert(newsize <= (itup->t_info & INDEX_SIZE_MASK));

    itup->t_info &= ~INDEX_SIZE_MASK;
    itup->t_info |= newsize;

    GinSetNPosting(itup, nipd);
}

OffsetNumber gintuple_get_attrnum ( GinState ginstate,
IndexTuple  tuple 
)

Definition at line 112 of file ginutil.c.

References Assert, DatumGetUInt16, FirstOffsetNumber, index_getattr, GinState::oneCol, and GinState::tupdesc.

Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), gintuple_get_key(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().

{
    OffsetNumber colN;

    if (ginstate->oneCol)
    {
        /* column number is not stored explicitly */
        colN = FirstOffsetNumber;
    }
    else
    {
        Datum       res;
        bool        isnull;

        /*
         * First attribute is always int16, so we can safely use any tuple
         * descriptor to obtain first attribute of tuple
         */
        res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
                            &isnull);
        Assert(!isnull);

        colN = DatumGetUInt16(res);
        Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
    }

    return colN;
}

Datum gintuple_get_key ( GinState ginstate,
IndexTuple  tuple,
GinNullCategory category 
)

Definition at line 145 of file ginutil.c.

References FirstOffsetNumber, GinGetNullCategory, gintuple_get_attrnum(), index_getattr, OffsetNumberNext, GinState::oneCol, GinState::origTupdesc, and GinState::tupdesc.

Referenced by addItemPointersToLeafTuple(), collectMatchBitmap(), collectMatchesForHeapRow(), entryIsMoveRight(), entryLocateEntry(), entryLocateLeafEntry(), ginVacuumEntryPage(), matchPartialInPendingList(), and processPendingPage().

{
    Datum       res;
    bool        isnull;

    if (ginstate->oneCol)
    {
        /*
         * Single column index doesn't store attribute numbers in tuples
         */
        res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
                            &isnull);
    }
    else
    {
        /*
         * Since the datum type depends on which index column it's from, we
         * must be careful to use the right tuple descriptor here.
         */
        OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);

        res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
                            ginstate->tupdesc[colN - 1],
                            &isnull);
    }

    if (isnull)
        *category = GinGetNullCategory(tuple, ginstate);
    else
        *category = GIN_CAT_NORM_KEY;

    return res;
}

Datum ginvacuumcleanup ( PG_FUNCTION_ARGS   ) 

Definition at line 694 of file ginvacuum.c.

References IndexVacuumInfo::analyze_only, Assert, DataPageDeleteStack::blkno, BufferGetPage, IndexVacuumInfo::estimated_count, IndexBulkDeleteResult::estimated_count, ExclusiveLock, GIN_ROOT_BLKNO, GIN_SHARE, ginInsertCleanup(), GinPageIsData, GinPageIsDeleted, GinPageIsLeaf, GinPageIsList, ginUpdateStats(), IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), initGinState(), IsAutoVacuumWorkerProcess(), LockBuffer(), LockRelationForExtension(), MAIN_FORKNUM, GinStatsData::nDataPages, GinStatsData::nEntries, GinStatsData::nEntryPages, GinStatsData::nTotalPages, NULL, IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, PageGetMaxOffsetNumber, IndexBulkDeleteResult::pages_free, palloc0(), PG_GETARG_POINTER, PG_RETURN_POINTER, RBM_NORMAL, ReadBufferExtended(), RecordFreeIndexPage(), RELATION_IS_LOCAL, RelationGetNumberOfBlocks, IndexVacuumInfo::strategy, UnlockRelationForExtension(), UnlockReleaseBuffer(), and vacuum_delay_point().

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    Relation    index = info->index;
    bool        needLock;
    BlockNumber npages,
                blkno;
    BlockNumber totFreePages;
    GinState    ginstate;
    GinStatsData idxStat;

    /*
     * In an autovacuum analyze, we want to clean up pending insertions.
     * Otherwise, an ANALYZE-only call is a no-op.
     */
    if (info->analyze_only)
    {
        if (IsAutoVacuumWorkerProcess())
        {
            initGinState(&ginstate, index);
            ginInsertCleanup(&ginstate, true, stats);
        }
        PG_RETURN_POINTER(stats);
    }

    /*
     * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
     * wasn't called
     */
    if (stats == NULL)
    {
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
        initGinState(&ginstate, index);
        ginInsertCleanup(&ginstate, true, stats);
    }

    memset(&idxStat, 0, sizeof(idxStat));

    /*
     * XXX we always report the heap tuple count as the number of index
     * entries.  This is bogus if the index is partial, but it's real hard to
     * tell how many distinct heap entries are referenced by a GIN index.
     */
    stats->num_index_tuples = info->num_heap_tuples;
    stats->estimated_count = info->estimated_count;

    /*
     * Need lock unless it's local to this backend.
     */
    needLock = !RELATION_IS_LOCAL(index);

    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);
    npages = RelationGetNumberOfBlocks(index);
    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    totFreePages = 0;

    for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
    {
        Buffer      buffer;
        Page        page;

        vacuum_delay_point();

        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
        LockBuffer(buffer, GIN_SHARE);
        page = (Page) BufferGetPage(buffer);

        if (GinPageIsDeleted(page))
        {
            Assert(blkno != GIN_ROOT_BLKNO);
            RecordFreeIndexPage(index, blkno);
            totFreePages++;
        }
        else if (GinPageIsData(page))
        {
            idxStat.nDataPages++;
        }
        else if (!GinPageIsList(page))
        {
            idxStat.nEntryPages++;

            if (GinPageIsLeaf(page))
                idxStat.nEntries += PageGetMaxOffsetNumber(page);
        }

        UnlockReleaseBuffer(buffer);
    }

    /* Update the metapage with accurate page and entry counts */
    idxStat.nTotalPages = npages;
    ginUpdateStats(info->index, &idxStat);

    /* Finally, vacuum the FSM */
    IndexFreeSpaceMapVacuum(info->index);

    stats->pages_free = totFreePages;

    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);
    stats->num_pages = RelationGetNumberOfBlocks(index);
    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    PG_RETURN_POINTER(stats);
}

void initGinState ( GinState state,
Relation  index 
)

Definition at line 32 of file ginutil.c.

References tupleDesc::attrs, GinState::canPartialMatch, GinState::compareFn, GinState::comparePartialFn, GinState::consistentFn, CreateTemplateTupleDesc(), CurrentMemoryContext, GinState::extractQueryFn, GinState::extractValueFn, fmgr_info_copy(), GIN_COMPARE_PARTIAL_PROC, GIN_COMPARE_PROC, GIN_CONSISTENT_PROC, GIN_EXTRACTQUERY_PROC, GIN_EXTRACTVALUE_PROC, i, GinState::index, index_getprocid(), index_getprocinfo(), INT2OID, InvalidOid, MemSet, tupleDesc::natts, NULL, OidIsValid, GinState::oneCol, GinState::origTupdesc, RelationData::rd_indcollation, RelationGetDescr, GinState::supportCollation, GinState::tupdesc, TupleDescInitEntry(), and TupleDescInitEntryCollation().

Referenced by ginbeginscan(), ginbuild(), ginbulkdelete(), gininsert(), and ginvacuumcleanup().

{
    TupleDesc   origTupdesc = RelationGetDescr(index);
    int         i;

    MemSet(state, 0, sizeof(GinState));

    state->index = index;
    state->oneCol = (origTupdesc->natts == 1) ? true : false;
    state->origTupdesc = origTupdesc;

    for (i = 0; i < origTupdesc->natts; i++)
    {
        if (state->oneCol)
            state->tupdesc[i] = state->origTupdesc;
        else
        {
            state->tupdesc[i] = CreateTemplateTupleDesc(2, false);

            TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
                               INT2OID, -1, 0);
            TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
                               origTupdesc->attrs[i]->atttypid,
                               origTupdesc->attrs[i]->atttypmod,
                               origTupdesc->attrs[i]->attndims);
            TupleDescInitEntryCollation(state->tupdesc[i], (AttrNumber) 2,
                                        origTupdesc->attrs[i]->attcollation);
        }

        fmgr_info_copy(&(state->compareFn[i]),
                       index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->extractValueFn[i]),
                       index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->extractQueryFn[i]),
                       index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
                       CurrentMemoryContext);
        fmgr_info_copy(&(state->consistentFn[i]),
                       index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
                       CurrentMemoryContext);

        /*
         * Check opclass capability to do partial match.
         */
        if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
        {
            fmgr_info_copy(&(state->comparePartialFn[i]),
                   index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
                           CurrentMemoryContext);
            state->canPartialMatch[i] = true;
        }
        else
        {
            state->canPartialMatch[i] = false;
        }

        /*
         * If the index column has a specified collation, we should honor that
         * while doing comparisons.  However, we may have a collatable storage
         * type for a noncollatable indexed data type (for instance, hstore
         * uses text index entries).  If there's no index collation then
         * specify default collation in case the support functions need
         * collation.  This is harmless if the support functions don't care
         * about collation, so we just do it unconditionally.  (We could
         * alternatively call get_typcollation, but that seems like expensive
         * overkill --- there aren't going to be any cases where a GIN storage
         * type has a nondefault collation.)
         */
        if (OidIsValid(index->rd_indcollation[i]))
            state->supportCollation[i] = index->rd_indcollation[i];
        else
            state->supportCollation[i] = DEFAULT_COLLATION_OID;
    }
}