PostgreSQL Source Code  git master
gist_private.h File Reference
#include "access/amapi.h"
#include "access/gist.h"
#include "access/itup.h"
#include "lib/pairingheap.h"
#include "storage/bufmgr.h"
#include "storage/buffile.h"
#include "utils/hsearch.h"
#include "access/genam.h"
Include dependency graph for gist_private.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  GISTNodeBufferPage
 
struct  GISTSTATE
 
struct  GISTSearchHeapItem
 
struct  GISTSearchItem
 
struct  GISTScanOpaqueData
 
struct  gistxlogPage
 
struct  SplitedPageLayout
 
struct  GISTInsertStack
 
struct  GistSplitVector
 
struct  GISTInsertState
 
struct  GISTNodeBuffer
 
struct  GISTBuildBuffers
 
struct  GiSTOptions
 
struct  GISTPageSplitInfo
 

Macros

#define GIST_MAX_SPLIT_PAGES   75
 
#define GIST_SHARE   BUFFER_LOCK_SHARE
 
#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE
 
#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK
 
#define BUFFER_PAGE_DATA_OFFSET   MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))
 
#define PAGE_FREE_SPACE(nbp)   (nbp->freespace)
 
#define PAGE_IS_EMPTY(nbp)   (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)
 
#define PAGE_NO_SPACE(nbp, itup)
 
#define GISTSearchItemIsHeap(item)   ((item).blkno == InvalidBlockNumber)
 
#define SizeOfGISTSearchItem(n_distances)
 
#define GIST_ROOT_BLKNO   0
 
#define TUPLE_IS_VALID   0xffff
 
#define TUPLE_IS_INVALID   0xfffe
 
#define GistTupleIsInvalid(itup)   ( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )
 
#define GistTupleSetValid(itup)   ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )
 
#define LEVEL_HAS_BUFFERS(nlevel, gfbb)
 
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb)   ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)
 
#define BUFFER_OVERFLOWED(nodeBuffer, gfbb)   ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)
 
#define GiSTPageSize   ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )
 
#define GIST_MIN_FILLFACTOR   10
 
#define GIST_DEFAULT_FILLFACTOR   90
 

Typedefs

typedef struct GISTSTATE GISTSTATE
 
typedef struct GISTSearchHeapItem GISTSearchHeapItem
 
typedef struct GISTSearchItem GISTSearchItem
 
typedef struct GISTScanOpaqueData GISTScanOpaqueData
 
typedef GISTScanOpaqueDataGISTScanOpaque
 
typedef struct gistxlogPage gistxlogPage
 
typedef struct SplitedPageLayout SplitedPageLayout
 
typedef struct GISTInsertStack GISTInsertStack
 
typedef struct GistSplitVector GistSplitVector
 
typedef struct GISTBuildBuffers GISTBuildBuffers
 
typedef enum GistOptBufferingMode GistOptBufferingMode
 
typedef struct GiSTOptions GiSTOptions
 

Enumerations

enum  GistOptBufferingMode { GIST_OPTION_BUFFERING_AUTO, GIST_OPTION_BUFFERING_ON, GIST_OPTION_BUFFERING_OFF }
 

Functions

void gistbuildempty (Relation index)
 
bool gistinsert (Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, struct IndexInfo *indexInfo)
 
MemoryContext createTempGistContext (void)
 
GISTSTATEinitGISTstate (Relation index)
 
void freeGISTstate (GISTSTATE *giststate)
 
void gistdoinsert (Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
 
bool gistplacetopage (Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright, Relation heapRel, bool is_build)
 
SplitedPageLayoutgistSplit (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
 
XLogRecPtr gistXLogPageDelete (Buffer buffer, FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset)
 
void gistXLogPageReuse (Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ntup, Buffer leftchild)
 
XLogRecPtr gistXLogDelete (Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId latestRemovedXid)
 
XLogRecPtr gistXLogSplit (bool page_is_leaf, SplitedPageLayout *dist, BlockNumber origrlink, GistNSN oldnsn, Buffer leftchild, bool markfollowright)
 
bool gistgettuple (IndexScanDesc scan, ScanDirection dir)
 
int64 gistgetbitmap (IndexScanDesc scan, TIDBitmap *tbm)
 
bool gistcanreturn (Relation index, int attno)
 
bool gistvalidate (Oid opclassoid)
 
byteagistoptions (Datum reloptions, bool validate)
 
bool gistproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 
bool gistfitpage (IndexTuple *itvec, int len)
 
bool gistnospace (Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
 
void gistcheckpage (Relation rel, Buffer buf)
 
Buffer gistNewBuffer (Relation r)
 
bool gistPageRecyclable (Page page)
 
void gistfillbuffer (Page page, IndexTuple *itup, int len, OffsetNumber off)
 
IndexTuplegistextractpage (Page page, int *len)
 
IndexTuplegistjoinvector (IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
 
IndexTupleDatagistfillitupvec (IndexTuple *vec, int veclen, int *memlen)
 
IndexTuple gistunion (Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 
IndexTuple gistgetadjusted (Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 
IndexTuple gistFormTuple (GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf)
 
OffsetNumber gistchoose (Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
 
void GISTInitBuffer (Buffer b, uint32 f)
 
void gistdentryinit (GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
 
float gistpenalty (GISTSTATE *giststate, int attno, GISTENTRY *key1, bool isNull1, GISTENTRY *key2, bool isNull2)
 
void gistMakeUnionItVec (GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
 
bool gistKeyIsEQ (GISTSTATE *giststate, int attno, Datum a, Datum b)
 
void gistDeCompressAtt (GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
 
HeapTuple gistFetchTuple (GISTSTATE *giststate, Relation r, IndexTuple tuple)
 
void gistMakeUnionKey (GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
 
XLogRecPtr gistGetFakeLSN (Relation rel)
 
IndexBulkDeleteResultgistbulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
 
IndexBulkDeleteResultgistvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 
void gistSplitByKey (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
 
IndexBuildResultgistbuild (Relation heap, Relation index, struct IndexInfo *indexInfo)
 
void gistValidateBufferingOption (const char *value)
 
GISTBuildBuffersgistInitBuildBuffers (int pagesPerBuffer, int levelStep, int maxLevel)
 
GISTNodeBuffergistGetNodeBuffer (GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber blkno, int level)
 
void gistPushItupToNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple item)
 
bool gistPopItupFromNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *item)
 
void gistFreeBuildBuffers (GISTBuildBuffers *gfbb)
 
void gistRelocateBuildBuffersOnSplit (GISTBuildBuffers *gfbb, GISTSTATE *giststate, Relation r, int level, Buffer buffer, List *splitinfo)
 
void gistUnloadNodeBuffers (GISTBuildBuffers *gfbb)
 

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

#define BUFFER_HALF_FILLED (   nodeBuffer,
  gfbb 
)    ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer / 2)

Definition at line 324 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ BUFFER_OVERFLOWED

#define BUFFER_OVERFLOWED (   nodeBuffer,
  gfbb 
)    ((nodeBuffer)->blocksCount > (gfbb)->pagesPerBuffer)

Definition at line 332 of file gist_private.h.

Referenced by gistProcessItup().

◆ BUFFER_PAGE_DATA_OFFSET

#define BUFFER_PAGE_DATA_OFFSET   MAXALIGN(offsetof(GISTNodeBufferPage, tupledata))

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 472 of file gist_private.h.

Referenced by gistbuild().

◆ GIST_EXCLUSIVE

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

Referenced by gistplacetopage().

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 471 of file gist_private.h.

◆ GIST_ROOT_BLKNO

◆ GIST_SHARE

◆ GIST_UNLOCK

◆ GiSTPageSize

#define GiSTPageSize   ( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GISTPageOpaqueData)) )

Definition at line 468 of file gist_private.h.

Referenced by gistfitpage(), and gistSplit().

◆ GISTSearchItemIsHeap

#define GISTSearchItemIsHeap (   item)    ((item).blkno == InvalidBlockNumber)

Definition at line 145 of file gist_private.h.

Referenced by getNextNearest(), gistScanPage(), and pairingheap_GISTSearchItem_cmp().

◆ GistTupleIsInvalid

#define GistTupleIsInvalid (   itup)    ( ItemPointerGetOffsetNumber( &((itup)->t_tid) ) == TUPLE_IS_INVALID )

Definition at line 288 of file gist_private.h.

Referenced by gistdoinsert(), gistindex_keytest(), and gistvacuumpage().

◆ GistTupleSetValid

#define GistTupleSetValid (   itup)    ItemPointerSetOffsetNumber( &((itup)->t_tid), TUPLE_IS_VALID )

Definition at line 289 of file gist_private.h.

Referenced by gistformdownlink(), and gistplacetopage().

◆ LEVEL_HAS_BUFFERS

#define LEVEL_HAS_BUFFERS (   nlevel,
  gfbb 
)
Value:
((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
(nlevel) != (gfbb)->rootlevel)
Datum nlevel(PG_FUNCTION_ARGS)
Definition: ltree_op.c:133

Definition at line 319 of file gist_private.h.

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ PAGE_FREE_SPACE

#define PAGE_FREE_SPACE (   nbp)    (nbp->freespace)

◆ PAGE_IS_EMPTY

#define PAGE_IS_EMPTY (   nbp)    (nbp->freespace == BLCKSZ - BUFFER_PAGE_DATA_OFFSET)

Definition at line 57 of file gist_private.h.

Referenced by gistGetItupFromPage(), and gistPopItupFromNodeBuffer().

◆ PAGE_NO_SPACE

#define PAGE_NO_SPACE (   nbp,
  itup 
)
Value:
(PAGE_FREE_SPACE(nbp) < \
MAXALIGN(IndexTupleSize(itup)))
#define PAGE_FREE_SPACE(nbp)
Definition: gist_private.h:55
#define IndexTupleSize(itup)
Definition: itup.h:71

Definition at line 59 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ SizeOfGISTSearchItem

#define SizeOfGISTSearchItem (   n_distances)
Value:
(offsetof(GISTSearchItem, distances) + \
sizeof(IndexOrderByDistance) * (n_distances))
#define offsetof(type, field)
Definition: c.h:655

Definition at line 147 of file gist_private.h.

Referenced by gistScanPage().

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 286 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 285 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GistOptBufferingMode

◆ GiSTOptions

typedef struct GiSTOptions GiSTOptions

◆ GISTScanOpaque

Definition at line 181 of file gist_private.h.

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

typedef struct GISTSTATE GISTSTATE

◆ gistxlogPage

typedef struct gistxlogPage gistxlogPage

◆ SplitedPageLayout

Enumeration Type Documentation

◆ GistOptBufferingMode

Enumerator
GIST_OPTION_BUFFERING_AUTO 
GIST_OPTION_BUFFERING_ON 
GIST_OPTION_BUFFERING_OFF 

Definition at line 384 of file gist_private.h.

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 114 of file gist.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

Referenced by gist_xlog_startup(), gistbeginscan(), gistbuild(), and gistinsert().

115 {
117  "GiST temporary context",
119 }
#define AllocSetContextCreate
Definition: memutils.h:170
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1614 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

1615 {
1616  /* It's sufficient to delete the scanCxt */
1617  MemoryContextDelete(giststate->scanCxt);
1618 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
MemoryContext scanCxt
Definition: gist_private.h:77

◆ gistbuild()

IndexBuildResult* gistbuild ( Relation  heap,
Relation  index,
struct IndexInfo indexInfo 
)

Definition at line 116 of file gistbuild.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, GiSTOptions::buffering_mode, GISTBuildState::bufferingMode, createTempGistContext(), CurrentMemoryContext, DEBUG1, elog, END_CRIT_SECTION, ERROR, F_LEAF, fillfactor, GiSTOptions::fillfactor, freeGISTstate(), GISTBuildState::freespace, GIST_BUFFERING_ACTIVE, GIST_BUFFERING_AUTO, GIST_BUFFERING_DISABLED, GIST_BUFFERING_STATS, GIST_DEFAULT_FILLFACTOR, GIST_OPTION_BUFFERING_OFF, GIST_OPTION_BUFFERING_ON, GIST_ROOT_BLKNO, gistBuildCallback(), GistBuildLSN, gistEmptyAllBuffers(), gistFreeBuildBuffers(), GISTInitBuffer(), gistNewBuffer(), GISTBuildState::giststate, IndexBuildResult::heap_tuples, GISTBuildState::heaprel, IndexBuildResult::index_tuples, GISTBuildState::indexrel, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), log_newpage_range(), MAIN_FORKNUM, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), PageSetLSN, palloc(), RelationData::rd_options, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, reltuples, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, and UnlockReleaseBuffer().

Referenced by gisthandler().

117 {
118  IndexBuildResult *result;
119  double reltuples;
120  GISTBuildState buildstate;
121  Buffer buffer;
122  Page page;
124  int fillfactor;
125 
126  buildstate.indexrel = index;
127  buildstate.heaprel = heap;
128 
129  if (index->rd_options)
130  {
131  /* Get buffering mode from the options string */
133 
135  buildstate.bufferingMode = GIST_BUFFERING_STATS;
136  else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
138  else
139  buildstate.bufferingMode = GIST_BUFFERING_AUTO;
140 
141  fillfactor = options->fillfactor;
142  }
143  else
144  {
145  /*
146  * By default, switch to buffering mode when the index grows too large
147  * to fit in cache.
148  */
149  buildstate.bufferingMode = GIST_BUFFERING_AUTO;
150  fillfactor = GIST_DEFAULT_FILLFACTOR;
151  }
152  /* Calculate target amount of free space to leave on pages */
153  buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
154 
155  /*
156  * We expect to be called exactly once for any index relation. If that's
157  * not the case, big trouble's what we have.
158  */
159  if (RelationGetNumberOfBlocks(index) != 0)
160  elog(ERROR, "index \"%s\" already contains data",
161  RelationGetRelationName(index));
162 
163  /* no locking is needed */
164  buildstate.giststate = initGISTstate(index);
165 
166  /*
167  * Create a temporary memory context that is reset once for each tuple
168  * processed. (Note: we don't bother to make this a child of the
169  * giststate's scanCxt, so we have to delete it separately at the end.)
170  */
171  buildstate.giststate->tempCxt = createTempGistContext();
172 
173  /* initialize the root page */
174  buffer = gistNewBuffer(index);
176  page = BufferGetPage(buffer);
177 
179 
180  GISTInitBuffer(buffer, F_LEAF);
181 
182  MarkBufferDirty(buffer);
183  PageSetLSN(page, GistBuildLSN);
184 
185  UnlockReleaseBuffer(buffer);
186 
188 
189  /* build the index */
190  buildstate.indtuples = 0;
191  buildstate.indtuplesSize = 0;
192 
193  /*
194  * Do the heap scan.
195  */
196  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
198  (void *) &buildstate, NULL);
199 
200  /*
201  * If buffering was used, flush out all the tuples that are still in the
202  * buffers.
203  */
204  if (buildstate.bufferingMode == GIST_BUFFERING_ACTIVE)
205  {
206  elog(DEBUG1, "all tuples processed, emptying buffers");
207  gistEmptyAllBuffers(&buildstate);
208  gistFreeBuildBuffers(buildstate.gfbb);
209  }
210 
211  /* okay, all heap tuples are indexed */
212  MemoryContextSwitchTo(oldcxt);
214 
215  freeGISTstate(buildstate.giststate);
216 
217  /*
218  * We didn't write WAL records as we built the index, so if WAL-logging is
219  * required, write all pages to the WAL now.
220  */
221  if (RelationNeedsWAL(index))
222  {
224  0, RelationGetNumberOfBlocks(index),
225  true);
226  }
227 
228  /*
229  * Return statistics
230  */
231  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
232 
233  result->heap_tuples = reltuples;
234  result->index_tuples = (double) buildstate.indtuples;
235 
236  return result;
237 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define GistBuildLSN
Definition: gist.h:58
GistOptBufferingMode buffering_mode
Definition: gist_private.h:398
#define DEBUG1
Definition: elog.h:25
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
MemoryContext createTempGistContext(void)
Definition: gist.c:114
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
GistBufferingMode bufferingMode
Definition: gistbuild.c:76
int64 indtuplesSize
Definition: gistbuild.c:64
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define ERROR
Definition: elog.h:43
int fillfactor
Definition: pgbench.c:157
static double table_index_build_scan(Relation table_rel, Relation index_rel, struct IndexInfo *index_info, bool allow_sync, bool progress, IndexBuildCallback callback, void *callback_state, TableScanDesc scan)
Definition: tableam.h:1499
int64 indtuples
Definition: gistbuild.c:63
MemoryContext tempCxt
Definition: gist_private.h:78
Size freespace
Definition: gistbuild.c:66
#define RelationGetRelationName(relation)
Definition: rel.h:453
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:472
Relation heaprel
Definition: gistbuild.c:60
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1614
GISTSTATE * giststate
Definition: gistbuild.c:61
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:198
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1486
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:977
#define Assert(condition)
Definition: c.h:732
Relation indexrel
Definition: gistbuild.c:59
#define RelationNeedsWAL(relation)
Definition: rel.h:521
void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1042
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
void * palloc(Size size)
Definition: mcxt.c:949
static void gistBuildCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:442
#define F_LEAF
Definition: gist.h:43
GISTBuildBuffers * gfbb
Definition: gistbuild.c:73
#define elog(elevel,...)
Definition: elog.h:226
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:749
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:810
float4 reltuples
Definition: pg_class.h:63
bytea * rd_options
Definition: rel.h:126
Pointer Page
Definition: bufpage.h:78
double index_tuples
Definition: genam.h:33
double heap_tuples
Definition: genam.h:32

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 125 of file gist.c.

References BUFFER_LOCK_EXCLUSIVE, END_CRIT_SECTION, F_LEAF, GISTInitBuffer(), INIT_FORKNUM, LockBuffer(), log_newpage_buffer(), MarkBufferDirty(), P_NEW, RBM_NORMAL, ReadBufferExtended(), START_CRIT_SECTION, and UnlockReleaseBuffer().

Referenced by gisthandler().

126 {
127  Buffer buffer;
128 
129  /* Initialize the root page */
130  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
132 
133  /* Initialize and xlog buffer */
135  GISTInitBuffer(buffer, F_LEAF);
136  MarkBufferDirty(buffer);
137  log_newpage_buffer(buffer, true);
139 
140  /* Unlock and release the buffer */
141  UnlockReleaseBuffer(buffer);
142 }
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1009
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:642
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define P_NEW
Definition: bufmgr.h:81
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:88
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3602
#define F_LEAF
Definition: gist.h:43
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:749
int Buffer
Definition: buf.h:23

◆ gistbulkdelete()

IndexBulkDeleteResult* gistbulkdelete ( IndexVacuumInfo info,
IndexBulkDeleteResult stats,
IndexBulkDeleteCallback  callback,
void *  callback_state 
)

Definition at line 83 of file gistvacuum.c.

References create_GistBulkDeleteResult(), and gistvacuumscan().

Referenced by gisthandler().

85 {
86  GistBulkDeleteResult *gist_stats = (GistBulkDeleteResult *) stats;
87 
88  /* allocate stats if first time through, else re-use existing struct */
89  if (gist_stats == NULL)
90  gist_stats = create_GistBulkDeleteResult();
91 
92  gistvacuumscan(info, gist_stats, callback, callback_state);
93 
94  return (IndexBulkDeleteResult *) gist_stats;
95 }
static void gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:164
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
static GistBulkDeleteResult * create_GistBulkDeleteResult(void)
Definition: gistvacuum.c:66

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

References GIST_COMPRESS_PROC, GIST_FETCH_PROC, index_getprocid(), IndexRelationGetNumberOfKeyAttributes, and OidIsValid.

Referenced by gisthandler().

796 {
797  if (attno > IndexRelationGetNumberOfKeyAttributes(index) ||
798  OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
800  return true;
801  else
802  return false;
803 }
#define GIST_FETCH_PROC
Definition: gist.h:37
#define OidIsValid(objectId)
Definition: c.h:638
#define GIST_COMPRESS_PROC
Definition: gist.h:31
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:438
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:760

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 771 of file gistutil.c.

References BufferGetBlockNumber(), BufferGetPage, ereport, errcode(), errhint(), errmsg(), ERROR, MAXALIGN, PageGetSpecialSize, PageIsNew, and RelationGetRelationName.

Referenced by gistBufferingFindCorrectParent(), gistdoinsert(), gistFindCorrectParent(), gistFindPath(), gistkillitems(), gistNewBuffer(), gistScanPage(), gistvacuum_delete_empty_pages(), and pgstat_gist_page().

772 {
773  Page page = BufferGetPage(buf);
774 
775  /*
776  * ReadBuffer verifies that every newly-read page passes
777  * PageHeaderIsValid, which means it either contains a reasonably sane
778  * page header or is all-zero. We have to defend against the all-zero
779  * case, however.
780  */
781  if (PageIsNew(page))
782  ereport(ERROR,
783  (errcode(ERRCODE_INDEX_CORRUPTED),
784  errmsg("index \"%s\" contains unexpected zero page at block %u",
787  errhint("Please REINDEX it.")));
788 
789  /*
790  * Additionally check that the special area looks sane.
791  */
792  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
793  ereport(ERROR,
794  (errcode(ERRCODE_INDEX_CORRUPTED),
795  errmsg("index \"%s\" contains corrupted page at block %u",
798  errhint("Please REINDEX it.")));
799 }
int errhint(const char *fmt,...)
Definition: elog.c:974
int errcode(int sqlerrcode)
Definition: elog.c:570
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:68
#define RelationGetRelationName(relation)
Definition: rel.h:453
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define MAXALIGN(LEN)
Definition: c.h:685
#define PageGetSpecialSize(page)
Definition: bufpage.h:300
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:784
Pointer Page
Definition: bufpage.h:78

◆ gistchoose()

OffsetNumber gistchoose ( Relation  r,
Page  p,
IndexTuple  it,
GISTSTATE giststate 
)

Definition at line 374 of file gistutil.c.

References Assert, FirstOffsetNumber, gistDeCompressAtt(), gistdentryinit(), GistPageIsLeaf, gistpenalty(), i, index_getattr, INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, GISTSTATE::leafTupdesc, MAX_RANDOM_VALUE, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, and random().

Referenced by gistdoinsert(), and gistProcessItup().

376 {
377  OffsetNumber result;
378  OffsetNumber maxoff;
379  OffsetNumber i;
380  float best_penalty[INDEX_MAX_KEYS];
381  GISTENTRY entry,
382  identry[INDEX_MAX_KEYS];
383  bool isnull[INDEX_MAX_KEYS];
384  int keep_current_best;
385 
386  Assert(!GistPageIsLeaf(p));
387 
388  gistDeCompressAtt(giststate, r,
389  it, NULL, (OffsetNumber) 0,
390  identry, isnull);
391 
392  /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
393  result = FirstOffsetNumber;
394 
395  /*
396  * The index may have multiple columns, and there's a penalty value for
397  * each column. The penalty associated with a column that appears earlier
398  * in the index definition is strictly more important than the penalty of
399  * a column that appears later in the index definition.
400  *
401  * best_penalty[j] is the best penalty we have seen so far for column j,
402  * or -1 when we haven't yet examined column j. Array entries to the
403  * right of the first -1 are undefined.
404  */
405  best_penalty[0] = -1;
406 
407  /*
408  * If we find a tuple that's exactly as good as the currently best one, we
409  * could use either one. When inserting a lot of tuples with the same or
410  * similar keys, it's preferable to descend down the same path when
411  * possible, as that's more cache-friendly. On the other hand, if all
412  * inserts land on the same leaf page after a split, we're never going to
413  * insert anything to the other half of the split, and will end up using
414  * only 50% of the available space. Distributing the inserts evenly would
415  * lead to better space usage, but that hurts cache-locality during
416  * insertion. To get the best of both worlds, when we find a tuple that's
417  * exactly as good as the previous best, choose randomly whether to stick
418  * to the old best, or use the new one. Once we decide to stick to the
419  * old best, we keep sticking to it for any subsequent equally good tuples
420  * we might find. This favors tuples with low offsets, but still allows
421  * some inserts to go to other equally-good subtrees.
422  *
423  * keep_current_best is -1 if we haven't yet had to make a random choice
424  * whether to keep the current best tuple. If we have done so, and
425  * decided to keep it, keep_current_best is 1; if we've decided to
426  * replace, keep_current_best is 0. (This state will be reset to -1 as
427  * soon as we've made the replacement, but sometimes we make the choice in
428  * advance of actually finding a replacement best tuple.)
429  */
430  keep_current_best = -1;
431 
432  /*
433  * Loop over tuples on page.
434  */
435  maxoff = PageGetMaxOffsetNumber(p);
436  Assert(maxoff >= FirstOffsetNumber);
437 
438  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
439  {
440  IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
441  bool zero_penalty;
442  int j;
443 
444  zero_penalty = true;
445 
446  /* Loop over index attributes. */
447  for (j = 0; j < IndexRelationGetNumberOfKeyAttributes(r); j++)
448  {
449  Datum datum;
450  float usize;
451  bool IsNull;
452 
453  /* Compute penalty for this column. */
454  datum = index_getattr(itup, j + 1, giststate->leafTupdesc,
455  &IsNull);
456  gistdentryinit(giststate, j, &entry, datum, r, p, i,
457  false, IsNull);
458  usize = gistpenalty(giststate, j, &entry, IsNull,
459  &identry[j], isnull[j]);
460  if (usize > 0)
461  zero_penalty = false;
462 
463  if (best_penalty[j] < 0 || usize < best_penalty[j])
464  {
465  /*
466  * New best penalty for column. Tentatively select this tuple
467  * as the target, and record the best penalty. Then reset the
468  * next column's penalty to "unknown" (and indirectly, the
469  * same for all the ones to its right). This will force us to
470  * adopt this tuple's penalty values as the best for all the
471  * remaining columns during subsequent loop iterations.
472  */
473  result = i;
474  best_penalty[j] = usize;
475 
477  best_penalty[j + 1] = -1;
478 
479  /* we have new best, so reset keep-it decision */
480  keep_current_best = -1;
481  }
482  else if (best_penalty[j] == usize)
483  {
484  /*
485  * The current tuple is exactly as good for this column as the
486  * best tuple seen so far. The next iteration of this loop
487  * will compare the next column.
488  */
489  }
490  else
491  {
492  /*
493  * The current tuple is worse for this column than the best
494  * tuple seen so far. Skip the remaining columns and move on
495  * to the next tuple, if any.
496  */
497  zero_penalty = false; /* so outer loop won't exit */
498  break;
499  }
500  }
501 
502  /*
503  * If we looped past the last column, and did not update "result",
504  * then this tuple is exactly as good as the prior best tuple.
505  */
506  if (j == IndexRelationGetNumberOfKeyAttributes(r) && result != i)
507  {
508  if (keep_current_best == -1)
509  {
510  /* we didn't make the random choice yet for this old best */
511  keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
512  }
513  if (keep_current_best == 0)
514  {
515  /* we choose to use the new tuple */
516  result = i;
517  /* choose again if there are even more exactly-as-good ones */
518  keep_current_best = -1;
519  }
520  }
521 
522  /*
523  * If we find a tuple with zero penalty for all columns, and we've
524  * decided we don't want to search for another tuple with equal
525  * penalty, there's no need to examine remaining tuples; just break
526  * out of the loop and return it.
527  */
528  if (zero_penalty)
529  {
530  if (keep_current_best == -1)
531  {
532  /* we didn't make the random choice yet for this old best */
533  keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
534  }
535  if (keep_current_best == 1)
536  break;
537  }
538  }
539 
540  return result;
541 }
long random(void)
Definition: random.c:22
void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
Definition: gistutil.c:296
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
Definition: gistutil.c:716
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:547
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
#define MAX_RANDOM_VALUE
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:438
TupleDesc leafTupdesc
Definition: gist_private.h:80
#define GistPageIsLeaf(page)
Definition: gist.h:140
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:732
#define INDEX_MAX_KEYS
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ gistDeCompressAtt()

void gistDeCompressAtt ( GISTSTATE giststate,
Relation  r,
IndexTuple  tuple,
Page  p,
OffsetNumber  o,
GISTENTRY attdata,
bool isnull 
)

Definition at line 296 of file gistutil.c.

References gistdentryinit(), i, index_getattr, IndexRelationGetNumberOfKeyAttributes, and GISTSTATE::leafTupdesc.

Referenced by gistchoose(), gistgetadjusted(), gistRelocateBuildBuffersOnSplit(), and placeOne().

298 {
299  int i;
300 
301  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
302  {
303  Datum datum;
304 
305  datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
306  gistdentryinit(giststate, i, &attdata[i],
307  datum, r, p, o,
308  false, isnull[i]);
309  }
310 }
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:547
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:438
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
int i

◆ gistdentryinit()

void gistdentryinit ( GISTSTATE giststate,
int  nkey,
GISTENTRY e,
Datum  k,
Relation  r,
Page  pg,
OffsetNumber  o,
bool  l,
bool  isNull 
)

Definition at line 547 of file gistutil.c.

References DatumGetPointer, GISTSTATE::decompressFn, FmgrInfo::fn_oid, FunctionCall1Coll(), gistentryinit, GISTENTRY::key, GISTENTRY::leafkey, GISTENTRY::offset, OidIsValid, GISTENTRY::page, PointerGetDatum, GISTENTRY::rel, and GISTSTATE::supportCollation.

Referenced by gistchoose(), gistDeCompressAtt(), gistindex_keytest(), gistMakeUnionItVec(), and gistSplitByKey().

550 {
551  if (!isNull)
552  {
553  GISTENTRY *dep;
554 
555  gistentryinit(*e, k, r, pg, o, l);
556 
557  /* there may not be a decompress function in opclass */
558  if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
559  return;
560 
561  dep = (GISTENTRY *)
563  giststate->supportCollation[nkey],
564  PointerGetDatum(e)));
565  /* decompressFn may just return the given pointer */
566  if (dep != e)
567  gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
568  dep->leafkey);
569  }
570  else
571  gistentryinit(*e, (Datum) 0, r, pg, o, l);
572 }
Relation rel
Definition: gist.h:132
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
#define OidIsValid(objectId)
Definition: c.h:638
Page page
Definition: gist.h:133
Datum key
Definition: gist.h:131
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
bool leafkey
Definition: gist.h:135
uintptr_t Datum
Definition: postgres.h:367
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1130
Oid fn_oid
Definition: fmgr.h:59
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:215
#define DatumGetPointer(X)
Definition: postgres.h:549
OffsetNumber offset
Definition: gist.h:134

◆ gistdoinsert()

void gistdoinsert ( Relation  r,
IndexTuple  itup,
Size  freespace,
GISTSTATE giststate,
Relation  heapRel,
bool  is_build 
)

Definition at line 623 of file gist.c.

References Assert, GISTInsertStack::blkno, GISTInsertStack::buffer, BufferGetLSNAtomic(), BufferGetPage, GISTInsertStack::downlinkoffnum, ereport, errdetail(), errhint(), errmsg(), ERROR, GISTInsertState::freespace, GIST_EXCLUSIVE, GIST_ROOT_BLKNO, GIST_SHARE, GIST_UNLOCK, gistcheckpage(), gistchoose(), gistfixsplit(), GistFollowRight, gistgetadjusted(), gistinserttuple(), GistPageGetNSN, GistPageIsDeleted, GistPageIsLeaf, GistTupleIsInvalid, GISTInsertState::heapRel, InvalidOffsetNumber, GISTInsertState::is_build, ItemPointerGetBlockNumber, LockBuffer(), GISTInsertStack::lsn, GISTInsertStack::page, PageGetItem, PageGetItemId, PageGetLSN, palloc0(), GISTInsertStack::parent, GISTInsertState::r, ReadBuffer(), RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), GISTInsertStack::retry_from_parent, GISTInsertState::stack, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogRecPtrIsInvalid.

Referenced by gistBuildCallback(), and gistinsert().

625 {
626  ItemId iid;
627  IndexTuple idxtuple;
628  GISTInsertStack firststack;
629  GISTInsertStack *stack;
631  bool xlocked = false;
632 
633  memset(&state, 0, sizeof(GISTInsertState));
634  state.freespace = freespace;
635  state.r = r;
636  state.heapRel = heapRel;
637  state.is_build = is_build;
638 
639  /* Start from the root */
640  firststack.blkno = GIST_ROOT_BLKNO;
641  firststack.lsn = 0;
642  firststack.retry_from_parent = false;
643  firststack.parent = NULL;
644  firststack.downlinkoffnum = InvalidOffsetNumber;
645  state.stack = stack = &firststack;
646 
647  /*
648  * Walk down along the path of smallest penalty, updating the parent
649  * pointers with the key we're inserting as we go. If we crash in the
650  * middle, the tree is consistent, although the possible parent updates
651  * were a waste.
652  */
653  for (;;)
654  {
655  /*
656  * If we split an internal page while descending the tree, we have to
657  * retry at the parent. (Normally, the LSN-NSN interlock below would
658  * also catch this and cause us to retry. But LSNs are not updated
659  * during index build.)
660  */
661  while (stack->retry_from_parent)
662  {
663  if (xlocked)
664  LockBuffer(stack->buffer, GIST_UNLOCK);
665  xlocked = false;
666  ReleaseBuffer(stack->buffer);
667  state.stack = stack = stack->parent;
668  }
669 
670  if (XLogRecPtrIsInvalid(stack->lsn))
671  stack->buffer = ReadBuffer(state.r, stack->blkno);
672 
673  /*
674  * Be optimistic and grab shared lock first. Swap it for an exclusive
675  * lock later if we need to update the page.
676  */
677  if (!xlocked)
678  {
679  LockBuffer(stack->buffer, GIST_SHARE);
680  gistcheckpage(state.r, stack->buffer);
681  }
682 
683  stack->page = (Page) BufferGetPage(stack->buffer);
684  stack->lsn = xlocked ?
685  PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
686  Assert(!RelationNeedsWAL(state.r) || !XLogRecPtrIsInvalid(stack->lsn));
687 
688  /*
689  * If this page was split but the downlink was never inserted to the
690  * parent because the inserting backend crashed before doing that, fix
691  * that now.
692  */
693  if (GistFollowRight(stack->page))
694  {
695  if (!xlocked)
696  {
697  LockBuffer(stack->buffer, GIST_UNLOCK);
699  xlocked = true;
700  /* someone might've completed the split when we unlocked */
701  if (!GistFollowRight(stack->page))
702  continue;
703  }
704  gistfixsplit(&state, giststate);
705 
706  UnlockReleaseBuffer(stack->buffer);
707  xlocked = false;
708  state.stack = stack = stack->parent;
709  continue;
710  }
711 
712  if ((stack->blkno != GIST_ROOT_BLKNO &&
713  stack->parent->lsn < GistPageGetNSN(stack->page)) ||
714  GistPageIsDeleted(stack->page))
715  {
716  /*
717  * Concurrent split or page deletion detected. There's no
718  * guarantee that the downlink for this page is consistent with
719  * the tuple we're inserting anymore, so go back to parent and
720  * rechoose the best child.
721  */
722  UnlockReleaseBuffer(stack->buffer);
723  xlocked = false;
724  state.stack = stack = stack->parent;
725  continue;
726  }
727 
728  if (!GistPageIsLeaf(stack->page))
729  {
730  /*
731  * This is an internal page so continue to walk down the tree.
732  * Find the child node that has the minimum insertion penalty.
733  */
734  BlockNumber childblkno;
735  IndexTuple newtup;
736  GISTInsertStack *item;
737  OffsetNumber downlinkoffnum;
738 
739  downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
740  iid = PageGetItemId(stack->page, downlinkoffnum);
741  idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
742  childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
743 
744  /*
745  * Check that it's not a leftover invalid tuple from pre-9.1
746  */
747  if (GistTupleIsInvalid(idxtuple))
748  ereport(ERROR,
749  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
751  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
752  errhint("Please REINDEX it.")));
753 
754  /*
755  * Check that the key representing the target child node is
756  * consistent with the key we're inserting. Update it if it's not.
757  */
758  newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
759  if (newtup)
760  {
761  /*
762  * Swap shared lock for an exclusive one. Beware, the page may
763  * change while we unlock/lock the page...
764  */
765  if (!xlocked)
766  {
767  LockBuffer(stack->buffer, GIST_UNLOCK);
769  xlocked = true;
770  stack->page = (Page) BufferGetPage(stack->buffer);
771 
772  if (PageGetLSN(stack->page) != stack->lsn)
773  {
774  /* the page was changed while we unlocked it, retry */
775  continue;
776  }
777  }
778 
779  /*
780  * Update the tuple.
781  *
782  * We still hold the lock after gistinserttuple(), but it
783  * might have to split the page to make the updated tuple fit.
784  * In that case the updated tuple might migrate to the other
785  * half of the split, so we have to go back to the parent and
786  * descend back to the half that's a better fit for the new
787  * tuple.
788  */
789  if (gistinserttuple(&state, stack, giststate, newtup,
790  downlinkoffnum))
791  {
792  /*
793  * If this was a root split, the root page continues to be
794  * the parent and the updated tuple went to one of the
795  * child pages, so we just need to retry from the root
796  * page.
797  */
798  if (stack->blkno != GIST_ROOT_BLKNO)
799  {
800  UnlockReleaseBuffer(stack->buffer);
801  xlocked = false;
802  state.stack = stack = stack->parent;
803  }
804  continue;
805  }
806  }
807  LockBuffer(stack->buffer, GIST_UNLOCK);
808  xlocked = false;
809 
810  /* descend to the chosen child */
811  item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
812  item->blkno = childblkno;
813  item->parent = stack;
814  item->downlinkoffnum = downlinkoffnum;
815  state.stack = stack = item;
816  }
817  else
818  {
819  /*
820  * Leaf page. Insert the new key. We've already updated all the
821  * parents on the way down, but we might have to split the page if
822  * it doesn't fit. gistinserttuple() will take care of that.
823  */
824 
825  /*
826  * Swap shared lock for an exclusive one. Be careful, the page may
827  * change while we unlock/lock the page...
828  */
829  if (!xlocked)
830  {
831  LockBuffer(stack->buffer, GIST_UNLOCK);
833  xlocked = true;
834  stack->page = (Page) BufferGetPage(stack->buffer);
835  stack->lsn = PageGetLSN(stack->page);
836 
837  if (stack->blkno == GIST_ROOT_BLKNO)
838  {
839  /*
840  * the only page that can become inner instead of leaf is
841  * the root page, so for root we should recheck it
842  */
843  if (!GistPageIsLeaf(stack->page))
844  {
845  /*
846  * very rare situation: during unlock/lock index with
847  * number of pages = 1 was increased
848  */
849  LockBuffer(stack->buffer, GIST_UNLOCK);
850  xlocked = false;
851  continue;
852  }
853 
854  /*
855  * we don't need to check root split, because checking
856  * leaf/inner is enough to recognize split for root
857  */
858  }
859  else if ((GistFollowRight(stack->page) ||
860  stack->parent->lsn < GistPageGetNSN(stack->page)) &&
861  GistPageIsDeleted(stack->page))
862  {
863  /*
864  * The page was split or deleted while we momentarily
865  * unlocked the page. Go back to parent.
866  */
867  UnlockReleaseBuffer(stack->buffer);
868  xlocked = false;
869  state.stack = stack = stack->parent;
870  continue;
871  }
872  }
873 
874  /* now state.stack->(page, buffer and blkno) points to leaf page */
875 
876  gistinserttuple(&state, stack, giststate, itup,
878  LockBuffer(stack->buffer, GIST_UNLOCK);
879 
880  /* Release any pins we might still hold before exiting */
881  for (; stack; stack = stack->parent)
882  ReleaseBuffer(stack->buffer);
883  break;
884  }
885  }
886 }
#define GistFollowRight(page)
Definition: gist.h:153
BlockNumber blkno
Definition: gist_private.h:210
#define GistPageGetNSN(page)
Definition: gist.h:157
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1219
#define GistPageIsDeleted(page)
Definition: gist.h:143
int errhint(const char *fmt,...)
Definition: elog.c:974
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1160
Relation heapRel
Definition: gist_private.h:254
ItemPointerData t_tid
Definition: itup.h:37
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:288
#define GIST_UNLOCK
Definition: gist_private.h:44
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3365
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:316
uint16 OffsetNumber
Definition: off.h:24
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define ERROR
Definition: elog.h:43
GISTInsertStack * stack
Definition: gist_private.h:258
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:2876
IndexTupleData * IndexTuple
Definition: itup.h:53
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define RelationGetRelationName(relation)
Definition: rel.h:453
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:374
#define GistPageIsLeaf(page)
Definition: gist.h:140
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
OffsetNumber downlinkoffnum
Definition: gist_private.h:228
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void * palloc0(Size size)
Definition: mcxt.c:980
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3602
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:732
Definition: regguts.h:298
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:771
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define GIST_SHARE
Definition: gist_private.h:42
#define RelationNeedsWAL(relation)
Definition: rel.h:521
#define PageGetLSN(page)
Definition: bufpage.h:366
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define GIST_EXCLUSIVE
Definition: gist_private.h:43
struct GISTInsertStack * parent
Definition: gist_private.h:231
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 95 of file gistutil.c.

References FirstOffsetNumber, i, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, and palloc().

Referenced by gistplacetopage().

96 {
98  maxoff;
99  IndexTuple *itvec;
100 
101  maxoff = PageGetMaxOffsetNumber(page);
102  *len = maxoff;
103  itvec = palloc(sizeof(IndexTuple) * maxoff);
104  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
105  itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
106 
107  return itvec;
108 }
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
void * palloc(Size size)
Definition: mcxt.c:949
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ gistFetchTuple()

HeapTuple gistFetchTuple ( GISTSTATE giststate,
Relation  r,
IndexTuple  tuple 
)

Definition at line 659 of file gistutil.c.

References GISTSTATE::compressFn, fetchatt, GISTSTATE::fetchFn, GISTSTATE::fetchTupdesc, FmgrInfo::fn_oid, gistFetchAtt(), heap_form_tuple(), i, index_getattr, INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidOid, GISTSTATE::leafTupdesc, MemoryContextSwitchTo(), TupleDescData::natts, RelationData::rd_att, and GISTSTATE::tempCxt.

Referenced by gistScanPage().

660 {
661  MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
663  bool isnull[INDEX_MAX_KEYS];
664  int i;
665 
666  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
667  {
668  Datum datum;
669 
670  datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
671 
672  if (giststate->fetchFn[i].fn_oid != InvalidOid)
673  {
674  if (!isnull[i])
675  fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
676  else
677  fetchatt[i] = (Datum) 0;
678  }
679  else if (giststate->compressFn[i].fn_oid == InvalidOid)
680  {
681  /*
682  * If opclass does not provide compress method that could change
683  * original value, att is necessarily stored in original form.
684  */
685  if (!isnull[i])
686  fetchatt[i] = datum;
687  else
688  fetchatt[i] = (Datum) 0;
689  }
690  else
691  {
692  /*
693  * Index-only scans not supported for this column. Since the
694  * planner chose an index-only scan anyway, it is not interested
695  * in this column, and we can replace it with a NULL.
696  */
697  isnull[i] = true;
698  fetchatt[i] = (Datum) 0;
699  }
700  }
701 
702  /*
703  * Get each included attribute.
704  */
705  for (; i < r->rd_att->natts; i++)
706  {
707  fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
708  &isnull[i]);
709  }
710  MemoryContextSwitchTo(oldcxt);
711 
712  return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
713 }
static Datum gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
Definition: gistutil.c:638
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:94
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define fetchatt(A, T)
Definition: tupmacs.h:39
MemoryContext tempCxt
Definition: gist_private.h:78
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:438
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc rd_att
Definition: rel.h:84
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
TupleDesc fetchTupdesc
Definition: gist_private.h:83
#define INDEX_MAX_KEYS
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
int i

◆ gistfillbuffer()

void gistfillbuffer ( Page  page,
IndexTuple itup,
int  len,
OffsetNumber  off 
)

Definition at line 34 of file gistutil.c.

References elog, ERROR, FirstOffsetNumber, i, IndexTupleSize, InvalidOffsetNumber, OffsetNumberNext, PageAddItem, PageGetMaxOffsetNumber, and PageIsEmpty.

Referenced by gistplacetopage(), and gistRedoPageSplitRecord().

35 {
37  int i;
38 
39  if (off == InvalidOffsetNumber)
40  off = (PageIsEmpty(page)) ? FirstOffsetNumber :
42 
43  for (i = 0; i < len; i++)
44  {
45  Size sz = IndexTupleSize(itup[i]);
46 
47  l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
48  if (l == InvalidOffsetNumber)
49  elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
50  i, len, (int) sz);
51  off++;
52  }
53 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
Pointer Item
Definition: item.h:17
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:416
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define ERROR
Definition: elog.h:43
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
size_t Size
Definition: c.h:466
#define elog(elevel,...)
Definition: elog.h:226
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistfillitupvec()

IndexTupleData* gistfillitupvec ( IndexTuple vec,
int  veclen,
int *  memlen 
)

Definition at line 127 of file gistutil.c.

References i, IndexTupleSize, and palloc().

Referenced by gistplacetopage(), and gistSplit().

128 {
129  char *ptr,
130  *ret;
131  int i;
132 
133  *memlen = 0;
134 
135  for (i = 0; i < veclen; i++)
136  *memlen += IndexTupleSize(vec[i]);
137 
138  ptr = ret = palloc(*memlen);
139 
140  for (i = 0; i < veclen; i++)
141  {
142  memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
143  ptr += IndexTupleSize(vec[i]);
144  }
145 
146  return (IndexTupleData *) ret;
147 }
void * palloc(Size size)
Definition: mcxt.c:949
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 79 of file gistutil.c.

References GiSTPageSize, i, and IndexTupleSize.

Referenced by gistSplit().

80 {
81  int i;
82  Size size = 0;
83 
84  for (i = 0; i < len; i++)
85  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
86 
87  /* TODO: Consider fillfactor */
88  return (size <= GiSTPageSize);
89 }
struct ItemIdData ItemIdData
#define GiSTPageSize
Definition: gist_private.h:468
size_t Size
Definition: c.h:466
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistFormTuple()

IndexTuple gistFormTuple ( GISTSTATE giststate,
Relation  r,
Datum attdata,
bool isnull,
bool  isleaf 
)

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 512 of file gistbuildbuffers.c.

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

513 {
514  /* Close buffers file. */
515  BufFileClose(gfbb->pfile);
516 
517  /* All other things will be freed on memory context release */
518 }
void BufFileClose(BufFile *file)
Definition: buffile.c:391

◆ gistgetadjusted()

IndexTuple gistgetadjusted ( Relation  r,
IndexTuple  oldtup,
IndexTuple  addtup,
GISTSTATE giststate 
)

Definition at line 316 of file gistutil.c.

References gistDeCompressAtt(), gistFormTuple(), gistKeyIsEQ(), gistMakeUnionKey(), i, INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, sort-test::key, and IndexTupleData::t_tid.

Referenced by gistdoinsert(), gistformdownlink(), gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

317 {
318  bool neednew = false;
319  GISTENTRY oldentries[INDEX_MAX_KEYS],
320  addentries[INDEX_MAX_KEYS];
321  bool oldisnull[INDEX_MAX_KEYS],
322  addisnull[INDEX_MAX_KEYS];
323  Datum attr[INDEX_MAX_KEYS];
324  bool isnull[INDEX_MAX_KEYS];
325  IndexTuple newtup = NULL;
326  int i;
327 
328  gistDeCompressAtt(giststate, r, oldtup, NULL,
329  (OffsetNumber) 0, oldentries, oldisnull);
330 
331  gistDeCompressAtt(giststate, r, addtup, NULL,
332  (OffsetNumber) 0, addentries, addisnull);
333 
334  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
335  {
336  gistMakeUnionKey(giststate, i,
337  oldentries + i, oldisnull[i],
338  addentries + i, addisnull[i],
339  attr + i, isnull + i);
340 
341  if (neednew)
342  /* we already need new key, so we can skip check */
343  continue;
344 
345  if (isnull[i])
346  /* union of key may be NULL if and only if both keys are NULL */
347  continue;
348 
349  if (!addisnull[i])
350  {
351  if (oldisnull[i] ||
352  !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
353  neednew = true;
354  }
355  }
356 
357  if (neednew)
358  {
359  /* need to update key */
360  newtup = gistFormTuple(giststate, r, attr, isnull, false);
361  newtup->t_tid = oldtup->t_tid;
362  }
363 
364  return newtup;
365 }
void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
Definition: gistutil.c:296
ItemPointerData t_tid
Definition: itup.h:37
bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
Definition: gistutil.c:281
uint16 OffsetNumber
Definition: off.h:24
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:438
uintptr_t Datum
Definition: postgres.h:367
void gistMakeUnionKey(GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
Definition: gistutil.c:233
#define INDEX_MAX_KEYS
int i
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:575

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

References CHECK_FOR_INTERRUPTS, GISTScanOpaqueData::curPageData, GISTSearchItem::distances, getNextGISTSearchItem(), GIST_ROOT_BLKNO, gistScanPage(), IndexScanDescData::indexRelation, MemoryContextReset(), GISTScanOpaqueData::nPageData, IndexScanDescData::opaque, GISTScanOpaqueData::pageDataCxt, pfree(), pgstat_count_index_scan, GISTScanOpaqueData::qual_ok, and IndexScanDescData::xs_hitup.

Referenced by gisthandler().

746 {
747  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
748  int64 ntids = 0;
749  GISTSearchItem fakeItem;
750 
751  if (!so->qual_ok)
752  return 0;
753 
755 
756  /* Begin the scan by processing the root page */
757  so->curPageData = so->nPageData = 0;
758  scan->xs_hitup = NULL;
759  if (so->pageDataCxt)
761 
762  fakeItem.blkno = GIST_ROOT_BLKNO;
763  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
764  gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
765 
766  /*
767  * While scanning a leaf page, ItemPointers of matching heap tuples will
768  * be stored directly into tbm, so we don't need to deal with them here.
769  */
770  for (;;)
771  {
773 
774  if (!item)
775  break;
776 
778 
779  gistScanPage(scan, item, item->distances, tbm, &ntids);
780 
781  pfree(item);
782  }
783 
784  return ntids;
785 }
IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:142
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
MemoryContext pageDataCxt
Definition: gist_private.h:177
Relation indexRelation
Definition: relscan.h:103
void pfree(void *pointer)
Definition: mcxt.c:1056
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1373
OffsetNumber nPageData
Definition: gist_private.h:175
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:329
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:540
OffsetNumber curPageData
Definition: gist_private.h:176
XLogRecPtr GistNSN
Definition: gist.h:51
HeapTuple xs_hitup
Definition: relscan.h:129
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1027 of file gistutil.c.

References Assert, FirstNormalUnloggedLSN, GetFakeLSNForUnloggedRel(), and RelationData::rd_rel.

Referenced by gistdeletepage(), gistplacetopage(), gistprunepage(), gistvacuumpage(), and gistvacuumscan().

1028 {
1029  static XLogRecPtr counter = FirstNormalUnloggedLSN;
1030 
1031  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1032  {
1033  /*
1034  * Temporary relations are only accessible in our session, so a simple
1035  * backend-local counter will do.
1036  */
1037  return counter++;
1038  }
1039  else
1040  {
1041  /*
1042  * Unlogged relations are accessible from other backends, and survive
1043  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1044  */
1045  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1046  return GetFakeLSNForUnloggedRel();
1047  }
1048 }
Form_pg_class rd_rel
Definition: rel.h:83
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4838
#define FirstNormalUnloggedLSN
Definition: xlogdefs.h:36
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:732

◆ gistGetNodeBuffer()

GISTNodeBuffer* gistGetNodeBuffer ( GISTBuildBuffers gfbb,
GISTSTATE giststate,
BlockNumber  blkno,
int  level 
)

Definition at line 118 of file gistbuildbuffers.c.

References GISTNodeBuffer::blocksCount, GISTBuildBuffers::buffersOnLevels, GISTBuildBuffers::buffersOnLevelsLen, GISTBuildBuffers::context, HASH_ENTER, hash_search(), i, InvalidBlockNumber, GISTNodeBuffer::isTemp, lcons(), GISTNodeBuffer::level, MemoryContextSwitchTo(), NIL, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, GISTNodeBuffer::queuedForEmptying, and repalloc().

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

120 {
121  GISTNodeBuffer *nodeBuffer;
122  bool found;
123 
124  /* Find node buffer in hash table */
125  nodeBuffer = (GISTNodeBuffer *) hash_search(gfbb->nodeBuffersTab,
126  (const void *) &nodeBlocknum,
127  HASH_ENTER,
128  &found);
129  if (!found)
130  {
131  /*
132  * Node buffer wasn't found. Initialize the new buffer as empty.
133  */
135 
136  /* nodeBuffer->nodeBlocknum is the hash key and was filled in already */
137  nodeBuffer->blocksCount = 0;
138  nodeBuffer->pageBlocknum = InvalidBlockNumber;
139  nodeBuffer->pageBuffer = NULL;
140  nodeBuffer->queuedForEmptying = false;
141  nodeBuffer->isTemp = false;
142  nodeBuffer->level = level;
143 
144  /*
145  * Add this buffer to the list of buffers on this level. Enlarge
146  * buffersOnLevels array if needed.
147  */
148  if (level >= gfbb->buffersOnLevelsLen)
149  {
150  int i;
151 
152  gfbb->buffersOnLevels =
153  (List **) repalloc(gfbb->buffersOnLevels,
154  (level + 1) * sizeof(List *));
155 
156  /* initialize the enlarged portion */
157  for (i = gfbb->buffersOnLevelsLen; i <= level; i++)
158  gfbb->buffersOnLevels[i] = NIL;
159  gfbb->buffersOnLevelsLen = level + 1;
160  }
161 
162  /*
163  * Prepend the new buffer to the list of buffers on this level. It's
164  * not arbitrary that the new buffer is put to the beginning of the
165  * list: in the final emptying phase we loop through all buffers at
166  * each level, and flush them. If a page is split during the emptying,
167  * it's more efficient to flush the new splitted pages first, before
168  * moving on to pre-existing pages on the level. The buffers just
169  * created during the page split are likely still in cache, so
170  * flushing them immediately is more efficient than putting them to
171  * the end of the queue.
172  */
173  gfbb->buffersOnLevels[level] = lcons(nodeBuffer,
174  gfbb->buffersOnLevels[level]);
175 
176  MemoryContextSwitchTo(oldcxt);
177  }
178 
179  return nodeBuffer;
180 }
#define NIL
Definition: pg_list.h:65
BlockNumber pageBlocknum
Definition: gist_private.h:303
MemoryContext context
Definition: gist_private.h:341
List ** buffersOnLevels
Definition: gist_private.h:368
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
bool queuedForEmptying
Definition: gist_private.h:307
List * lcons(void *datum, List *list)
Definition: list.c:454
#define InvalidBlockNumber
Definition: block.h:33
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
int i
Definition: pg_list.h:50

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 614 of file gistget.c.

References GISTSearchItem::blkno, CHECK_FOR_INTERRUPTS, GISTScanOpaqueData::curBlkno, GISTScanOpaqueData::curPageData, GISTSearchItem::data, GISTSearchItem::distances, elog, ERROR, GISTScanOpaqueData::firstCall, ForwardScanDirection, getNextGISTSearchItem(), getNextNearest(), GIST_ROOT_BLKNO, gistkillitems(), gistScanPage(), GISTScanOpaqueData::giststate, GISTSearchHeapItem::heapPtr, IndexScanDescData::indexRelation, InvalidBlockNumber, IndexScanDescData::kill_prior_tuple, GISTScanOpaqueData::killedItems, MaxIndexTuplesPerPage, MemoryContextReset(), MemoryContextSwitchTo(), GISTScanOpaqueData::nPageData, IndexScanDescData::numberOfOrderBys, GISTScanOpaqueData::numKilled, GISTSearchHeapItem::offnum, IndexScanDescData::opaque, GISTScanOpaqueData::pageData, GISTScanOpaqueData::pageDataCxt, palloc(), GISTSearchItem::parentlsn, pfree(), pgstat_count_index_scan, GISTScanOpaqueData::qual_ok, GISTSearchHeapItem::recheck, GISTSearchHeapItem::recontup, GISTSTATE::scanCxt, IndexScanDescData::xs_heaptid, IndexScanDescData::xs_hitup, IndexScanDescData::xs_recheck, and IndexScanDescData::xs_want_itup.

Referenced by gisthandler().

615 {
616  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
617 
618  if (dir != ForwardScanDirection)
619  elog(ERROR, "GiST only supports forward scan direction");
620 
621  if (!so->qual_ok)
622  return false;
623 
624  if (so->firstCall)
625  {
626  /* Begin the scan by processing the root page */
627  GISTSearchItem fakeItem;
628 
630 
631  so->firstCall = false;
632  so->curPageData = so->nPageData = 0;
633  scan->xs_hitup = NULL;
634  if (so->pageDataCxt)
636 
637  fakeItem.blkno = GIST_ROOT_BLKNO;
638  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
639  gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
640  }
641 
642  if (scan->numberOfOrderBys > 0)
643  {
644  /* Must fetch tuples in strict distance order */
645  return getNextNearest(scan);
646  }
647  else
648  {
649  /* Fetch tuples index-page-at-a-time */
650  for (;;)
651  {
652  if (so->curPageData < so->nPageData)
653  {
654  if (scan->kill_prior_tuple && so->curPageData > 0)
655  {
656 
657  if (so->killedItems == NULL)
658  {
659  MemoryContext oldCxt =
661 
662  so->killedItems =
664  * sizeof(OffsetNumber));
665 
666  MemoryContextSwitchTo(oldCxt);
667  }
669  so->killedItems[so->numKilled++] =
670  so->pageData[so->curPageData - 1].offnum;
671  }
672  /* continuing to return tuples from a leaf page */
673  scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;
674  scan->xs_recheck = so->pageData[so->curPageData].recheck;
675 
676  /* in an index-only scan, also return the reconstructed tuple */
677  if (scan->xs_want_itup)
678  scan->xs_hitup = so->pageData[so->curPageData].recontup;
679 
680  so->curPageData++;
681 
682  return true;
683  }
684 
685  /*
686  * Check the last returned tuple and add it to killedItems if
687  * necessary
688  */
689  if (scan->kill_prior_tuple
690  && so->curPageData > 0
691  && so->curPageData == so->nPageData)
692  {
693 
694  if (so->killedItems == NULL)
695  {
696  MemoryContext oldCxt =
698 
699  so->killedItems =
701  * sizeof(OffsetNumber));
702 
703  MemoryContextSwitchTo(oldCxt);
704  }
706  so->killedItems[so->numKilled++] =
707  so->pageData[so->curPageData - 1].offnum;
708  }
709  /* find and process the next index page */
710  do
711  {
712  GISTSearchItem *item;
713 
714  if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
715  gistkillitems(scan);
716 
717  item = getNextGISTSearchItem(so);
718 
719  if (!item)
720  return false;
721 
723 
724  /* save current item BlockNumber for next gistkillitems() call */
725  so->curBlkno = item->blkno;
726 
727  /*
728  * While scanning a leaf page, ItemPointers of matching heap
729  * tuples are stored in so->pageData. If there are any on
730  * this page, we fall out of the inner "do" and loop around to
731  * return them.
732  */
733  gistScanPage(scan, item, item->distances, NULL, NULL);
734 
735  pfree(item);
736  } while (so->nPageData == 0);
737  }
738  }
739 }
BlockNumber blkno
Definition: gist_private.h:133
IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:142
OffsetNumber * killedItems
Definition: gist_private.h:168
BlockNumber curBlkno
Definition: gist_private.h:170
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
MemoryContext pageDataCxt
Definition: gist_private.h:177
Relation indexRelation
Definition: relscan.h:103
uint16 OffsetNumber
Definition: off.h:24
GISTSTATE * giststate
Definition: gist_private.h:156
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ERROR
Definition: elog.h:43
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
ItemPointerData xs_heaptid
Definition: relscan.h:132
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1373
OffsetNumber nPageData
Definition: gist_private.h:175
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:329
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:540
union GISTSearchItem::@43 data
ItemPointerData heapPtr
Definition: gist_private.h:120
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:562
GISTSearchHeapItem pageData[BLCKSZ/sizeof(IndexTupleData)]
Definition: gist_private.h:174
#define InvalidBlockNumber
Definition: block.h:33
OffsetNumber curPageData
Definition: gist_private.h:176
XLogRecPtr GistNSN
Definition: gist.h:51
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:39
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:949
HeapTuple xs_hitup
Definition: relscan.h:129
OffsetNumber offnum
Definition: gist_private.h:125
#define elog(elevel,...)
Definition: elog.h:226
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
bool kill_prior_tuple
Definition: relscan.h:113
MemoryContext scanCxt
Definition: gist_private.h:77
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
int numberOfOrderBys
Definition: relscan.h:106
GistNSN parentlsn
Definition: gist_private.h:136

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 749 of file gistutil.c.

References BufferGetPage, BufferGetPageSize, GISTPageOpaqueData::flags, GISTPageOpaqueData::gist_page_id, GIST_PAGE_ID, GistPageGetOpaque, InvalidBlockNumber, PageInit(), and GISTPageOpaqueData::rightlink.

Referenced by gistbuild(), gistbuildempty(), gistplacetopage(), and gistRedoPageSplitRecord().

750 {
751  GISTPageOpaque opaque;
752  Page page;
753  Size pageSize;
754 
755  pageSize = BufferGetPageSize(b);
756  page = BufferGetPage(b);
757  PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
758 
759  opaque = GistPageGetOpaque(page);
760  /* page was already zeroed by PageInit, so this is not needed: */
761  /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
762  opaque->rightlink = InvalidBlockNumber;
763  opaque->flags = f;
764  opaque->gist_page_id = GIST_PAGE_ID;
765 }
uint16 gist_page_id
Definition: gist.h:71
uint16 flags
Definition: gist.h:70
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:146
#define GistPageGetOpaque(page)
Definition: gist.h:138
size_t Size
Definition: c.h:466
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber rightlink
Definition: gist.h:69
#define GIST_PAGE_ID
Definition: gist.h:82
Pointer Page
Definition: bufpage.h:78
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ gistInitBuildBuffers()

GISTBuildBuffers* gistInitBuildBuffers ( int  pagesPerBuffer,
int  levelStep,
int  maxLevel 
)

Definition at line 48 of file gistbuildbuffers.c.

References GISTBuildBuffers::bufferEmptyingQueue, GISTBuildBuffers::buffersOnLevels, GISTBuildBuffers::buffersOnLevelsLen, BufFileCreateTemp(), GISTBuildBuffers::context, CurrentMemoryContext, HASHCTL::entrysize, GISTBuildBuffers::freeBlocks, GISTBuildBuffers::freeBlocksLen, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, GISTBuildBuffers::levelStep, GISTBuildBuffers::loadedBuffers, GISTBuildBuffers::loadedBuffersCount, GISTBuildBuffers::loadedBuffersLen, GISTBuildBuffers::nFileBlocks, GISTBuildBuffers::nFreeBlocks, NIL, GISTBuildBuffers::nodeBuffersTab, GISTBuildBuffers::pagesPerBuffer, palloc(), GISTBuildBuffers::pfile, and GISTBuildBuffers::rootlevel.

Referenced by gistInitBuffering().

49 {
50  GISTBuildBuffers *gfbb;
51  HASHCTL hashCtl;
52 
53  gfbb = palloc(sizeof(GISTBuildBuffers));
54  gfbb->pagesPerBuffer = pagesPerBuffer;
55  gfbb->levelStep = levelStep;
56 
57  /*
58  * Create a temporary file to hold buffer pages that are swapped out of
59  * memory.
60  */
61  gfbb->pfile = BufFileCreateTemp(false);
62  gfbb->nFileBlocks = 0;
63 
64  /* Initialize free page management. */
65  gfbb->nFreeBlocks = 0;
66  gfbb->freeBlocksLen = 32;
67  gfbb->freeBlocks = (long *) palloc(gfbb->freeBlocksLen * sizeof(long));
68 
69  /*
70  * Current memory context will be used for all in-memory data structures
71  * of buffers which are persistent during buffering build.
72  */
74 
75  /*
76  * nodeBuffersTab hash is association between index blocks and it's
77  * buffers.
78  */
79  memset(&hashCtl, 0, sizeof(hashCtl));
80  hashCtl.keysize = sizeof(BlockNumber);
81  hashCtl.entrysize = sizeof(GISTNodeBuffer);
82  hashCtl.hcxt = CurrentMemoryContext;
83  gfbb->nodeBuffersTab = hash_create("gistbuildbuffers",
84  1024,
85  &hashCtl,
87 
88  gfbb->bufferEmptyingQueue = NIL;
89 
90  /*
91  * Per-level node buffers lists for final buffers emptying process. Node
92  * buffers are inserted here when they are created.
93  */
94  gfbb->buffersOnLevelsLen = 1;
95  gfbb->buffersOnLevels = (List **) palloc(sizeof(List *) *
96  gfbb->buffersOnLevelsLen);
97  gfbb->buffersOnLevels[0] = NIL;
98 
99  /*
100  * Block numbers of node buffers which last pages are currently loaded
101  * into main memory.
102  */
103  gfbb->loadedBuffersLen = 32;
105  sizeof(GISTNodeBuffer *));
106  gfbb->loadedBuffersCount = 0;
107 
108  gfbb->rootlevel = maxLevel;
109 
110  return gfbb;
111 }
#define NIL
Definition: pg_list.h:65
#define HASH_CONTEXT
Definition: hsearch.h:93
#define HASH_ELEM
Definition: hsearch.h:87
MemoryContext hcxt
Definition: hsearch.h:78
MemoryContext context
Definition: gist_private.h:341
List ** buffersOnLevels
Definition: gist_private.h:368
Size entrysize
Definition: hsearch.h:73
uint32 BlockNumber
Definition: block.h:31
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:184
List * bufferEmptyingQueue
Definition: gist_private.h:357
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define HASH_BLOBS
Definition: hsearch.h:88
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
Size keysize
Definition: hsearch.h:72
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
void * palloc(Size size)
Definition: mcxt.c:949
Definition: pg_list.h:50

◆ gistinsert()

bool gistinsert ( Relation  r,
Datum values,
bool isnull,
ItemPointer  ht_ctid,
Relation  heapRel,
IndexUniqueCheck  checkUnique,
struct IndexInfo indexInfo 
)

Definition at line 151 of file gist.c.

References createTempGistContext(), gistdoinsert(), gistFormTuple(), IndexInfo::ii_AmCache, IndexInfo::ii_Context, initGISTstate(), MemoryContextReset(), MemoryContextSwitchTo(), and GISTSTATE::tempCxt.

Referenced by gisthandler().

155 {
156  GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
157  IndexTuple itup;
158  MemoryContext oldCxt;
159 
160  /* Initialize GISTSTATE cache if first call in this statement */
161  if (giststate == NULL)
162  {
163  oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
164  giststate = initGISTstate(r);
165  giststate->tempCxt = createTempGistContext();
166  indexInfo->ii_AmCache = (void *) giststate;
167  MemoryContextSwitchTo(oldCxt);
168  }
169 
170  oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
171 
172  itup = gistFormTuple(giststate, r,
173  values, isnull, true /* size is currently bogus */ );
174  itup->t_tid = *ht_ctid;
175 
176  gistdoinsert(r, itup, 0, giststate, heapRel, false);
177 
178  /* cleanup */
179  MemoryContextSwitchTo(oldCxt);
180  MemoryContextReset(giststate->tempCxt);
181 
182  return false;
183 }
MemoryContext ii_Context
Definition: execnodes.h:177
MemoryContext createTempGistContext(void)
Definition: gist.c:114
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate, Relation heapRel, bool is_build)
Definition: gist.c:623
MemoryContext tempCxt
Definition: gist_private.h:78
void * ii_AmCache
Definition: execnodes.h:176
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1486
static Datum values[MAXATTR]
Definition: bootstrap.c:167
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:575

◆ gistjoinvector()

IndexTuple* gistjoinvector ( IndexTuple itvec,
int *  len,
IndexTuple additvec,
int  addlen 
)

Definition at line 114 of file gistutil.c.

References memmove, and repalloc().

Referenced by gistplacetopage().

115 {
116  itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen));
117  memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
118  *len += addlen;
119  return itvec;
120 }
#define memmove(d, s, c)
Definition: c.h:1238
IndexTupleData * IndexTuple
Definition: itup.h:53
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069

◆ gistKeyIsEQ()

bool gistKeyIsEQ ( GISTSTATE giststate,
int  attno,
Datum  a,
Datum  b 
)

Definition at line 281 of file gistutil.c.

References GISTSTATE::equalFn, FunctionCall3Coll(), PointerGetDatum, and GISTSTATE::supportCollation.

Referenced by gistgetadjusted(), and gistUserPicksplit().

282 {
283  bool result;
284 
285  FunctionCall3Coll(&giststate->equalFn[attno],
286  giststate->supportCollation[attno],
287  a, b,
288  PointerGetDatum(&result));
289  return result;
290 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1172

◆ gistMakeUnionItVec()

void gistMakeUnionItVec ( GISTSTATE giststate,
IndexTuple itvec,
int  len,
Datum attr,
bool isnull 
)

Definition at line 155 of file gistutil.c.

References FunctionCall2Coll(), GEVHDRSZ, gistdentryinit(), i, index_getattr, GISTSTATE::leafTupdesc, GistEntryVector::n, TupleDescData::natts, GISTSTATE::nonLeafTupdesc, palloc(), PointerGetDatum, GISTSTATE::supportCollation, GISTSTATE::unionFn, and GistEntryVector::vector.

Referenced by gistunion(), and gistunionsubkeyvec().

157 {
158  int i;
159  GistEntryVector *evec;
160  int attrsize;
161 
162  evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
163 
164  for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
165  {
166  int j;
167 
168  /* Collect non-null datums for this column */
169  evec->n = 0;
170  for (j = 0; j < len; j++)
171  {
172  Datum datum;
173  bool IsNull;
174 
175  datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
176  &IsNull);
177  if (IsNull)
178  continue;
179 
180  gistdentryinit(giststate, i,
181  evec->vector + evec->n,
182  datum,
183  NULL, NULL, (OffsetNumber) 0,
184  false, IsNull);
185  evec->n++;
186  }
187 
188  /* If this column was all NULLs, the union is NULL */
189  if (evec->n == 0)
190  {
191  attr[i] = (Datum) 0;
192  isnull[i] = true;
193  }
194  else
195  {
196  if (evec->n == 1)
197  {
198  /* unionFn may expect at least two inputs */
199  evec->n = 2;
200  evec->vector[1] = evec->vector[0];
201  }
202 
203  /* Make union and store in attr array */
204  attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
205  giststate->supportCollation[i],
206  PointerGetDatum(evec),
207  PointerGetDatum(&attrsize));
208 
209  isnull[i] = false;
210  }
211  }
212 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
int32 n
Definition: gist.h:206
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:207
#define GEVHDRSZ
Definition: gist.h:210
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:547
struct GISTENTRY GISTENTRY
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
void * palloc(Size size)
Definition: mcxt.c:949
int i
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87

◆ gistMakeUnionKey()

void gistMakeUnionKey ( GISTSTATE giststate,
int  attno,
GISTENTRY entry1,
bool  isnull1,
GISTENTRY entry2,
bool  isnull2,
Datum dst,
bool dstisnull 
)

Definition at line 233 of file gistutil.c.

References FunctionCall2Coll(), GEVHDRSZ, GistEntryVector::n, PointerGetDatum, GISTSTATE::supportCollation, GISTSTATE::unionFn, and GistEntryVector::vector.

Referenced by gistgetadjusted(), and supportSecondarySplit().

237 {
238  /* we need a GistEntryVector with room for exactly 2 elements */
239  union
240  {
241  GistEntryVector gev;
242  char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
243  } storage;
244  GistEntryVector *evec = &storage.gev;
245  int dstsize;
246 
247  evec->n = 2;
248 
249  if (isnull1 && isnull2)
250  {
251  *dstisnull = true;
252  *dst = (Datum) 0;
253  }
254  else
255  {
256  if (isnull1 == false && isnull2 == false)
257  {
258  evec->vector[0] = *entry1;
259  evec->vector[1] = *entry2;
260  }
261  else if (isnull1 == false)
262  {
263  evec->vector[0] = *entry1;
264  evec->vector[1] = *entry1;
265  }
266  else
267  {
268  evec->vector[0] = *entry2;
269  evec->vector[1] = *entry2;
270  }
271 
272  *dstisnull = false;
273  *dst = FunctionCall2Coll(&giststate->unionFn[attno],
274  giststate->supportCollation[attno],
275  PointerGetDatum(evec),
276  PointerGetDatum(&dstsize));
277  }
278 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
int32 n
Definition: gist.h:206
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:207
#define GEVHDRSZ
Definition: gist.h:210
struct GISTENTRY GISTENTRY
uintptr_t Datum
Definition: postgres.h:367
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 810 of file gistutil.c.

References DataPageDeleteStack::blkno, BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), GIST_EXCLUSIVE, GIST_UNLOCK, gistcheckpage(), GistPageGetDeleteXid(), gistPageRecyclable(), gistXLogPageReuse(), InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, RelationNeedsWAL, ReleaseBuffer(), UnlockRelationForExtension(), and XLogStandbyInfoActive.

Referenced by gistbuild(), and gistplacetopage().

811 {
812  Buffer buffer;
813  bool needLock;
814 
815  /* First, try to get a page from FSM */
816  for (;;)
817  {
818  BlockNumber blkno = GetFreeIndexPage(r);
819 
820  if (blkno == InvalidBlockNumber)
821  break; /* nothing left in FSM */
822 
823  buffer = ReadBuffer(r, blkno);
824 
825  /*
826  * We have to guard against the possibility that someone else already
827  * recycled this page; the buffer may be locked if so.
828  */
829  if (ConditionalLockBuffer(buffer))
830  {
831  Page page = BufferGetPage(buffer);
832 
833  /*
834  * If the page was never initialized, it's OK to use.
835  */
836  if (PageIsNew(page))
837  return buffer;
838 
839  gistcheckpage(r, buffer);
840 
841  /*
842  * Otherwise, recycle it if deleted, and too old to have any
843  * processes interested in it.
844  */
845  if (gistPageRecyclable(page))
846  {
847  /*
848  * If we are generating WAL for Hot Standby then create a WAL
849  * record that will allow us to conflict with queries running
850  * on standby, in case they have snapshots older than the
851  * page's deleteXid.
852  */
854  gistXLogPageReuse(r, blkno, GistPageGetDeleteXid(page));
855 
856  return buffer;
857  }
858 
859  LockBuffer(buffer, GIST_UNLOCK);
860  }
861 
862  /* Can't use it, so release buffer and try again */
863  ReleaseBuffer(buffer);
864  }
865 
866  /* Must extend the file */
867  needLock = !RELATION_IS_LOCAL(r);
868 
869  if (needLock)
871 
872  buffer = ReadBuffer(r, P_NEW);
873  LockBuffer(buffer, GIST_EXCLUSIVE);
874 
875  if (needLock)
877 
878  return buffer;
879 }
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:539
#define GIST_UNLOCK
Definition: gist_private.h:44
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3365
#define P_NEW
Definition: bufmgr.h:81
void gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid)
Definition: gistxlog.c:599
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3628
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:402
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:452
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3602
#define XLogStandbyInfoActive()
Definition: xlog.h:195
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:771
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:521
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:186
bool gistPageRecyclable(Page page)
Definition: gistutil.c:883
#define PageIsNew(page)
Definition: bufpage.h:229
#define GIST_EXCLUSIVE
Definition: gist_private.h:43
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ gistnospace()

bool gistnospace ( Page  page,
IndexTuple itvec,
int  len,
OffsetNumber  todelete,
Size  freespace 
)

Definition at line 59 of file gistutil.c.

References i, IndexTupleSize, InvalidOffsetNumber, PageGetFreeSpace(), PageGetItem, and PageGetItemId.

Referenced by gistplacetopage().

60 {
61  unsigned int size = freespace,
62  deleted = 0;
63  int i;
64 
65  for (i = 0; i < len; i++)
66  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
67 
68  if (todelete != InvalidOffsetNumber)
69  {
70  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
71 
72  deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
73  }
74 
75  return (PageGetFreeSpace(page) + deleted < size);
76 }
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:581
IndexTupleData * IndexTuple
Definition: itup.h:53
struct ItemIdData ItemIdData
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 909 of file gistutil.c.

References allocateReloptStruct(), fillfactor, fillRelOptions(), lengthof, offsetof, options, parseRelOptions(), pfree(), RELOPT_KIND_GIST, RELOPT_TYPE_ENUM, and RELOPT_TYPE_INT.

Referenced by gisthandler().

910 {
912  GiSTOptions *rdopts;
913  int numoptions;
914  static const relopt_parse_elt tab[] = {
915  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
916  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
917  };
918 
919  options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
920  &numoptions);
921 
922  /* if none set, we're done */
923  if (numoptions == 0)
924  return NULL;
925 
926  rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
927 
928  fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
929  validate, tab, lengthof(tab));
930 
931  pfree(options);
932 
933  return (bytea *) rdopts;
934 }
#define lengthof(array)
Definition: c.h:662
void pfree(void *pointer)
Definition: mcxt.c:1056
int fillfactor
Definition: pgbench.c:157
void * allocateReloptStruct(Size base, relopt_value *options, int numoptions)
Definition: reloptions.c:1369
static char ** options
void fillRelOptions(void *rdopts, Size basesize, relopt_value *options, int numoptions, bool validate, const relopt_parse_elt *elems, int numelems)
Definition: reloptions.c:1393
Definition: c.h:549
relopt_value * parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts)
Definition: reloptions.c:1144
#define offsetof(type, field)
Definition: c.h:655

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 883 of file gistutil.c.

References FullTransactionIdPrecedes, GetFullRecentGlobalXmin(), GistPageGetDeleteXid(), GistPageIsDeleted, and PageIsNew.

Referenced by gistNewBuffer(), and gistvacuumpage().

884 {
885  if (PageIsNew(page))
886  return true;
887  if (GistPageIsDeleted(page))
888  {
889  /*
890  * The page was deleted, but when? If it was just deleted, a scan
891  * might have seen the downlink to it, and will read the page later.
892  * As long as that can happen, we must keep the deleted page around as
893  * a tombstone.
894  *
895  * Compare the deletion XID with RecentGlobalXmin. If deleteXid <
896  * RecentGlobalXmin, then no scan that's still in progress could have
897  * seen its downlink, and we can recycle it.
898  */
899  FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
900  FullTransactionId recentxmin_full = GetFullRecentGlobalXmin();
901 
902  if (FullTransactionIdPrecedes(deletexid_full, recentxmin_full))
903  return true;
904  }
905  return false;
906 }
#define GistPageIsDeleted(page)
Definition: gist.h:143
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:186
#define PageIsNew(page)
Definition: bufpage.h:229
#define FullTransactionIdPrecedes(a, b)
Definition: transam.h:50
FullTransactionId GetFullRecentGlobalXmin(void)
Definition: snapmgr.c:963

◆ gistpenalty()

float gistpenalty ( GISTSTATE giststate,
int  attno,
GISTENTRY key1,
bool  isNull1,
GISTENTRY key2,
bool  isNull2 
)

Definition at line 716 of file gistutil.c.

References FmgrInfo::fn_strict, FunctionCall3Coll(), get_float4_infinity(), GISTSTATE::penaltyFn, PointerGetDatum, and GISTSTATE::supportCollation.

Referenced by findDontCares(), gistchoose(), gistRelocateBuildBuffersOnSplit(), placeOne(), and supportSecondarySplit().

719 {
720  float penalty = 0.0;
721 
722  if (giststate->penaltyFn[attno].fn_strict == false ||
723  (isNullOrig == false && isNullAdd == false))
724  {
725  FunctionCall3Coll(&giststate->penaltyFn[attno],
726  giststate->supportCollation[attno],
727  PointerGetDatum(orig),
728  PointerGetDatum(add),
729  PointerGetDatum(&penalty));
730  /* disallow negative or NaN penalty */
731  if (isnan(penalty) || penalty < 0.0)
732  penalty = 0.0;
733  }
734  else if (isNullOrig && isNullAdd)
735  penalty = 0.0;
736  else
737  {
738  /* try to prevent mixing null and non-null values */
739  penalty = get_float4_infinity();
740  }
741 
742  return penalty;
743 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
bool fn_strict
Definition: fmgr.h:61
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1172
static float4 get_float4_infinity(void)
Definition: float.h:70

◆ gistplacetopage()

bool gistplacetopage ( Relation  rel,
Size  freespace,
GISTSTATE giststate,
Buffer  buffer,
IndexTuple itup,
int  ntup,
OffsetNumber  oldoffnum,
BlockNumber newblkno,
Buffer  leftchildbuf,
List **  splitinfo,
bool  markfollowright,
Relation  heapRel,
bool  is_build 
)

Definition at line 216 of file gist.c.

References DataPageDeleteStack::blkno, gistxlogPage::blkno, SplitedPageLayout::block, GISTPageSplitInfo::buf, SplitedPageLayout::buffer, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, GISTPageSplitInfo::downlink, elog, END_CRIT_SECTION, ERROR, F_LEAF, FirstOffsetNumber, GIST_MAX_SPLIT_PAGES, GIST_ROOT_BLKNO, GistBuildLSN, GistClearFollowRight, gistextractpage(), gistfillbuffer(), gistfillitupvec(), GistFollowRight, gistGetFakeLSN(), GISTInitBuffer(), gistjoinvector(), GistMarkFollowRight, gistNewBuffer(), gistnospace(), GistPageGetNSN, GistPageGetOpaque, GistPageHasGarbage, GistPageIsLeaf, GistPageSetNSN, gistprunepage(), gistSplit(), GistTupleSetValid, gistXLogSplit(), gistXLogUpdate(), i, IndexTupleSize, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerEquals(), ItemPointerSetBlockNumber, SplitedPageLayout::itup, lappend(), SplitedPageLayout::lenlist, SplitedPageLayout::list, MarkBufferDirty(), memmove, SplitedPageLayout::next, NIL, gistxlogPage::num, OffsetNumberIsValid, SplitedPageLayout::page, PageAddItem, PageGetTempPageCopySpecial(), PageIndexTupleDelete(), PageIndexTupleOverwrite(), PageRestoreTempPage(), PageSetLSN, palloc(), PredicateLockPageSplit(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogEnsureRecordSpace().

Referenced by gistbufferinginserttuples(), and gistinserttuples().

225 {
226  BlockNumber blkno = BufferGetBlockNumber(buffer);
227  Page page = BufferGetPage(buffer);
228  bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
229  XLogRecPtr recptr;
230  int i;
231  bool is_split;
232 
233  /*
234  * Refuse to modify a page that's incompletely split. This should not
235  * happen because we finish any incomplete splits while we walk down the
236  * tree. However, it's remotely possible that another concurrent inserter
237  * splits a parent page, and errors out before completing the split. We
238  * will just throw an error in that case, and leave any split we had in
239  * progress unfinished too. The next insert that comes along will clean up
240  * the mess.
241  */
242  if (GistFollowRight(page))
243  elog(ERROR, "concurrent GiST page split was incomplete");
244 
245  *splitinfo = NIL;
246 
247  /*
248  * if isupdate, remove old key: This node's key has been modified, either
249  * because a child split occurred or because we needed to adjust our key
250  * for an insert in a child node. Therefore, remove the old version of
251  * this node's key.
252  *
253  * for WAL replay, in the non-split case we handle this by setting up a
254  * one-element todelete array; in the split case, it's handled implicitly
255  * because the tuple vector passed to gistSplit won't include this tuple.
256  */
257  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
258 
259  /*
260  * If leaf page is full, try at first to delete dead tuples. And then
261  * check again.
262  */
263  if (is_split && GistPageIsLeaf(page) && GistPageHasGarbage(page))
264  {
265  gistprunepage(rel, page, buffer, heapRel);
266  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
267  }
268 
269  if (is_split)
270  {
271  /* no space for insertion */
272  IndexTuple *itvec;
273  int tlen;
274  SplitedPageLayout *dist = NULL,
275  *ptr;
276  BlockNumber oldrlink = InvalidBlockNumber;
277  GistNSN oldnsn = 0;
278  SplitedPageLayout rootpg;
279  bool is_rootsplit;
280  int npage;
281 
282  is_rootsplit = (blkno == GIST_ROOT_BLKNO);
283 
284  /*
285  * Form index tuples vector to split. If we're replacing an old tuple,
286  * remove the old version from the vector.
287  */
288  itvec = gistextractpage(page, &tlen);
289  if (OffsetNumberIsValid(oldoffnum))
290  {
291  /* on inner page we should remove old tuple */
292  int pos = oldoffnum - FirstOffsetNumber;
293 
294  tlen--;
295  if (pos != tlen)
296  memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
297  }
298  itvec = gistjoinvector(itvec, &tlen, itup, ntup);
299  dist = gistSplit(rel, page, itvec, tlen, giststate);
300 
301  /*
302  * Check that split didn't produce too many pages.
303  */
304  npage = 0;
305  for (ptr = dist; ptr; ptr = ptr->next)
306  npage++;
307  /* in a root split, we'll add one more page to the list below */
308  if (is_rootsplit)
309  npage++;
310  if (npage > GIST_MAX_SPLIT_PAGES)
311  elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
312  npage, GIST_MAX_SPLIT_PAGES);
313 
314  /*
315  * Set up pages to work with. Allocate new buffers for all but the
316  * leftmost page. The original page becomes the new leftmost page, and
317  * is just replaced with the new contents.
318  *
319  * For a root-split, allocate new buffers for all child pages, the
320  * original page is overwritten with new root page containing
321  * downlinks to the new child pages.
322  */
323  ptr = dist;
324  if (!is_rootsplit)
325  {
326  /* save old rightlink and NSN */
327  oldrlink = GistPageGetOpaque(page)->rightlink;
328  oldnsn = GistPageGetNSN(page);
329 
330  dist->buffer = buffer;
331  dist->block.blkno = BufferGetBlockNumber(buffer);
333 
334  /* clean all flags except F_LEAF */
335  GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
336 
337  ptr = ptr->next;
338  }
339  for (; ptr; ptr = ptr->next)
340  {
341  /* Allocate new page */
342  ptr->buffer = gistNewBuffer(rel);
343  GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
344  ptr->page = BufferGetPage(ptr->buffer);
345  ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
347  BufferGetBlockNumber(buffer),
348  BufferGetBlockNumber(ptr->buffer));
349  }
350 
351  /*
352  * Now that we know which blocks the new pages go to, set up downlink
353  * tuples to point to them.
354  */
355  for (ptr = dist; ptr; ptr = ptr->next)
356  {
357  ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
358  GistTupleSetValid(ptr->itup);
359  }
360 
361  /*
362  * If this is a root split, we construct the new root page with the
363  * downlinks here directly, instead of requiring the caller to insert
364  * them. Add the new root page to the list along with the child pages.
365  */
366  if (is_rootsplit)
367  {
368  IndexTuple *downlinks;
369  int ndownlinks = 0;
370  int i;
371 
372  rootpg.buffer = buffer;
374  GistPageGetOpaque(rootpg.page)->flags = 0;
375 
376  /* Prepare a vector of all the downlinks */
377  for (ptr = dist; ptr; ptr = ptr->next)
378  ndownlinks++;
379  downlinks = palloc(sizeof(IndexTuple) * ndownlinks);
380  for (i = 0, ptr = dist; ptr; ptr = ptr->next)
381  downlinks[i++] = ptr->itup;
382 
383  rootpg.block.blkno = GIST_ROOT_BLKNO;
384  rootpg.block.num = ndownlinks;
385  rootpg.list = gistfillitupvec(downlinks, ndownlinks,
386  &(rootpg.lenlist));
387  rootpg.itup = NULL;
388 
389  rootpg.next = dist;
390  dist = &rootpg;
391  }
392  else
393  {
394  /* Prepare split-info to be returned to caller */
395  for (ptr = dist; ptr; ptr = ptr->next)
396  {
398 
399  si->buf = ptr->buffer;
400  si->downlink = ptr->itup;
401  *splitinfo = lappend(*splitinfo, si);
402  }
403  }
404 
405  /*
406  * Fill all pages. All the pages are new, ie. freshly allocated empty
407  * pages, or a temporary copy of the old page.
408  */
409  for (ptr = dist; ptr; ptr = ptr->next)
410  {
411  char *data = (char *) (ptr->list);
412 
413  for (i = 0; i < ptr->block.num; i++)
414  {
415  IndexTuple thistup = (IndexTuple) data;
416 
417  if (PageAddItem(ptr->page, (Item) data, IndexTupleSize(thistup), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
418  elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(rel));
419 
420  /*
421  * If this is the first inserted/updated tuple, let the caller
422  * know which page it landed on.
423  */
424  if (newblkno && ItemPointerEquals(&thistup->t_tid, &(*itup)->t_tid))
425  *newblkno = ptr->block.blkno;
426 
427  data += IndexTupleSize(thistup);
428  }
429 
430  /* Set up rightlinks */
431  if (ptr->next && ptr->block.blkno != GIST_ROOT_BLKNO)
432  GistPageGetOpaque(ptr->page)->rightlink =
433  ptr->next->block.blkno;
434  else
435  GistPageGetOpaque(ptr->page)->rightlink = oldrlink;
436 
437  /*
438  * Mark the all but the right-most page with the follow-right
439  * flag. It will be cleared as soon as the downlink is inserted
440  * into the parent, but this ensures that if we error out before
441  * that, the index is still consistent. (in buffering build mode,
442  * any error will abort the index build anyway, so this is not
443  * needed.)
444  */
445  if (ptr->next && !is_rootsplit && markfollowright)
446  GistMarkFollowRight(ptr->page);
447  else
448  GistClearFollowRight(ptr->page);
449 
450  /*
451  * Copy the NSN of the original page to all pages. The
452  * F_FOLLOW_RIGHT flags ensure that scans will follow the
453  * rightlinks until the downlinks are inserted.
454  */
455  GistPageSetNSN(ptr->page, oldnsn);
456  }
457 
458  /*
459  * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
460  * insertion for that. NB: The number of pages and data segments
461  * specified here must match the calculations in gistXLogSplit()!
462  */
463  if (!is_build && RelationNeedsWAL(rel))
464  XLogEnsureRecordSpace(npage, 1 + npage * 2);
465 
467 
468  /*
469  * Must mark buffers dirty before XLogInsert, even though we'll still
470  * be changing their opaque fields below.
471  */
472  for (ptr = dist; ptr; ptr = ptr->next)
473  MarkBufferDirty(ptr->buffer);
474  if (BufferIsValid(leftchildbuf))
475  MarkBufferDirty(leftchildbuf);
476 
477  /*
478  * The first page in the chain was a temporary working copy meant to
479  * replace the old page. Copy it over the old page.
480  */
482  dist->page = BufferGetPage(dist->buffer);
483 
484  /*
485  * Write the WAL record.
486  *
487  * If we're building a new index, however, we don't WAL-log changes
488  * yet. The LSN-NSN interlock between parent and child requires that
489  * LSNs never move backwards, so set the LSNs to a value that's
490  * smaller than any real or fake unlogged LSN that might be generated
491  * later. (There can't be any concurrent scans during index build, so
492  * we don't need to be able to detect concurrent splits yet.)
493  */
494  if (is_build)
495  recptr = GistBuildLSN;
496  else
497  {
498  if (RelationNeedsWAL(rel))
499  recptr = gistXLogSplit(is_leaf,
500  dist, oldrlink, oldnsn, leftchildbuf,
501  markfollowright);
502  else
503  recptr = gistGetFakeLSN(rel);
504  }
505 
506  for (ptr = dist; ptr; ptr = ptr->next)
507  PageSetLSN(ptr->page, recptr);
508 
509  /*
510  * Return the new child buffers to the caller.
511  *
512  * If this was a root split, we've already inserted the downlink
513  * pointers, in the form of a new root page. Therefore we can release
514  * all the new buffers, and keep just the root page locked.
515  */
516  if (is_rootsplit)
517  {
518  for (ptr = dist->next; ptr; ptr = ptr->next)
519  UnlockReleaseBuffer(ptr->buffer);
520  }
521  }
522  else
523  {
524  /*
525  * Enough space. We always get here if ntup==0.
526  */
528 
529  /*
530  * Delete old tuple if any, then insert new tuple(s) if any. If
531  * possible, use the fast path of PageIndexTupleOverwrite.
532  */
533  if (OffsetNumberIsValid(oldoffnum))
534  {
535  if (ntup == 1)
536  {
537  /* One-for-one replacement, so use PageIndexTupleOverwrite */
538  if (!PageIndexTupleOverwrite(page, oldoffnum, (Item) *itup,
539  IndexTupleSize(*itup)))
540  elog(ERROR, "failed to add item to index page in \"%s\"",
542  }
543  else
544  {
545  /* Delete old, then append new tuple(s) to page */
546  PageIndexTupleDelete(page, oldoffnum);
547  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
548  }
549  }
550  else
551  {
552  /* Just append new tuples at the end of the page */
553  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
554  }
555 
556  MarkBufferDirty(buffer);
557 
558  if (BufferIsValid(leftchildbuf))
559  MarkBufferDirty(leftchildbuf);
560 
561  if (is_build)
562  recptr = GistBuildLSN;
563  else
564  {
565  if (RelationNeedsWAL(rel))
566  {
567  OffsetNumber ndeloffs = 0,
568  deloffs[1];
569 
570  if (OffsetNumberIsValid(oldoffnum))
571  {
572  deloffs[0] = oldoffnum;
573  ndeloffs = 1;
574  }
575 
576  recptr = gistXLogUpdate(buffer,
577  deloffs, ndeloffs, itup, ntup,
578  leftchildbuf);
579  }
580  else
581  recptr = gistGetFakeLSN(rel);
582  }
583  PageSetLSN(page, recptr);
584 
585  if (newblkno)
586  *newblkno = blkno;
587  }
588 
589  /*
590  * If we inserted the downlink for a child page, set NSN and clear
591  * F_FOLLOW_RIGHT flag on the left child, so that concurrent scans know to
592  * follow the rightlink if and only if they looked at the parent page
593  * before we inserted the downlink.
594  *
595  * Note that we do this *after* writing the WAL record. That means that
596  * the possible full page image in the WAL record does not include these
597  * changes, and they must be replayed even if the page is restored from
598  * the full page image. There's a chicken-and-egg problem: if we updated
599  * the child pages first, we wouldn't know the recptr of the WAL record
600  * we're about to write.
601  */
602  if (BufferIsValid(leftchildbuf))
603  {
604  Page leftpg = BufferGetPage(leftchildbuf);
605 
606  GistPageSetNSN(leftpg, recptr);
607  GistClearFollowRight(leftpg);
608 
609  PageSetLSN(leftpg, recptr);
610  }
611 
613 
614  return is_split;
615 }
#define GistFollowRight(page)
Definition: gist.h:153
#define NIL
Definition: pg_list.h:65
#define GistPageGetNSN(page)
Definition: gist.h:157
#define GistBuildLSN
Definition: gist.h:58
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:410
BlockNumber blkno
Definition: gist_private.h:186
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:726
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
ItemPointerData t_tid
Definition: itup.h:37
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:34
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
Pointer Item
Definition: item.h:17
#define GistPageSetNSN(page, val)
Definition: gist.h:158
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:416
#define GistPageHasGarbage(page)
Definition: gist.h:149
uint32 BlockNumber
Definition: block.h:31
IndexTupleData * list
Definition: gist_private.h:194
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:1027
uint16 OffsetNumber
Definition: off.h:24
Page PageGetTempPageCopySpecial(Page page)
Definition: bufpage.c:388
gistxlogPage block
Definition: gist_private.h:193
XLogRecPtr gistXLogSplit(bool page_is_leaf, SplitedPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:518
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:95
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define ERROR
Definition: elog.h:43
#define GIST_MAX_SPLIT_PAGES
Definition: gist_private.h:39
IndexTuple downlink
Definition: gist_private.h:421
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1065
#define memmove(d, s, c)
Definition: c.h:1238
static void gistprunepage(Relation rel, Page page, Buffer buffer, Relation heapRel)
Definition: gist.c:1625
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:59
#define RelationGetRelationName(relation)
Definition: rel.h:453
struct SplitedPageLayout * next
Definition: gist_private.h:200
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
List * lappend(List *list, void *datum)
Definition: list.c:322
#define GistPageIsLeaf(page)
Definition: gist.h:140
#define GistTupleSetValid(itup)
Definition: gist_private.h:289
#define GistClearFollowRight(page)
Definition: gist.h:155
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:114
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1399
#define InvalidOffsetNumber
Definition: off.h:26
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:632
#define GistPageGetOpaque(page)
Definition: gist.h:138
uint64 XLogRecPtr
Definition: xlogdefs.h:21
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:127
#define InvalidBlockNumber
Definition: block.h:33
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:146
#define BufferIsValid(bufnum)
Definition: bufmgr.h:113
#define ItemPointerSetBlockNumber(pointer, blockNumber)
Definition: itemptr.h:138
#define GistMarkFollowRight(page)
Definition: gist.h:154
#define RelationNeedsWAL(relation)
Definition: rel.h:521
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
XLogRecPtr GistNSN
Definition: gist.h:51
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
void * palloc(Size size)
Definition: mcxt.c:949
#define F_LEAF
Definition: gist.h:43
#define elog(elevel,...)
Definition: elog.h:226
int i
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:749
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:810
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3118
Pointer Page
Definition: bufpage.h:78
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistPopItupFromNodeBuffer()

bool gistPopItupFromNodeBuffer ( GISTBuildBuffers gfbb,
GISTNodeBuffer nodeBuffer,
IndexTuple item 
)

Definition at line 411 of file gistbuildbuffers.c.

References Assert, GISTNodeBuffer::blocksCount, gistBuffersReleaseBlock(), gistGetItupFromPage(), gistLoadNodeBuffer(), InvalidBlockNumber, PAGE_IS_EMPTY, GISTNodeBuffer::pageBuffer, GISTBuildBuffers::pfile, pfree(), GISTNodeBufferPage::prev, and ReadTempFileBlock().

Referenced by gistProcessEmptyingQueue(), and gistRelocateBuildBuffersOnSplit().

413 {
414  /*
415  * If node buffer is empty then return false.
416  */
417  if (nodeBuffer->blocksCount <= 0)
418  return false;
419 
420  /* Load last page of node buffer if needed */
421  if (!nodeBuffer->pageBuffer)
422  gistLoadNodeBuffer(gfbb, nodeBuffer);
423 
424  /*
425  * Get index tuple from last non-empty page.
426  */
427  gistGetItupFromPage(nodeBuffer->pageBuffer, itup);
428 
429  /*
430  * If we just removed the last tuple from the page, fetch previous page on
431  * this node buffer (if any).
432  */
433  if (PAGE_IS_EMPTY(nodeBuffer->pageBuffer))
434  {
435  BlockNumber prevblkno;
436 
437  /*
438  * blocksCount includes the page in pageBuffer, so decrease it now.
439  */
440  nodeBuffer->blocksCount--;
441 
442  /*
443  * If there's more pages, fetch previous one.
444  */
445  prevblkno = nodeBuffer->pageBuffer->prev;
446  if (prevblkno != InvalidBlockNumber)
447  {
448  /* There is a previous page. Fetch it. */
449  Assert(nodeBuffer->blocksCount > 0);
450  ReadTempFileBlock(gfbb->pfile, prevblkno, nodeBuffer->pageBuffer);
451 
452  /*
453  * Now that we've read the block in memory, we can release its
454  * on-disk block for reuse.
455  */
456  gistBuffersReleaseBlock(gfbb, prevblkno);
457  }
458  else
459  {
460  /* No more pages. Free memory. */
461  Assert(nodeBuffer->blocksCount == 0);
462  pfree(nodeBuffer->pageBuffer);
463  nodeBuffer->pageBuffer = NULL;
464  }
465  }
466  return true;
467 }
uint32 BlockNumber
Definition: block.h:31
static void gistBuffersReleaseBlock(GISTBuildBuffers *gfbb, long blocknum)
static void gistLoadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
void pfree(void *pointer)
Definition: mcxt.c:1056
static void gistGetItupFromPage(GISTNodeBufferPage *pageBuffer, IndexTuple *item)
BlockNumber prev
Definition: gist_private.h:48
#define Assert(condition)
Definition: c.h:732
#define InvalidBlockNumber
Definition: block.h:33
#define PAGE_IS_EMPTY(nbp)
Definition: gist_private.h:57
static void ReadTempFileBlock(BufFile *file, long blknum, void *ptr)

◆ gistproperty()

bool gistproperty ( Oid  index_oid,
int  attno,
IndexAMProperty  prop,
const char *  propname,
bool res,
bool isnull 
)

Definition at line 944 of file gistutil.c.

References AMPROCNUM, AMPROP_DISTANCE_ORDERABLE, AMPROP_RETURNABLE, get_index_column_opclass(), get_opclass_opfamily_and_input_type(), GIST_COMPRESS_PROC, GIST_DISTANCE_PROC, GIST_FETCH_PROC, Int16GetDatum, ObjectIdGetDatum, OidIsValid, and SearchSysCacheExists4.

Referenced by gisthandler().

947 {
948  Oid opclass,
949  opfamily,
950  opcintype;
951  int16 procno;
952 
953  /* Only answer column-level inquiries */
954  if (attno == 0)
955  return false;
956 
957  /*
958  * Currently, GiST distance-ordered scans require that there be a distance
959  * function in the opclass with the default types (i.e. the one loaded
960  * into the relcache entry, see initGISTstate). So we assume that if such
961  * a function exists, then there's a reason for it (rather than grubbing
962  * through all the opfamily's operators to find an ordered one).
963  *
964  * Essentially the same code can test whether we support returning the
965  * column data, since that's true if the opclass provides a fetch proc.
966  */
967 
968  switch (prop)
969  {
971  procno = GIST_DISTANCE_PROC;
972  break;
973  case AMPROP_RETURNABLE:
974  procno = GIST_FETCH_PROC;
975  break;
976  default:
977  return false;
978  }
979 
980  /* First we need to know the column's opclass. */
981  opclass = get_index_column_opclass(index_oid, attno);
982  if (!OidIsValid(opclass))
983  {
984  *isnull = true;
985  return true;
986  }
987 
988  /* Now look up the opclass family and input datatype. */
989  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
990  {
991  *isnull = true;
992  return true;
993  }
994 
995  /* And now we can check whether the function is provided. */
996 
998  ObjectIdGetDatum(opfamily),
999  ObjectIdGetDatum(opcintype),
1000  ObjectIdGetDatum(opcintype),
1001  Int16GetDatum(procno));
1002 
1003  /*
1004  * Special case: even without a fetch function, AMPROP_RETURNABLE is true
1005  * if the opclass has no compress function.
1006  */
1007  if (prop == AMPROP_RETURNABLE && !*res)
1008  {
1010  ObjectIdGetDatum(opfamily),
1011  ObjectIdGetDatum(opcintype),
1012  ObjectIdGetDatum(opcintype),
1014  }
1015 
1016  *isnull = false;
1017 
1018  return true;
1019 }
signed short int16
Definition: c.h:345
#define GIST_FETCH_PROC
Definition: gist.h:37
#define Int16GetDatum(X)
Definition: postgres.h:451
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:189
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1064
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define GIST_COMPRESS_PROC
Definition: gist.h:31
#define GIST_DISTANCE_PROC
Definition: gist.h:36
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3164

◆ gistPushItupToNodeBuffer()

void gistPushItupToNodeBuffer ( GISTBuildBuffers gfbb,
GISTNodeBuffer nodeBuffer,
IndexTuple  item 
)

Definition at line 341 of file gistbuildbuffers.c.

References DataPageDeleteStack::blkno, GISTNodeBuffer::blocksCount, BUFFER_HALF_FILLED, GISTBuildBuffers::bufferEmptyingQueue, GISTBuildBuffers::context, gistAddLoadedBuffer(), gistAllocateNewPageBuffer(), gistBuffersGetFreeBlock(), gistLoadNodeBuffer(), gistPlaceItupToPage(), lcons(), MAXALIGN, MemoryContextSwitchTo(), offsetof, PAGE_FREE_SPACE, PAGE_NO_SPACE, GISTNodeBuffer::pageBuffer, GISTBuildBuffers::pfile, GISTNodeBufferPage::prev, GISTNodeBuffer::queuedForEmptying, and WriteTempFileBlock().

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

343 {
344  /*
345  * Most part of memory operations will be in buffering build persistent
346  * context. So, let's switch to it.
347  */
349 
350  /*
351  * If the buffer is currently empty, create the first page.
352  */
353  if (nodeBuffer->blocksCount == 0)
354  {
355  nodeBuffer->pageBuffer = gistAllocateNewPageBuffer(gfbb);
356  nodeBuffer->blocksCount = 1;
357  gistAddLoadedBuffer(gfbb, nodeBuffer);
358  }
359 
360  /* Load last page of node buffer if it wasn't in memory already */
361  if (!nodeBuffer->pageBuffer)
362  gistLoadNodeBuffer(gfbb, nodeBuffer);
363 
364  /*
365  * Check if there is enough space on the last page for the tuple.
366  */
367  if (PAGE_NO_SPACE(nodeBuffer->pageBuffer, itup))
368  {
369  /*
370  * Nope. Swap previous block to disk and allocate a new one.
371  */
372  BlockNumber blkno;
373 
374  /* Write filled page to the disk */
375  blkno = gistBuffersGetFreeBlock(gfbb);
376  WriteTempFileBlock(gfbb->pfile, blkno, nodeBuffer->pageBuffer);
377 
378  /*
379  * Reset the in-memory page as empty, and link the previous block to
380  * the new page by storing its block number in the prev-link.
381  */
382  PAGE_FREE_SPACE(nodeBuffer->pageBuffer) =
383  BLCKSZ - MAXALIGN(offsetof(GISTNodeBufferPage, tupledata));
384  nodeBuffer->pageBuffer->prev = blkno;
385 
386  /* We've just added one more page */
387  nodeBuffer->blocksCount++;
388  }
389 
390  gistPlaceItupToPage(nodeBuffer->pageBuffer, itup);
391 
392  /*
393  * If the buffer just overflowed, add it to the emptying queue.
394  */
395  if (BUFFER_HALF_FILLED(nodeBuffer, gfbb) && !nodeBuffer->queuedForEmptying)
396  {
397  gfbb->bufferEmptyingQueue = lcons(nodeBuffer,
398  gfbb->bufferEmptyingQueue);
399  nodeBuffer->queuedForEmptying = true;
400  }
401 
402  /* Restore memory context */
403  MemoryContextSwitchTo(oldcxt);
404 }
#define PAGE_FREE_SPACE(nbp)
Definition: gist_private.h:55
static void gistPlaceItupToPage(GISTNodeBufferPage *pageBuffer, IndexTuple item)
static GISTNodeBufferPage * gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
MemoryContext context
Definition: gist_private.h:341
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb)
Definition: gist_private.h:324
uint32 BlockNumber
Definition: block.h:31
static void gistLoadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
static long gistBuffersGetFreeBlock(GISTBuildBuffers *gfbb)
List * bufferEmptyingQueue
Definition: gist_private.h:357
#define PAGE_NO_SPACE(nbp, itup)
Definition: gist_private.h:59
bool queuedForEmptying
Definition: gist_private.h:307
static void WriteTempFileBlock(BufFile *file, long blknum, void *ptr)
List * lcons(void *datum, List *list)
Definition: list.c:454
BlockNumber prev
Definition: gist_private.h:48
#define MAXALIGN(LEN)
Definition: c.h:685
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
#define offsetof(type, field)
Definition: c.h:655

◆ gistRelocateBuildBuffersOnSplit()

void gistRelocateBuildBuffersOnSplit ( GISTBuildBuffers gfbb,
GISTSTATE giststate,
Relation  r,
int  level,
Buffer  buffer,
List splitinfo 
)

Definition at line 538 of file gistbuildbuffers.c.

References Assert, GISTNodeBuffer::blocksCount, GISTPageSplitInfo::buf, BufferGetBlockNumber(), GISTPageSplitInfo::downlink, RelocationBufferInfo::entry, GIST_ROOT_BLKNO, gistDeCompressAtt(), gistgetadjusted(), gistGetNodeBuffer(), gistpenalty(), gistPopItupFromNodeBuffer(), gistPushItupToNodeBuffer(), HASH_FIND, hash_search(), i, INDEX_MAX_KEYS, InvalidBlockNumber, RelocationBufferInfo::isnull, GISTNodeBuffer::isTemp, LEVEL_HAS_BUFFERS, lfirst, list_length(), TupleDescData::natts, RelocationBufferInfo::nodeBuffer, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, palloc(), pfree(), RelationData::rd_att, and RelocationBufferInfo::splitinfo.

Referenced by gistbufferinginserttuples().

541 {
542  RelocationBufferInfo *relocationBuffersInfos;
543  bool found;
544  GISTNodeBuffer *nodeBuffer;
545  BlockNumber blocknum;
546  IndexTuple itup;
547  int splitPagesCount = 0,
548  i;
549  GISTENTRY entry[INDEX_MAX_KEYS];
550  bool isnull[INDEX_MAX_KEYS];
551  GISTNodeBuffer oldBuf;
552  ListCell *lc;
553 
554  /* If the splitted page doesn't have buffers, we have nothing to do. */
555  if (!LEVEL_HAS_BUFFERS(level, gfbb))
556  return;
557 
558  /*
559  * Get the node buffer of the splitted page.
560  */
561  blocknum = BufferGetBlockNumber(buffer);
562  nodeBuffer = hash_search(gfbb->nodeBuffersTab, &blocknum,
563  HASH_FIND, &found);
564  if (!found)
565  {
566  /* The page has no buffer, so we have nothing to do. */
567  return;
568  }
569 
570  /*
571  * Make a copy of the old buffer, as we're going reuse it as the buffer
572  * for the new left page, which is on the same block as the old page.
573  * That's not true for the root page, but that's fine because we never
574  * have a buffer on the root page anyway. The original algorithm as
575  * described by Arge et al did, but it's of no use, as you might as well
576  * read the tuples straight from the heap instead of the root buffer.
577  */
578  Assert(blocknum != GIST_ROOT_BLKNO);
579  memcpy(&oldBuf, nodeBuffer, sizeof(GISTNodeBuffer));
580  oldBuf.isTemp = true;
581 
582  /* Reset the old buffer, used for the new left page from now on */
583  nodeBuffer->blocksCount = 0;
584  nodeBuffer->pageBuffer = NULL;
585  nodeBuffer->pageBlocknum = InvalidBlockNumber;
586 
587  /*
588  * Allocate memory for information about relocation buffers.
589  */
590  splitPagesCount = list_length(splitinfo);
591  relocationBuffersInfos =
593  splitPagesCount);
594 
595  /*
596  * Fill relocation buffers information for node buffers of pages produced
597  * by split.
598  */
599  i = 0;
600  foreach(lc, splitinfo)
601  {
603  GISTNodeBuffer *newNodeBuffer;
604 
605  /* Decompress parent index tuple of node buffer page. */
606  gistDeCompressAtt(giststate, r,
607  si->downlink, NULL, (OffsetNumber) 0,
608  relocationBuffersInfos[i].entry,
609  relocationBuffersInfos[i].isnull);
610 
611  /*
612  * Create a node buffer for the page. The leftmost half is on the same
613  * block as the old page before split, so for the leftmost half this
614  * will return the original buffer. The tuples on the original buffer
615  * were relinked to the temporary buffer, so the original one is now
616  * empty.
617  */
618  newNodeBuffer = gistGetNodeBuffer(gfbb, giststate, BufferGetBlockNumber(si->buf), level);
619 
620  relocationBuffersInfos[i].nodeBuffer = newNodeBuffer;
621  relocationBuffersInfos[i].splitinfo = si;
622 
623  i++;
624  }
625 
626  /*
627  * Loop through all index tuples in the buffer of the page being split,
628  * moving them to buffers for the new pages. We try to move each tuple to
629  * the page that will result in the lowest penalty for the leading column
630  * or, in the case of a tie, the lowest penalty for the earliest column
631  * that is not tied.
632  *
633  * The page searching logic is very similar to gistchoose().
634  */
635  while (gistPopItupFromNodeBuffer(gfbb, &oldBuf, &itup))
636  {
637  float best_penalty[INDEX_MAX_KEYS];
638  int i,
639  which;
640  IndexTuple newtup;
641  RelocationBufferInfo *targetBufferInfo;
642 
643  gistDeCompressAtt(giststate, r,
644  itup, NULL, (OffsetNumber) 0, entry, isnull);
645 
646  /* default to using first page (shouldn't matter) */
647  which = 0;
648 
649  /*
650  * best_penalty[j] is the best penalty we have seen so far for column
651  * j, or -1 when we haven't yet examined column j. Array entries to
652  * the right of the first -1 are undefined.
653  */
654  best_penalty[0] = -1;
655 
656  /*
657  * Loop over possible target pages, looking for one to move this tuple
658  * to.
659  */
660  for (i = 0; i < splitPagesCount; i++)
661  {
662  RelocationBufferInfo *splitPageInfo = &relocationBuffersInfos[i];
663  bool zero_penalty;
664  int j;
665 
666  zero_penalty = true;
667 
668  /* Loop over index attributes. */
669  for (j = 0; j < r->rd_att->natts; j++)
670  {
671  float usize;
672 
673  /* Compute penalty for this column. */
674  usize = gistpenalty(giststate, j,
675  &splitPageInfo->entry[j],
676  splitPageInfo->isnull[j],
677  &entry[j], isnull[j]);
678  if (usize > 0)
679  zero_penalty = false;
680 
681  if (best_penalty[j] < 0 || usize < best_penalty[j])
682  {
683  /*
684  * New best penalty for column. Tentatively select this
685  * page as the target, and record the best penalty. Then
686  * reset the next column's penalty to "unknown" (and
687  * indirectly, the same for all the ones to its right).
688  * This will force us to adopt this page's penalty values
689  * as the best for all the remaining columns during
690  * subsequent loop iterations.
691  */
692  which = i;
693  best_penalty[j] = usize;
694 
695  if (j < r->rd_att->natts - 1)
696  best_penalty[j + 1] = -1;
697  }
698  else if (best_penalty[j] == usize)
699  {
700  /*
701  * The current page is exactly as good for this column as
702  * the best page seen so far. The next iteration of this
703  * loop will compare the next column.
704  */
705  }
706  else
707  {
708  /*
709  * The current page is worse for this column than the best
710  * page seen so far. Skip the remaining columns and move
711  * on to the next page, if any.
712  */
713  zero_penalty = false; /* so outer loop won't exit */
714  break;
715  }
716  }
717 
718  /*
719  * If we find a page with zero penalty for all columns, there's no
720  * need to examine remaining pages; just break out of the loop and
721  * return it.
722  */
723  if (zero_penalty)
724  break;
725  }
726 
727  /* OK, "which" is the page index to push the tuple to */
728  targetBufferInfo = &relocationBuffersInfos[which];
729 
730  /* Push item to selected node buffer */
731  gistPushItupToNodeBuffer(gfbb, targetBufferInfo->nodeBuffer, itup);
732 
733  /* Adjust the downlink for this page, if needed. */
734  newtup = gistgetadjusted(r, targetBufferInfo->splitinfo->downlink,
735  itup, giststate);
736  if (newtup)
737  {
738  gistDeCompressAtt(giststate, r,
739  newtup, NULL, (OffsetNumber) 0,
740  targetBufferInfo->entry,
741  targetBufferInfo->isnull);
742 
743  targetBufferInfo->splitinfo->downlink = newtup;
744  }
745  }
746 
747  pfree(relocationBuffersInfos);
748 }
GISTPageSplitInfo * splitinfo
bool isnull[INDEX_MAX_KEYS]
BlockNumber pageBlocknum
Definition: gist_private.h:303
void gistPushItupToNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
Definition: gistutil.c:296
uint32 BlockNumber
Definition: block.h:31
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:316
#define LEVEL_HAS_BUFFERS(nlevel, gfbb)
Definition: gist_private.h:319
GISTENTRY entry[INDEX_MAX_KEYS]
uint16 OffsetNumber
Definition: off.h:24
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
void pfree(void *pointer)
Definition: mcxt.c:1056
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
Definition: gistutil.c:716
IndexTuple downlink
Definition: gist_private.h:421
GISTNodeBuffer * gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
TupleDesc rd_att
Definition: rel.h:84
#define Assert(condition)
Definition: c.h:732
#define lfirst(lc)
Definition: pg_list.h:190
#define INDEX_MAX_KEYS
#define InvalidBlockNumber
Definition: block.h:33
static int list_length(const List *l)
Definition: pg_list.h:169
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
void * palloc(Size size)
Definition: mcxt.c:949
GISTNodeBuffer * nodeBuffer
int i
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
bool gistPopItupFromNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)

◆ gistSplit()

SplitedPageLayout* gistSplit ( Relation  r,
Page  page,
IndexTuple itup,
int  len,
GISTSTATE giststate 
)

Definition at line 1399 of file gist.c.

References Assert, SplitedPageLayout::block, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplit(), gistSplitByKey(), i, IndexTupleSize, SplitedPageLayout::itup, SplitedPageLayout::lenlist, SplitedPageLayout::list, TupleDescData::natts, SplitedPageLayout::next, GISTSTATE::nonLeafTupdesc, gistxlogPage::num, palloc(), RelationGetRelationName, ROTATEDIST, GistSplitVector::spl_lattr, GIST_SPLITVEC::spl_left, GistSplitVector::spl_lisnull, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GistSplitVector::spl_rattr, GIST_SPLITVEC::spl_right, GistSplitVector::spl_risnull, and GistSplitVector::splitVector.

Referenced by gistplacetopage(), and gistSplit().

1404 {
1405  IndexTuple *lvectup,
1406  *rvectup;
1407  GistSplitVector v;
1408  int i;
1409  SplitedPageLayout *res = NULL;
1410 
1411  /* this should never recurse very deeply, but better safe than sorry */
1413 
1414  /* there's no point in splitting an empty page */
1415  Assert(len > 0);
1416 
1417  /*
1418  * If a single tuple doesn't fit on a page, no amount of splitting will
1419  * help.
1420  */
1421  if (len == 1)
1422  ereport(ERROR,
1423  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1424  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1425  IndexTupleSize(itup[0]), GiSTPageSize,
1427 
1428  memset(v.spl_lisnull, true,
1429  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1430  memset(v.spl_risnull, true,
1431  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1432  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1433 
1434  /* form left and right vector */
1435  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1436  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1437 
1438  for (i = 0; i < v.splitVector.spl_nleft; i++)
1439  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1440 
1441  for (i = 0; i < v.splitVector.spl_nright; i++)
1442  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1443 
1444  /* finalize splitting (may need another split) */
1445  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1446  {
1447  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1448  }
1449  else
1450  {
1451  ROTATEDIST(res);
1452  res->block.num = v.splitVector.spl_nright;
1453  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1454  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1455  }
1456 
1457  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1458  {
1459  SplitedPageLayout *resptr,
1460  *subres;
1461 
1462  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1463 
1464  /* install on list's tail */
1465  while (resptr->next)
1466  resptr = resptr->next;
1467 
1468  resptr->next = res;
1469  res = subres;
1470  }
1471  else
1472  {
1473  ROTATEDIST(res);
1474  res->block.num = v.splitVector.spl_nleft;
1475  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1476  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1477  }
1478 
1479  return res;
1480 }
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
OffsetNumber * spl_left
Definition: gist.h:113
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int errcode(int sqlerrcode)
Definition: elog.c:570
int spl_nleft
Definition: gist.h:114
IndexTupleData * list
Definition: gist_private.h:194
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
gistxlogPage block
Definition: gist_private.h:193
#define ERROR
Definition: elog.h:43
int spl_nright
Definition: gist.h:119
void check_stack_depth(void)
Definition: postgres.c:3262
#define RelationGetRelationName(relation)
Definition: rel.h:453
struct SplitedPageLayout * next
Definition: gist_private.h:200
#define ereport(elevel, rest)
Definition: elog.h:141
#define GiSTPageSize
Definition: gist_private.h:468
#define ROTATEDIST(d)
Definition: gist.c:45
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1399
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:79
#define Assert(condition)
Definition: c.h:732
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:127
OffsetNumber * spl_right
Definition: gist.h:118
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:784
int i
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:575
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistSplitByKey()

void gistSplitByKey ( Relation  r,
Page  page,
IndexTuple itup,
int  len,
GISTSTATE giststate,
GistSplitVector v,
int  attno 
)

Definition at line 623 of file gistsplit.c.

References Assert, GEVHDRSZ, gistdentryinit(), gistSplitByKey(), gistSplitHalf(), gistunionsubkey(), gistUserPicksplit(), i, index_getattr, GISTSTATE::leafTupdesc, GistEntryVector::n, TupleDescData::natts, GISTSTATE::nonLeafTupdesc, palloc(), GistSplitVector::spl_dontcare, GIST_SPLITVEC::spl_left, GistSplitVector::spl_lisnull, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_right, GistSplitVector::spl_risnull, GistSplitVector::splitVector, and GistEntryVector::vector.

Referenced by gistSplit(), and gistSplitByKey().

625 {
626  GistEntryVector *entryvec;
627  OffsetNumber *offNullTuples;
628  int nOffNullTuples = 0;
629  int i;
630 
631  /* generate the item array, and identify tuples with null keys */
632  /* note that entryvec->vector[0] goes unused in this code */
633  entryvec = palloc(GEVHDRSZ + (len + 1) * sizeof(GISTENTRY));
634  entryvec->n = len + 1;
635  offNullTuples = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
636 
637  for (i = 1; i <= len; i++)
638  {
639  Datum datum;
640  bool IsNull;
641 
642  datum = index_getattr(itup[i - 1], attno + 1, giststate->leafTupdesc,
643  &IsNull);
644  gistdentryinit(giststate, attno, &(entryvec->vector[i]),
645  datum, r, page, i,
646  false, IsNull);
647  if (IsNull)
648  offNullTuples[nOffNullTuples++] = i;
649  }
650 
651  if (nOffNullTuples == len)
652  {
653  /*
654  * Corner case: All keys in attno column are null, so just transfer
655  * our attention to the next column. If there's no next column, just
656  * split page in half.
657  */
658  v->spl_risnull[attno] = v->spl_lisnull[attno] = true;
659 
660  if (attno + 1 < giststate->nonLeafTupdesc->natts)
661  gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
662  else
663  gistSplitHalf(&v->splitVector, len);
664  }
665  else if (nOffNullTuples > 0)
666  {
667  int j = 0;
668 
669  /*
670  * We don't want to mix NULL and not-NULL keys on one page, so split
671  * nulls to right page and not-nulls to left.
672  */
673  v->splitVector.spl_right = offNullTuples;
674  v->splitVector.spl_nright = nOffNullTuples;
675  v->spl_risnull[attno] = true;
676 
677  v->splitVector.spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
678  v->splitVector.spl_nleft = 0;
679  for (i = 1; i <= len; i++)
680  if (j < v->splitVector.spl_nright && offNullTuples[j] == i)
681  j++;
682  else
684 
685  /* Compute union keys, unless outer recursion level will handle it */
686  if (attno == 0 && giststate->nonLeafTupdesc->natts == 1)
687  {
688  v->spl_dontcare = NULL;
689  gistunionsubkey(giststate, itup, v);
690  }
691  }
692  else
693  {
694  /*
695  * All keys are not-null, so apply user-defined PickSplit method
696  */
697  if (gistUserPicksplit(r, entryvec, attno, v, itup, len, giststate))
698  {
699  /*
700  * Splitting on attno column is not optimal, so consider
701  * redistributing don't-care tuples according to the next column
702  */
703  Assert(attno + 1 < giststate->nonLeafTupdesc->natts);
704 
705  if (v->spl_dontcare == NULL)
706  {
707  /*
708  * This split was actually degenerate, so ignore it altogether
709  * and just split according to the next column.
710  */
711  gistSplitByKey(r, page, itup, len, giststate, v, attno + 1);
712  }
713  else
714  {
715  /*
716  * Form an array of just the don't-care tuples to pass to a
717  * recursive invocation of this function for the next column.
718  */
719  IndexTuple *newitup = (IndexTuple *) palloc(len * sizeof(IndexTuple));
720  OffsetNumber *map = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
721  int newlen = 0;
722  GIST_SPLITVEC backupSplit;
723 
724  for (i = 0; i < len; i++)
725  {
726  if (v->spl_dontcare[i + 1])
727  {
728  newitup[newlen] = itup[i];
729  map[newlen] = i + 1;
730  newlen++;
731  }
732  }
733 
734  Assert(newlen > 0);
735 
736  /*
737  * Make a backup copy of v->splitVector, since the recursive
738  * call will overwrite that with its own result.
739  */
740  backupSplit = v->splitVector;
741  backupSplit.spl_left = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len);
742  memcpy(backupSplit.spl_left, v->splitVector.spl_left, sizeof(OffsetNumber) * v->splitVector.spl_nleft);
743  backupSplit.spl_right = (OffsetNumber *) palloc(sizeof(OffsetNumber) * len);
744  memcpy(backupSplit.spl_right, v->splitVector.spl_right, sizeof(OffsetNumber) * v->splitVector.spl_nright);
745 
746  /* Recursively decide how to split the don't-care tuples */
747  gistSplitByKey(r, page, newitup, newlen, giststate, v, attno + 1);
748 
749  /* Merge result of subsplit with non-don't-care tuples */
750  for (i = 0; i < v->splitVector.spl_nleft; i++)
751  backupSplit.spl_left[backupSplit.spl_nleft++] = map[v->splitVector.spl_left[i] - 1];
752  for (i = 0; i < v->splitVector.spl_nright; i++)
753  backupSplit.spl_right[backupSplit.spl_nright++] = map[v->splitVector.spl_right[i] - 1];
754 
755  v->splitVector = backupSplit;
756  }
757  }
758  }
759 
760  /*
761  * If we're handling a multicolumn index, at the end of the recursion
762  * recompute the left and right union datums for all index columns. This
763  * makes sure we hand back correct union datums in all corner cases,
764  * including when we haven't processed all columns to start with, or when
765  * a secondary split moved "don't care" tuples from one side to the other
766  * (we really shouldn't assume that that didn't change the union datums).
767  *
768  * Note: when we're in an internal recursion (attno > 0), we do not worry
769  * about whether the union datums we return with are sensible, since
770  * calling levels won't care. Also, in a single-column index, we expect
771  * that PickSplit (or the special cases above) produced correct union
772  * datums.
773  */
774  if (attno == 0 && giststate->nonLeafTupdesc->natts > 1)
775  {
776  v->spl_dontcare = NULL;
777  gistunionsubkey(giststate, itup, v);
778  }
779 }
OffsetNumber * spl_left
Definition: gist.h:113
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int32 n
Definition: gist.h:206
int spl_nleft
Definition: gist.h:114
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:207
#define GEVHDRSZ
Definition: gist.h:210
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:547
int spl_nright
Definition: gist.h:119
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define Assert(condition)
Definition: c.h:732
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585
OffsetNumber * spl_right
Definition: gist.h:118
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
void * palloc(Size size)
Definition: mcxt.c:949
int i
static void gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GistSplitVector *spl)
Definition: gistsplit.c:80
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
static bool gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVector *v, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gistsplit.c:415

◆ gistunion()

IndexTuple gistunion ( Relation  r,
IndexTuple itvec,
int  len,
GISTSTATE giststate 
)

Definition at line 219 of file gistutil.c.

References gistFormTuple(), gistMakeUnionItVec(), and INDEX_MAX_KEYS.

220 {
221  Datum attr[INDEX_MAX_KEYS];
222  bool isnull[INDEX_MAX_KEYS];
223 
224  gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
225 
226  return gistFormTuple(giststate, r, attr, isnull, false);
227 }
uintptr_t Datum
Definition: postgres.h:367
void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
Definition: gistutil.c:155
#define INDEX_MAX_KEYS
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:575

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 277 of file gistbuildbuffers.c.

References gistUnloadNodeBuffer(), i, GISTBuildBuffers::loadedBuffers, and GISTBuildBuffers::loadedBuffersCount.

Referenced by gistProcessEmptyingQueue().

278 {
279  int i;
280 
281  /* Unload all the buffers that have a page loaded in memory. */
282  for (i = 0; i < gfbb->loadedBuffersCount; i++)
283  gistUnloadNodeBuffer(gfbb, gfbb->loadedBuffers[i]);
284 
285  /* Now there are no node buffers with loaded last page */
286  gfbb->loadedBuffersCount = 0;
287 }
static void gistUnloadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
int i

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 101 of file gistvacuum.c.

References IndexVacuumInfo::analyze_only, create_GistBulkDeleteResult(), GistBulkDeleteResult::empty_leaf_set, IndexVacuumInfo::estimated_count, gistvacuum_delete_empty_pages(), gistvacuumscan(), GistBulkDeleteResult::internal_page_set, MemoryContextDelete(), IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, GistBulkDeleteResult::page_set_context, and GistBulkDeleteResult::stats.

Referenced by gisthandler().

102 {
103  GistBulkDeleteResult *gist_stats = (GistBulkDeleteResult *) stats;
104 
105  /* No-op in ANALYZE ONLY mode */
106  if (info->analyze_only)
107  return stats;
108 
109  /*
110  * If gistbulkdelete was called, we need not do anything, just return the
111  * stats from the latest gistbulkdelete call. If it wasn't called, we
112  * still need to do a pass over the index, to obtain index statistics.
113  */
114  if (gist_stats == NULL)
115  {
116  gist_stats = create_GistBulkDeleteResult();
117  gistvacuumscan(info, gist_stats, NULL, NULL);
118  }
119 
120  /*
121  * If we saw any empty pages, try to unlink them from the tree so that
122  * they can be reused.
123  */
124  gistvacuum_delete_empty_pages(info, gist_stats);
125 
126  /* we don't need the internal and empty page sets anymore */
128  gist_stats->page_set_context = NULL;
129  gist_stats->internal_page_set = NULL;
130  gist_stats->empty_leaf_set = NULL;
131 
132  /*
133  * It's quite possible for us to be fooled by concurrent page splits into
134  * double-counting some index tuples, so disbelieve any total that exceeds
135  * the underlying heap's count ... if we know that accurately. Otherwise
136  * this might just make matters worse.
137  */
138  if (!info->estimated_count)
139  {
140  if (gist_stats->stats.num_index_tuples > info->num_heap_tuples)
141  gist_stats->stats.num_index_tuples = info->num_heap_tuples;
142  }
143 
144  return (IndexBulkDeleteResult *) gist_stats;
145 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
bool analyze_only
Definition: genam.h:47
static void gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:164
MemoryContext page_set_context
Definition: gistvacuum.c:41
IntegerSet * internal_page_set
Definition: gistvacuum.c:39
static void gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats)
Definition: gistvacuum.c:459
static GistBulkDeleteResult * create_GistBulkDeleteResult(void)
Definition: gistvacuum.c:66
double num_heap_tuples
Definition: genam.h:51
IndexBulkDeleteResult stats
Definition: gistvacuum.c:32
double num_index_tuples
Definition: genam.h:77
bool estimated_count
Definition: genam.h:49
IntegerSet * empty_leaf_set
Definition: gistvacuum.c:40

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

References AMOPSTRATEGY, AMPROCNUM, check_amop_signature(), check_amproc_signature(), CLAOID, elog, ereport, errcode(), errmsg(), ERROR, format_operator(), format_procedure(), OpFamilyOpFuncGroup::functionset, get_op_rettype(), get_opfamily_proc(), GETSTRUCT, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_UNION_PROC, GISTNProcs, HeapTupleIsValid, i, identify_opfamily_groups(), INFO, OpFamilyOpFuncGroup::lefttype, lfirst, catclist::members, catclist::n_members, NameStr, ObjectIdGetDatum, OidIsValid, opfamily_can_sort_type(), OPFAMILYOID, ReleaseCatCacheList(), ReleaseSysCache(), OpFamilyOpFuncGroup::righttype, SearchSysCache1(), SearchSysCacheList1, and catctup::tuple.

Referenced by gisthandler().

35 {
36  bool result = true;
37  HeapTuple classtup;
38  Form_pg_opclass classform;
39  Oid opfamilyoid;
40  Oid opcintype;
41  Oid opckeytype;
42  char *opclassname;
43  HeapTuple familytup;
44  Form_pg_opfamily familyform;
45  char *opfamilyname;
46  CatCList *proclist,
47  *oprlist;
48  List *grouplist;
49  OpFamilyOpFuncGroup *opclassgroup;
50  int i;
51  ListCell *lc;
52 
53  /* Fetch opclass information */
54  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
55  if (!HeapTupleIsValid(classtup))
56  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
57  classform = (Form_pg_opclass) GETSTRUCT(classtup);
58 
59  opfamilyoid = classform->opcfamily;
60  opcintype = classform->opcintype;
61  opckeytype = classform->opckeytype;
62  if (!OidIsValid(opckeytype))
63  opckeytype = opcintype;
64  opclassname = NameStr(classform->opcname);
65 
66  /* Fetch opfamily information */
67  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
68  if (!HeapTupleIsValid(familytup))
69  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
70  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
71 
72  opfamilyname = NameStr(familyform->opfname);
73 
74  /* Fetch all operators and support functions of the opfamily */
75  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
76  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
77 
78  /* Check individual support functions */
79  for (i = 0; i < proclist->n_members; i++)
80  {
81  HeapTuple proctup = &proclist->members[i]->tuple;
82  Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
83  bool ok;
84 
85  /*
86  * All GiST support functions should be registered with matching
87  * left/right types
88  */
89  if (procform->amproclefttype != procform->amprocrighttype)
90  {
91  ereport(INFO,
92  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
93  errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
94  opfamilyname, "gist",
95  format_procedure(procform->amproc))));
96  result = false;
97  }
98 
99  /*
100  * We can't check signatures except within the specific opclass, since
101  * we need to know the associated opckeytype in many cases.
102  */
103  if (procform->amproclefttype != opcintype)
104  continue;
105 
106  /* Check procedure numbers and function signatures */
107  switch (procform->amprocnum)
108  {
110  ok = check_amproc_signature(procform->amproc, BOOLOID, false,
111  5, 5, INTERNALOID, opcintype,
112  INT2OID, OIDOID, INTERNALOID);
113  break;
114  case GIST_UNION_PROC:
115  ok = check_amproc_signature(procform->amproc, opckeytype, false,
116  2, 2, INTERNALOID, INTERNALOID);
117  break;
118  case GIST_COMPRESS_PROC:
120  case GIST_FETCH_PROC:
121  ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
122  1, 1, INTERNALOID);
123  break;
124  case GIST_PENALTY_PROC:
125  ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
126  3, 3, INTERNALOID,
127  INTERNALOID, INTERNALOID);
128  break;
129  case GIST_PICKSPLIT_PROC:
130  ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
131  2, 2, INTERNALOID, INTERNALOID);
132  break;
133  case GIST_EQUAL_PROC:
134  ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
135  3, 3, opckeytype, opckeytype,
136  INTERNALOID);
137  break;
138  case GIST_DISTANCE_PROC:
139  ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
140  5, 5, INTERNALOID, opcintype,
141  INT2OID, OIDOID, INTERNALOID);
142  break;
143  default:
144  ereport(INFO,
145  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
146  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
147  opfamilyname, "gist",
148  format_procedure(procform->amproc),
149  procform->amprocnum)));
150  result = false;
151  continue; /* don't want additional message */
152  }
153 
154  if (!ok)
155  {
156  ereport(INFO,
157  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
158  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
159  opfamilyname, "gist",
160  format_procedure(procform->amproc),
161  procform->amprocnum)));
162  result = false;
163  }
164  }
165 
166  /* Check individual operators */
167  for (i = 0; i < oprlist->n_members; i++)
168  {
169  HeapTuple oprtup = &oprlist->members[i]->tuple;
170  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
171  Oid op_rettype;
172 
173  /* TODO: Check that only allowed strategy numbers exist */
174  if (oprform->amopstrategy < 1)
175  {
176  ereport(INFO,
177  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
178  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
179  opfamilyname, "gist",
180  format_operator(oprform->amopopr),
181  oprform->amopstrategy)));
182  result = false;
183  }
184 
185  /* GiST supports ORDER BY operators */
186  if (oprform->amoppurpose != AMOP_SEARCH)
187  {
188  /* ... but must have matching distance proc */
189  if (!OidIsValid(get_opfamily_proc(opfamilyoid,
190  oprform->amoplefttype,
191  oprform->amoplefttype,
193  {
194  ereport(INFO,
195  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
196  errmsg("operator family \"%s\" of access method %s contains unsupported ORDER BY specification for operator %s",
197  opfamilyname, "gist",
198  format_operator(oprform->amopopr))));
199  result = false;
200  }
201  /* ... and operator result must match the claimed btree opfamily */
202  op_rettype = get_op_rettype(oprform->amopopr);
203  if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
204  {
205  ereport(INFO,
206  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
207  errmsg("operator family \"%s\" of access method %s contains incorrect ORDER BY opfamily specification for operator %s",
208  opfamilyname, "gist",
209  format_operator(oprform->amopopr))));
210  result = false;
211  }
212  }
213  else
214  {
215  /* Search operators must always return bool */
216  op_rettype = BOOLOID;
217  }
218 
219  /* Check operator signature */
220  if (!check_amop_signature(oprform->amopopr, op_rettype,
221  oprform->amoplefttype,
222  oprform->amoprighttype))
223  {
224  ereport(INFO,
225  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
226  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
227  opfamilyname, "gist",
228  format_operator(oprform->amopopr))));
229  result = false;
230  }
231  }
232 
233  /* Now check for inconsistent groups of operators/functions */
234  grouplist = identify_opfamily_groups(oprlist, proclist);
235  opclassgroup = NULL;
236  foreach(lc, grouplist)
237  {
238  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
239 
240  /* Remember the group exactly matching the test opclass */
241  if (thisgroup->lefttype == opcintype &&
242  thisgroup->righttype == opcintype)
243  opclassgroup = thisgroup;
244 
245  /*
246  * There is not a lot we can do to check the operator sets, since each
247  * GiST opclass is more or less a law unto itself, and some contain
248  * only operators that are binary-compatible with the opclass datatype
249  * (meaning that empty operator sets can be OK). That case also means
250  * that we shouldn't insist on nonempty function sets except for the
251  * opclass's own group.
252  */
253  }
254 
255  /* Check that the originally-named opclass is complete */
256  for (i = 1; i <= GISTNProcs; i++)
257  {
258  if (opclassgroup &&
259  (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
260  continue; /* got it */
261  if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
263  continue; /* optional methods */
264  ereport(INFO,
265  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
266  errmsg("operator class \"%s\" of access method %s is missing support function %d",
267  opclassname, "gist", i)));
268  result = false;
269  }
270 
271  ReleaseCatCacheList(proclist);
272  ReleaseCatCacheList(oprlist);
273  ReleaseSysCache(familytup);
274  ReleaseSysCache(classtup);
275 
276  return result;
277 }
int n_members
Definition: catcache.h:176
#define GIST_FETCH_PROC
Definition: gist.h:37
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
#define GIST_EQUAL_PROC
Definition: gist.h:35
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
int errcode(int sqlerrcode)
Definition: elog.c:570
#define INFO
Definition: elog.h:33
char * format_operator(Oid operator_oid)
Definition: regproc.c:820
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1140
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1782
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:150
#define GIST_PICKSPLIT_PROC
Definition: gist.h:34
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
#define GIST_COMPRESS_PROC
Definition: gist.h:31
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define ereport(elevel, rest)
Definition: elog.h:141
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define GIST_CONSISTENT_PROC
Definition: gist.h:29
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
#define GIST_UNION_PROC
Definition: gist.h:30
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:744
#define GIST_PENALTY_PROC
Definition: gist.h:33
#define GIST_DISTANCE_PROC
Definition: gist.h:36
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
#define GISTNProcs
Definition: gist.h:38
int errmsg(const char *fmt,...)
Definition: elog.c:784
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
#define elog(elevel,...)
Definition: elog.h:226
int i
#define NameStr(name)
Definition: c.h:609
HeapTupleData tuple
Definition: catcache.h:121
#define GIST_DECOMPRESS_PROC
Definition: gist.h:32
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
Definition: pg_list.h:50

◆ gistValidateBufferingOption()

void gistValidateBufferingOption ( const char *  value)

◆ gistXLogDelete()

XLogRecPtr gistXLogDelete ( Buffer  buffer,
OffsetNumber todelete,
int  ntodelete,
TransactionId  latestRemovedXid 
)

Definition at line 673 of file gistxlog.c.

References gistxlogDelete::latestRemovedXid, gistxlogDelete::ntodelete, REGBUF_STANDARD, SizeOfGistxlogDelete, XLOG_GIST_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistprunepage().

675 {
676  gistxlogDelete xlrec;
677  XLogRecPtr recptr;
678 
679  xlrec.latestRemovedXid = latestRemovedXid;
680  xlrec.ntodelete = ntodelete;
681 
682  XLogBeginInsert();
683  XLogRegisterData((char *) &xlrec, SizeOfGistxlogDelete);
684 
685  /*
686  * We need the target-offsets array whether or not we store the whole
687  * buffer, to allow us to find the latestRemovedXid on a standby server.
688  */
689  XLogRegisterData((char *) todelete, ntodelete * sizeof(OffsetNumber));
690 
692 
693  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_DELETE);
694 
695  return recptr;
696 }
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
uint16 OffsetNumber
Definition: off.h:24
uint16 ntodelete
Definition: gistxlog.h:52
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLOG_GIST_DELETE
Definition: gistxlog.h:21
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define SizeOfGistxlogDelete
Definition: gistxlog.h:59
TransactionId latestRemovedXid
Definition: gistxlog.h:51
void XLogBeginInsert(void)
Definition: xloginsert.c:120

◆ gistXLogPageDelete()

XLogRecPtr gistXLogPageDelete ( Buffer  buffer,
FullTransactionId  xid,
Buffer  parentBuffer,
OffsetNumber  downlinkOffset 
)

Definition at line 575 of file gistxlog.c.

References gistxlogPageDelete::deleteXid, gistxlogPageDelete::downlinkOffset, REGBUF_STANDARD, SizeOfGistxlogPageDelete, XLOG_GIST_PAGE_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistdeletepage().

577 {
578  gistxlogPageDelete xlrec;
579  XLogRecPtr recptr;
580 
581  xlrec.deleteXid = xid;
582  xlrec.downlinkOffset = downlinkOffset;
583 
584  XLogBeginInsert();
585  XLogRegisterData((char *) &xlrec, SizeOfGistxlogPageDelete);
586 
588  XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
589 
590  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
591 
592  return recptr;
593 }
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
FullTransactionId deleteXid
Definition: gistxlog.h:86
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define SizeOfGistxlogPageDelete
Definition: gistxlog.h:91
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define XLOG_GIST_PAGE_DELETE
Definition: gistxlog.h:28
void XLogBeginInsert(void)
Definition: xloginsert.c:120
OffsetNumber downlinkOffset
Definition: gistxlog.h:87

◆ gistXLogPageReuse()

void gistXLogPageReuse ( Relation  rel,
BlockNumber  blkno,
FullTransactionId  latestRemovedXid 
)

Definition at line 599 of file gistxlog.c.

References DataPageDeleteStack::blkno, gistxlogPageReuse::block, gistxlogPageReuse::latestRemovedFullXid, gistxlogPageReuse::node, RelationData::rd_node, SizeOfGistxlogPageReuse, XLOG_GIST_PAGE_REUSE, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by gistNewBuffer().

600 {
601  gistxlogPageReuse xlrec_reuse;
602 
603  /*
604  * Note that we don't register the buffer with the record, because this
605  * operation doesn't modify the page. This record only exists to provide a
606  * conflict point for Hot Standby.
607  */
608 
609  /* XLOG stuff */
610  xlrec_reuse.node = rel->rd_node;
611  xlrec_reuse.block = blkno;
612  xlrec_reuse.latestRemovedFullXid = latestRemovedXid;
613 
614  XLogBeginInsert();
615  XLogRegisterData((char *) &xlrec_reuse, SizeOfGistxlogPageReuse);
616 
617  XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_REUSE);
618 }
BlockNumber block
Definition: gistxlog.h:100
RelFileNode node
Definition: gistxlog.h:99
#define SizeOfGistxlogPageReuse
Definition: gistxlog.h:104
#define XLOG_GIST_PAGE_REUSE
Definition: gistxlog.h:23
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
RelFileNode rd_node
Definition: rel.h:54
void XLogBeginInsert(void)
Definition: xloginsert.c:120
FullTransactionId latestRemovedFullXid
Definition: gistxlog.h:101

◆ gistXLogSplit()

XLogRecPtr gistXLogSplit ( bool  page_is_leaf,
SplitedPageLayout dist,
BlockNumber  origrlink,
GistNSN