PostgreSQL Source Code  git master
gist_private.h File Reference
#include "access/amapi.h"
#include "access/gist.h"
#include "access/itup.h"
#include "fmgr.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)   (offsetof(GISTSearchItem, distances) + sizeof(double) * (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 struct GiSTOptions GiSTOptions
 

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)
 
bool gistplacetopage (Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markleftchild)
 
SplitedPageLayoutgistSplit (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ntup, Buffer leftchild)
 
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)
 
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 311 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ BUFFER_OVERFLOWED

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

Definition at line 319 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 436 of file gist_private.h.

Referenced by gistbuild().

◆ GIST_EXCLUSIVE

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 40 of file gist_private.h.

Referenced by gistplacetopage().

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 435 of file gist_private.h.

◆ GIST_ROOT_BLKNO

◆ GIST_SHARE

◆ GIST_UNLOCK

#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK

◆ GiSTPageSize

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

Definition at line 432 of file gist_private.h.

Referenced by gistfitpage(), and gistSplit().

◆ GISTSearchItemIsHeap

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

Definition at line 143 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 275 of file gist_private.h.

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

◆ GistTupleSetValid

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

Definition at line 276 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:126

Definition at line 306 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 58 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:56
#define IndexTupleSize(itup)
Definition: itup.h:71

Definition at line 60 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ SizeOfGISTSearchItem

#define SizeOfGISTSearchItem (   n_distances)    (offsetof(GISTSearchItem, distances) + sizeof(double) * (n_distances))

Definition at line 145 of file gist_private.h.

Referenced by gistScanPage().

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 273 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 272 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GiSTOptions

◆ GISTScanOpaque

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

◆ gistxlogPage

◆ SplitedPageLayout

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 113 of file gist.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

114 {
116  "GiST temporary context",
118 }
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1535 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

1536 {
1537  /* It's sufficient to delete the scanCxt */
1538  MemoryContextDelete(giststate->scanCxt);
1539 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
MemoryContext scanCxt
Definition: gist_private.h:78

◆ gistbuild()

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

Definition at line 114 of file gistbuild.c.

References Assert, buffer, BufferGetBlockNumber(), BufferGetPage, GISTBuildState::bufferingMode, GiSTOptions::bufferingModeOffset, 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_ROOT_BLKNO, gistBuildCallback(), gistEmptyAllBuffers(), gistFreeBuildBuffers(), gistGetFakeLSN(), GISTInitBuffer(), gistNewBuffer(), GISTBuildState::giststate, IndexBuildResult::heap_tuples, IndexBuildResult::index_tuples, IndexBuildHeapScan(), GISTBuildState::indexrel, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), PageSetLSN, palloc(), RelationData::rd_options, REGBUF_WILL_INIT, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, reltuples, START_CRIT_SECTION, GISTSTATE::tempCxt, UnlockReleaseBuffer(), XLOG_GIST_CREATE_INDEX, XLogBeginInsert(), XLogInsert(), and XLogRegisterBuffer().

Referenced by gisthandler().

115 {
116  IndexBuildResult *result;
117  double reltuples;
118  GISTBuildState buildstate;
119  Buffer buffer;
120  Page page;
122  int fillfactor;
123 
124  buildstate.indexrel = index;
125  if (index->rd_options)
126  {
127  /* Get buffering mode from the options string */
129  char *bufferingMode = (char *) options + options->bufferingModeOffset;
130 
131  if (strcmp(bufferingMode, "on") == 0)
132  buildstate.bufferingMode = GIST_BUFFERING_STATS;
133  else if (strcmp(bufferingMode, "off") == 0)
135  else
136  buildstate.bufferingMode = GIST_BUFFERING_AUTO;
137 
138  fillfactor = options->fillfactor;
139  }
140  else
141  {
142  /*
143  * By default, switch to buffering mode when the index grows too large
144  * to fit in cache.
145  */
146  buildstate.bufferingMode = GIST_BUFFERING_AUTO;
147  fillfactor = GIST_DEFAULT_FILLFACTOR;
148  }
149  /* Calculate target amount of free space to leave on pages */
150  buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
151 
152  /*
153  * We expect to be called exactly once for any index relation. If that's
154  * not the case, big trouble's what we have.
155  */
156  if (RelationGetNumberOfBlocks(index) != 0)
157  elog(ERROR, "index \"%s\" already contains data",
158  RelationGetRelationName(index));
159 
160  /* no locking is needed */
161  buildstate.giststate = initGISTstate(index);
162 
163  /*
164  * Create a temporary memory context that is reset once for each tuple
165  * processed. (Note: we don't bother to make this a child of the
166  * giststate's scanCxt, so we have to delete it separately at the end.)
167  */
168  buildstate.giststate->tempCxt = createTempGistContext();
169 
170  /* initialize the root page */
171  buffer = gistNewBuffer(index);
173  page = BufferGetPage(buffer);
174 
176 
177  GISTInitBuffer(buffer, F_LEAF);
178 
179  MarkBufferDirty(buffer);
180 
181  if (RelationNeedsWAL(index))
182  {
183  XLogRecPtr recptr;
184 
185  XLogBeginInsert();
187 
188  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_CREATE_INDEX);
189  PageSetLSN(page, recptr);
190  }
191  else
192  PageSetLSN(page, gistGetFakeLSN(heap));
193 
194  UnlockReleaseBuffer(buffer);
195 
197 
198  /* build the index */
199  buildstate.indtuples = 0;
200  buildstate.indtuplesSize = 0;
201 
202  /*
203  * Do the heap scan.
204  */
205  reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
206  gistBuildCallback, (void *) &buildstate, NULL);
207 
208  /*
209  * If buffering was used, flush out all the tuples that are still in the
210  * buffers.
211  */
212  if (buildstate.bufferingMode == GIST_BUFFERING_ACTIVE)
213  {
214  elog(DEBUG1, "all tuples processed, emptying buffers");
215  gistEmptyAllBuffers(&buildstate);
216  gistFreeBuildBuffers(buildstate.gfbb);
217  }
218 
219  /* okay, all heap tuples are indexed */
220  MemoryContextSwitchTo(oldcxt);
222 
223  freeGISTstate(buildstate.giststate);
224 
225  /*
226  * Return statistics
227  */
228  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
229 
230  result->heap_tuples = reltuples;
231  result->index_tuples = (double) buildstate.indtuples;
232 
233  return result;
234 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define DEBUG1
Definition: elog.h:25
int bufferingModeOffset
Definition: gist_private.h:377
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
MemoryContext createTempGistContext(void)
Definition: gist.c:113
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
#define XLOG_GIST_CREATE_INDEX
Definition: gistxlog.h:24
GistBufferingMode bufferingMode
Definition: gistbuild.c:74
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:980
int64 indtuplesSize
Definition: gistbuild.c:62
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
int fillfactor
Definition: pgbench.c:127
double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool allow_sync, IndexBuildCallback callback, void *callback_state, HeapScanDesc scan)
Definition: index.c:2418
int64 indtuples
Definition: gistbuild.c:61
MemoryContext tempCxt
Definition: gist_private.h:79
Size freespace
Definition: gistbuild.c:64
#define RelationGetRelationName(relation)
Definition: rel.h:441
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1535
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
GISTSTATE * giststate
Definition: gistbuild.c:59
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1436
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:992
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699
Relation indexrel
Definition: gistbuild.c:58
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define RelationNeedsWAL(relation)
Definition: rel.h:510
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
void * palloc(Size size)
Definition: mcxt.c:924
static void gistBuildCallback(Relation index, HeapTuple htup, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:458
#define F_LEAF
Definition: gist.h:42
GISTBuildBuffers * gfbb
Definition: gistbuild.c:71
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:721
#define elog
Definition: elog.h:219
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:782
float4 reltuples
Definition: pg_class.h:44
bytea * rd_options
Definition: rel.h:128
Pointer Page
Definition: bufpage.h:74
double index_tuples
Definition: genam.h:33
double heap_tuples
Definition: genam.h:32

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 124 of file gist.c.

References buffer, 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().

125 {
126  Buffer buffer;
127 
128  /* Initialize the root page */
129  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
131 
132  /* Initialize and xlog buffer */
134  GISTInitBuffer(buffer, F_LEAF);
135  MarkBufferDirty(buffer);
136  log_newpage_buffer(buffer, true);
138 
139  /* Unlock and release the buffer */
140  UnlockReleaseBuffer(buffer);
141 }
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1009
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
#define P_NEW
Definition: bufmgr.h:82
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:89
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define F_LEAF
Definition: gist.h:42
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:721
int Buffer
Definition: buf.h:23

◆ gistbulkdelete()

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

Definition at line 138 of file gistvacuum.c.

References GistBDItem::blkno, buffer, BufferGetLSNAtomic(), BufferGetPage, callback(), END_CRIT_SECTION, ereport, errdetail(), errhint(), errmsg(), IndexBulkDeleteResult::estimated_count, FirstOffsetNumber, GIST_EXCLUSIVE, GIST_ROOT_BLKNO, GIST_SHARE, GIST_UNLOCK, gistcheckpage(), gistGetFakeLSN(), GistMarkTuplesDeleted, GistPageIsLeaf, GistTupleIsInvalid, gistXLogUpdate(), i, IndexVacuumInfo::index, InvalidBuffer, ItemPointerGetBlockNumber, LockBuffer(), LOG, MAIN_FORKNUM, MarkBufferDirty(), MaxOffsetNumber, GistBDItem::next, IndexBulkDeleteResult::num_index_tuples, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIndexMultiDelete(), PageSetLSN, palloc(), palloc0(), GistBDItem::parentlsn, pfree(), pushStackIfSplited(), RBM_NORMAL, ReadBufferExtended(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexVacuumInfo::strategy, IndexTupleData::t_tid, IndexBulkDeleteResult::tuples_removed, UnlockReleaseBuffer(), and vacuum_delay_point().

Referenced by gisthandler().

140 {
141  Relation rel = info->index;
142  GistBDItem *stack,
143  *ptr;
144 
145  /* first time through? */
146  if (stats == NULL)
148  /* we'll re-count the tuples each time */
149  stats->estimated_count = false;
150  stats->num_index_tuples = 0;
151 
152  stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
153  stack->blkno = GIST_ROOT_BLKNO;
154 
155  while (stack)
156  {
157  Buffer buffer;
158  Page page;
159  OffsetNumber i,
160  maxoff;
161  IndexTuple idxtuple;
162  ItemId iid;
163 
164  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
165  RBM_NORMAL, info->strategy);
166  LockBuffer(buffer, GIST_SHARE);
167  gistcheckpage(rel, buffer);
168  page = (Page) BufferGetPage(buffer);
169 
170  if (GistPageIsLeaf(page))
171  {
172  OffsetNumber todelete[MaxOffsetNumber];
173  int ntodelete = 0;
174 
175  LockBuffer(buffer, GIST_UNLOCK);
176  LockBuffer(buffer, GIST_EXCLUSIVE);
177 
178  page = (Page) BufferGetPage(buffer);
179  if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
180  {
181  /* only the root can become non-leaf during relock */
182  UnlockReleaseBuffer(buffer);
183  /* one more check */
184  continue;
185  }
186 
187  /*
188  * check for split proceeded after look at parent, we should check
189  * it after relock
190  */
191  pushStackIfSplited(page, stack);
192 
193  /*
194  * Remove deletable tuples from page
195  */
196 
197  maxoff = PageGetMaxOffsetNumber(page);
198 
199  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
200  {
201  iid = PageGetItemId(page, i);
202  idxtuple = (IndexTuple) PageGetItem(page, iid);
203 
204  if (callback(&(idxtuple->t_tid), callback_state))
205  todelete[ntodelete++] = i;
206  else
207  stats->num_index_tuples += 1;
208  }
209 
210  stats->tuples_removed += ntodelete;
211 
212  if (ntodelete)
213  {
215 
216  MarkBufferDirty(buffer);
217 
218  PageIndexMultiDelete(page, todelete, ntodelete);
219  GistMarkTuplesDeleted(page);
220 
221  if (RelationNeedsWAL(rel))
222  {
223  XLogRecPtr recptr;
224 
225  recptr = gistXLogUpdate(buffer,
226  todelete, ntodelete,
227  NULL, 0, InvalidBuffer);
228  PageSetLSN(page, recptr);
229  }
230  else
231  PageSetLSN(page, gistGetFakeLSN(rel));
232 
234  }
235 
236  }
237  else
238  {
239  /* check for split proceeded after look at parent */
240  pushStackIfSplited(page, stack);
241 
242  maxoff = PageGetMaxOffsetNumber(page);
243 
244  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
245  {
246  iid = PageGetItemId(page, i);
247  idxtuple = (IndexTuple) PageGetItem(page, iid);
248 
249  ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
250  ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
251  ptr->parentlsn = BufferGetLSNAtomic(buffer);
252  ptr->next = stack->next;
253  stack->next = ptr;
254 
255  if (GistTupleIsInvalid(idxtuple))
256  ereport(LOG,
257  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
259  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
260  errhint("Please REINDEX it.")));
261  }
262  }
263 
264  UnlockReleaseBuffer(buffer);
265 
266  ptr = stack->next;
267  pfree(stack);
268  stack = ptr;
269 
271  }
272 
273  return stats;
274 }
int errhint(const char *fmt,...)
Definition: elog.c:987
double tuples_removed
Definition: genam.h:77
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
#define MaxOffsetNumber
Definition: off.h:28
#define GistMarkTuplesDeleted(page)
Definition: gist.h:140
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
ItemPointerData t_tid
Definition: itup.h:37
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
BufferAccessStrategy strategy
Definition: genam.h:51
#define InvalidBuffer
Definition: buf.h:25
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
Relation index
Definition: genam.h:46
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:275
#define GIST_UNLOCK
Definition: gist_private.h:45
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
GistNSN parentlsn
Definition: gistvacuum.c:103
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:980
uint16 OffsetNumber
Definition: off.h:24
void pfree(void *pointer)
Definition: mcxt.c:1031
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:2832
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define RelationGetRelationName(relation)
Definition: rel.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define GistPageIsLeaf(page)
Definition: gist.h:132
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
void * palloc0(Size size)
Definition: mcxt.c:955
BlockNumber blkno
Definition: gistvacuum.c:104
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:457
uint64 XLogRecPtr
Definition: xlogdefs.h:21
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:832
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:743
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
#define GIST_SHARE
Definition: gist_private.h:43
#define RelationNeedsWAL(relation)
Definition: rel.h:510
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
struct GistBDItem * next
Definition: gistvacuum.c:105
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
void vacuum_delay_point(void)
Definition: vacuum.c:1672
#define GIST_EXCLUSIVE
Definition: gist_private.h:44
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
double num_index_tuples
Definition: genam.h:76
int Buffer
Definition: buf.h:23
bool estimated_count
Definition: genam.h:75
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
static void pushStackIfSplited(Page page, GistBDItem *stack)
Definition: gistvacuum.c:109

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 810 of file gistget.c.

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

Referenced by gisthandler().

811 {
812  if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
814  return true;
815  else
816  return false;
817 }
#define GIST_FETCH_PROC
Definition: gist.h:36
#define OidIsValid(objectId)
Definition: c.h:605
#define GIST_COMPRESS_PROC
Definition: gist.h:30
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:821

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 743 of file gistutil.c.

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

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

744 {
745  Page page = BufferGetPage(buf);
746 
747  /*
748  * ReadBuffer verifies that every newly-read page passes
749  * PageHeaderIsValid, which means it either contains a reasonably sane
750  * page header or is all-zero. We have to defend against the all-zero
751  * case, however.
752  */
753  if (PageIsNew(page))
754  ereport(ERROR,
755  (errcode(ERRCODE_INDEX_CORRUPTED),
756  errmsg("index \"%s\" contains unexpected zero page at block %u",
759  errhint("Please REINDEX it.")));
760 
761  /*
762  * Additionally check that the special area looks sane.
763  */
764  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
765  ereport(ERROR,
766  (errcode(ERRCODE_INDEX_CORRUPTED),
767  errmsg("index \"%s\" contains corrupted page at block %u",
770  errhint("Please REINDEX it.")));
771 }
int errhint(const char *fmt,...)
Definition: elog.c:987
int errcode(int sqlerrcode)
Definition: elog.c:575
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:67
#define RelationGetRelationName(relation)
Definition: rel.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define MAXALIGN(LEN)
Definition: c.h:652
#define PageGetSpecialSize(page)
Definition: bufpage.h:296
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define PageIsNew(page)
Definition: bufpage.h:225
int errmsg(const char *fmt,...)
Definition: elog.c:797
Pointer Page
Definition: bufpage.h:74

◆ gistchoose()

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

Definition at line 372 of file gistutil.c.

References Assert, FirstOffsetNumber, gistDeCompressAtt(), gistdentryinit(), GistPageIsLeaf, gistpenalty(), i, index_getattr, INDEX_MAX_KEYS, MAX_RANDOM_VALUE, tupleDesc::natts, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, random(), RelationData::rd_att, and GISTSTATE::tupdesc.

Referenced by gistdoinsert(), and gistProcessItup().

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

◆ gistDeCompressAtt()

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

Definition at line 294 of file gistutil.c.

References gistdentryinit(), i, index_getattr, tupleDesc::natts, RelationData::rd_att, and GISTSTATE::tupdesc.

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

296 {
297  int i;
298 
299  for (i = 0; i < r->rd_att->natts; i++)
300  {
301  Datum datum;
302 
303  datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
304  gistdentryinit(giststate, i, &attdata[i],
305  datum, r, p, o,
306  false, isnull[i]);
307  }
308 }
TupleDesc tupdesc
Definition: gist_private.h:81
int natts
Definition: tupdesc.h:82
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:544
uintptr_t Datum
Definition: postgres.h:367
TupleDesc rd_att
Definition: rel.h:85
#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 544 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().

547 {
548  if (!isNull)
549  {
550  GISTENTRY *dep;
551 
552  gistentryinit(*e, k, r, pg, o, l);
553 
554  /* there may not be a decompress function in opclass */
555  if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
556  return;
557 
558  dep = (GISTENTRY *)
560  giststate->supportCollation[nkey],
561  PointerGetDatum(e)));
562  /* decompressFn may just return the given pointer */
563  if (dep != e)
564  gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
565  dep->leafkey);
566  }
567  else
568  gistentryinit(*e, (Datum) 0, r, pg, o, l);
569 }
Relation rel
Definition: gist.h:124
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:96
#define PointerGetDatum(X)
Definition: postgres.h:541
#define OidIsValid(objectId)
Definition: c.h:605
Page page
Definition: gist.h:125
Datum key
Definition: gist.h:123
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
bool leafkey
Definition: gist.h:127
uintptr_t Datum
Definition: postgres.h:367
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1113
Oid fn_oid
Definition: fmgr.h:59
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:169
#define DatumGetPointer(X)
Definition: postgres.h:534
OffsetNumber offset
Definition: gist.h:126

◆ gistdoinsert()

void gistdoinsert ( Relation  r,
IndexTuple  itup,
Size  freespace,
GISTSTATE GISTstate 
)

Definition at line 607 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, GistPageIsLeaf, GistTupleIsInvalid, InvalidOffsetNumber, ItemPointerGetBlockNumber, LockBuffer(), GISTInsertStack::lsn, GISTInsertStack::page, PageGetItem, PageGetItemId, PageGetLSN, palloc0(), GISTInsertStack::parent, GISTInsertState::r, ReadBuffer(), RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), GISTInsertState::stack, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogRecPtrIsInvalid.

Referenced by gistBuildCallback(), and gistinsert().

608 {
609  ItemId iid;
610  IndexTuple idxtuple;
611  GISTInsertStack firststack;
612  GISTInsertStack *stack;
614  bool xlocked = false;
615 
616  memset(&state, 0, sizeof(GISTInsertState));
617  state.freespace = freespace;
618  state.r = r;
619 
620  /* Start from the root */
621  firststack.blkno = GIST_ROOT_BLKNO;
622  firststack.lsn = 0;
623  firststack.parent = NULL;
624  firststack.downlinkoffnum = InvalidOffsetNumber;
625  state.stack = stack = &firststack;
626 
627  /*
628  * Walk down along the path of smallest penalty, updating the parent
629  * pointers with the key we're inserting as we go. If we crash in the
630  * middle, the tree is consistent, although the possible parent updates
631  * were a waste.
632  */
633  for (;;)
634  {
635  if (XLogRecPtrIsInvalid(stack->lsn))
636  stack->buffer = ReadBuffer(state.r, stack->blkno);
637 
638  /*
639  * Be optimistic and grab shared lock first. Swap it for an exclusive
640  * lock later if we need to update the page.
641  */
642  if (!xlocked)
643  {
644  LockBuffer(stack->buffer, GIST_SHARE);
645  gistcheckpage(state.r, stack->buffer);
646  }
647 
648  stack->page = (Page) BufferGetPage(stack->buffer);
649  stack->lsn = xlocked ?
650  PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
651  Assert(!RelationNeedsWAL(state.r) || !XLogRecPtrIsInvalid(stack->lsn));
652 
653  /*
654  * If this page was split but the downlink was never inserted to the
655  * parent because the inserting backend crashed before doing that, fix
656  * that now.
657  */
658  if (GistFollowRight(stack->page))
659  {
660  if (!xlocked)
661  {
662  LockBuffer(stack->buffer, GIST_UNLOCK);
664  xlocked = true;
665  /* someone might've completed the split when we unlocked */
666  if (!GistFollowRight(stack->page))
667  continue;
668  }
669  gistfixsplit(&state, giststate);
670 
671  UnlockReleaseBuffer(stack->buffer);
672  xlocked = false;
673  state.stack = stack = stack->parent;
674  continue;
675  }
676 
677  if (stack->blkno != GIST_ROOT_BLKNO &&
678  stack->parent->lsn < GistPageGetNSN(stack->page))
679  {
680  /*
681  * Concurrent split detected. There's no guarantee that the
682  * downlink for this page is consistent with the tuple we're
683  * inserting anymore, so go back to parent and rechoose the best
684  * child.
685  */
686  UnlockReleaseBuffer(stack->buffer);
687  xlocked = false;
688  state.stack = stack = stack->parent;
689  continue;
690  }
691 
692  if (!GistPageIsLeaf(stack->page))
693  {
694  /*
695  * This is an internal page so continue to walk down the tree.
696  * Find the child node that has the minimum insertion penalty.
697  */
698  BlockNumber childblkno;
699  IndexTuple newtup;
700  GISTInsertStack *item;
701  OffsetNumber downlinkoffnum;
702 
703  downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
704  iid = PageGetItemId(stack->page, downlinkoffnum);
705  idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
706  childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
707 
708  /*
709  * Check that it's not a leftover invalid tuple from pre-9.1
710  */
711  if (GistTupleIsInvalid(idxtuple))
712  ereport(ERROR,
713  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
715  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
716  errhint("Please REINDEX it.")));
717 
718  /*
719  * Check that the key representing the target child node is
720  * consistent with the key we're inserting. Update it if it's not.
721  */
722  newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
723  if (newtup)
724  {
725  /*
726  * Swap shared lock for an exclusive one. Beware, the page may
727  * change while we unlock/lock the page...
728  */
729  if (!xlocked)
730  {
731  LockBuffer(stack->buffer, GIST_UNLOCK);
733  xlocked = true;
734  stack->page = (Page) BufferGetPage(stack->buffer);
735 
736  if (PageGetLSN(stack->page) != stack->lsn)
737  {
738  /* the page was changed while we unlocked it, retry */
739  continue;
740  }
741  }
742 
743  /*
744  * Update the tuple.
745  *
746  * We still hold the lock after gistinserttuple(), but it
747  * might have to split the page to make the updated tuple fit.
748  * In that case the updated tuple might migrate to the other
749  * half of the split, so we have to go back to the parent and
750  * descend back to the half that's a better fit for the new
751  * tuple.
752  */
753  if (gistinserttuple(&state, stack, giststate, newtup,
754  downlinkoffnum))
755  {
756  /*
757  * If this was a root split, the root page continues to be
758  * the parent and the updated tuple went to one of the
759  * child pages, so we just need to retry from the root
760  * page.
761  */
762  if (stack->blkno != GIST_ROOT_BLKNO)
763  {
764  UnlockReleaseBuffer(stack->buffer);
765  xlocked = false;
766  state.stack = stack = stack->parent;
767  }
768  continue;
769  }
770  }
771  LockBuffer(stack->buffer, GIST_UNLOCK);
772  xlocked = false;
773 
774  /* descend to the chosen child */
775  item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
776  item->blkno = childblkno;
777  item->parent = stack;
778  item->downlinkoffnum = downlinkoffnum;
779  state.stack = stack = item;
780  }
781  else
782  {
783  /*
784  * Leaf page. Insert the new key. We've already updated all the
785  * parents on the way down, but we might have to split the page if
786  * it doesn't fit. gistinserthere() will take care of that.
787  */
788 
789  /*
790  * Swap shared lock for an exclusive one. Be careful, the page may
791  * change while we unlock/lock the page...
792  */
793  if (!xlocked)
794  {
795  LockBuffer(stack->buffer, GIST_UNLOCK);
797  xlocked = true;
798  stack->page = (Page) BufferGetPage(stack->buffer);
799  stack->lsn = PageGetLSN(stack->page);
800 
801  if (stack->blkno == GIST_ROOT_BLKNO)
802  {
803  /*
804  * the only page that can become inner instead of leaf is
805  * the root page, so for root we should recheck it
806  */
807  if (!GistPageIsLeaf(stack->page))
808  {
809  /*
810  * very rare situation: during unlock/lock index with
811  * number of pages = 1 was increased
812  */
813  LockBuffer(stack->buffer, GIST_UNLOCK);
814  xlocked = false;
815  continue;
816  }
817 
818  /*
819  * we don't need to check root split, because checking
820  * leaf/inner is enough to recognize split for root
821  */
822  }
823  else if (GistFollowRight(stack->page) ||
824  stack->parent->lsn < GistPageGetNSN(stack->page))
825  {
826  /*
827  * The page was split while we momentarily unlocked the
828  * page. Go back to parent.
829  */
830  UnlockReleaseBuffer(stack->buffer);
831  xlocked = false;
832  state.stack = stack = stack->parent;
833  continue;
834  }
835  }
836 
837  /* now state.stack->(page, buffer and blkno) points to leaf page */
838 
839  gistinserttuple(&state, stack, giststate, itup,
841  LockBuffer(stack->buffer, GIST_UNLOCK);
842 
843  /* Release any pins we might still hold before exiting */
844  for (; stack; stack = stack->parent)
845  ReleaseBuffer(stack->buffer);
846  break;
847  }
848  }
849 }
#define GistFollowRight(page)
Definition: gist.h:147
BlockNumber blkno
Definition: gist_private.h:206
#define GistPageGetNSN(page)
Definition: gist.h:151
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1179
int errhint(const char *fmt,...)
Definition: elog.c:987
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1120
ItemPointerData t_tid
Definition: itup.h:37
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:275
#define GIST_UNLOCK
Definition: gist_private.h:45
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:314
uint16 OffsetNumber
Definition: off.h:24
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
GISTInsertStack * stack
Definition: gist_private.h:245
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:2832
IndexTupleData * IndexTuple
Definition: itup.h:53
int errdetail(const char *fmt,...)
Definition: elog.c:873
#define RelationGetRelationName(relation)
Definition: rel.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:372
#define GistPageIsLeaf(page)
Definition: gist.h:132
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
OffsetNumber downlinkoffnum
Definition: gist_private.h:217
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
void * palloc0(Size size)
Definition: mcxt.c:955
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:699
Definition: regguts.h:298
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:743
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define GIST_SHARE
Definition: gist_private.h:43
#define RelationNeedsWAL(relation)
Definition: rel.h:510
#define PageGetLSN(page)
Definition: bufpage.h:362
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
#define GIST_EXCLUSIVE
Definition: gist_private.h:44
struct GISTInsertStack * parent
Definition: gist_private.h:220
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 94 of file gistutil.c.

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

Referenced by gistplacetopage().

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

◆ gistFetchTuple()

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

Definition at line 640 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, InvalidOid, MemoryContextSwitchTo(), tupleDesc::natts, RelationData::rd_att, GISTSTATE::tempCxt, and GISTSTATE::tupdesc.

Referenced by gistScanPage().

641 {
642  MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
644  bool isnull[INDEX_MAX_KEYS];
645  int i;
646 
647  for (i = 0; i < r->rd_att->natts; i++)
648  {
649  Datum datum;
650 
651  datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
652 
653  if (giststate->fetchFn[i].fn_oid != InvalidOid)
654  {
655  if (!isnull[i])
656  fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
657  else
658  fetchatt[i] = (Datum) 0;
659  }
660  else if (giststate->compressFn[i].fn_oid == InvalidOid)
661  {
662  /*
663  * If opclass does not provide compress method that could change
664  * original value, att is necessarily stored in original form.
665  */
666  if (!isnull[i])
667  fetchatt[i] = datum;
668  else
669  fetchatt[i] = (Datum) 0;
670  }
671  else
672  {
673  /*
674  * Index-only scans not supported for this column. Since the
675  * planner chose an index-only scan anyway, it is not interested
676  * in this column, and we can replace it with a NULL.
677  */
678  isnull[i] = true;
679  fetchatt[i] = (Datum) 0;
680  }
681  }
682  MemoryContextSwitchTo(oldcxt);
683 
684  return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
685 }
TupleDesc tupdesc
Definition: gist_private.h:81
static Datum gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
Definition: gistutil.c:619
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1074
int natts
Definition: tupdesc.h:82
#define fetchatt(A, T)
Definition: tupmacs.h:37
MemoryContext tempCxt
Definition: gist_private.h:79
uintptr_t Datum
Definition: postgres.h:367
TupleDesc rd_att
Definition: rel.h:85
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:59
TupleDesc fetchTupdesc
Definition: gist_private.h:82
#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 33 of file gistutil.c.

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

Referenced by gistplacetopage(), and gistRedoPageSplitRecord().

34 {
36  int i;
37 
38  if (off == InvalidOffsetNumber)
39  off = (PageIsEmpty(page)) ? FirstOffsetNumber :
41 
42  for (i = 0; i < len; i++)
43  {
44  Size sz = IndexTupleSize(itup[i]);
45 
46  l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
47  if (l == InvalidOffsetNumber)
48  elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
49  i, len, (int) sz);
50  off++;
51  }
52 }
#define PageIsEmpty(page)
Definition: bufpage.h:218
Pointer Item
Definition: item.h:17
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
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:53
size_t Size
Definition: c.h:433
int i
#define elog
Definition: elog.h:219
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistfillitupvec()

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

Definition at line 126 of file gistutil.c.

References i, IndexTupleSize, and palloc().

Referenced by gistplacetopage(), and gistSplit().

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

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 78 of file gistutil.c.

References GiSTPageSize, i, and IndexTupleSize.

Referenced by gistSplit().

79 {
80  int i;
81  Size size = 0;
82 
83  for (i = 0; i < len; i++)
84  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
85 
86  /* TODO: Consider fillfactor */
87  return (size <= GiSTPageSize);
88 }
struct ItemIdData ItemIdData
#define GiSTPageSize
Definition: gist_private.h:432
size_t Size
Definition: c.h:433
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 511 of file gistbuildbuffers.c.

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

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

◆ gistgetadjusted()

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

Definition at line 314 of file gistutil.c.

References gistDeCompressAtt(), gistFormTuple(), gistKeyIsEQ(), gistMakeUnionKey(), i, INDEX_MAX_KEYS, tupleDesc::natts, RelationData::rd_att, and IndexTupleData::t_tid.

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 761 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().

762 {
763  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
764  int64 ntids = 0;
765  GISTSearchItem fakeItem;
766 
767  if (!so->qual_ok)
768  return 0;
769 
771 
772  /* Begin the scan by processing the root page */
773  so->curPageData = so->nPageData = 0;
774  scan->xs_hitup = NULL;
775  if (so->pageDataCxt)
777 
778  fakeItem.blkno = GIST_ROOT_BLKNO;
779  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
780  gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
781 
782  /*
783  * While scanning a leaf page, ItemPointers of matching heap tuples will
784  * be stored directly into tbm, so we don't need to deal with them here.
785  */
786  for (;;)
787  {
789 
790  if (!item)
791  break;
792 
794 
795  gistScanPage(scan, item, item->distances, tbm, &ntids);
796 
797  pfree(item);
798  }
799 
800  return ntids;
801 }
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
MemoryContext pageDataCxt
Definition: gist_private.h:173
Relation indexRelation
Definition: relscan.h:91
double distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:139
void pfree(void *pointer)
Definition: mcxt.c:1031
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:324
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:177
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1291
OffsetNumber nPageData
Definition: gist_private.h:171
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:520
OffsetNumber curPageData
Definition: gist_private.h:172
XLogRecPtr GistNSN
Definition: gist.h:50
HeapTuple xs_hitup
Definition: relscan.h:117
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 980 of file gistutil.c.

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

Referenced by gistbuild(), gistbulkdelete(), gistplacetopage(), and gistvacuumpage().

981 {
982  static XLogRecPtr counter = 1;
983 
984  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
985  {
986  /*
987  * Temporary relations are only accessible in our session, so a simple
988  * backend-local counter will do.
989  */
990  return counter++;
991  }
992  else
993  {
994  /*
995  * Unlogged relations are accessible from other backends, and survive
996  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
997  */
998  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
999  return GetFakeLSNForUnloggedRel();
1000  }
1001 }
Form_pg_class rd_rel
Definition: rel.h:84
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4777
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699

◆ 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, 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->level = level;
142 
143  /*
144  * Add this buffer to the list of buffers on this level. Enlarge
145  * buffersOnLevels array if needed.
146  */
147  if (level >= gfbb->buffersOnLevelsLen)
148  {
149  int i;
150 
151  gfbb->buffersOnLevels =
152  (List **) repalloc(gfbb->buffersOnLevels,
153  (level + 1) * sizeof(List *));
154 
155  /* initialize the enlarged portion */
156  for (i = gfbb->buffersOnLevelsLen; i <= level; i++)
157  gfbb->buffersOnLevels[i] = NIL;
158  gfbb->buffersOnLevelsLen = level + 1;
159  }
160 
161  /*
162  * Prepend the new buffer to the list of buffers on this level. It's
163  * not arbitrary that the new buffer is put to the beginning of the
164  * list: in the final emptying phase we loop through all buffers at
165  * each level, and flush them. If a page is split during the emptying,
166  * it's more efficient to flush the new splitted pages first, before
167  * moving on to pre-existing pages on the level. The buffers just
168  * created during the page split are likely still in cache, so
169  * flushing them immediately is more efficient than putting them to
170  * the end of the queue.
171  */
172  gfbb->buffersOnLevels[level] = lcons(nodeBuffer,
173  gfbb->buffersOnLevels[level]);
174 
175  MemoryContextSwitchTo(oldcxt);
176  }
177 
178  return nodeBuffer;
179 }
#define NIL
Definition: pg_list.h:69
BlockNumber pageBlocknum
Definition: gist_private.h:290
MemoryContext context
Definition: gist_private.h:328
List ** buffersOnLevels
Definition: gist_private.h:355
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:291
bool queuedForEmptying
Definition: gist_private.h:294
List * lcons(void *datum, List *list)
Definition: list.c:259
#define InvalidBlockNumber
Definition: block.h:33
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1044
int i
Definition: pg_list.h:45

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 630 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, HeapTupleData::t_self, IndexScanDescData::xs_ctup, IndexScanDescData::xs_hitup, IndexScanDescData::xs_recheck, and IndexScanDescData::xs_want_itup.

Referenced by gisthandler().

631 {
632  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
633 
634  if (dir != ForwardScanDirection)
635  elog(ERROR, "GiST only supports forward scan direction");
636 
637  if (!so->qual_ok)
638  return false;
639 
640  if (so->firstCall)
641  {
642  /* Begin the scan by processing the root page */
643  GISTSearchItem fakeItem;
644 
646 
647  so->firstCall = false;
648  so->curPageData = so->nPageData = 0;
649  scan->xs_hitup = NULL;
650  if (so->pageDataCxt)
652 
653  fakeItem.blkno = GIST_ROOT_BLKNO;
654  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
655  gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
656  }
657 
658  if (scan->numberOfOrderBys > 0)
659  {
660  /* Must fetch tuples in strict distance order */
661  return getNextNearest(scan);
662  }
663  else
664  {
665  /* Fetch tuples index-page-at-a-time */
666  for (;;)
667  {
668  if (so->curPageData < so->nPageData)
669  {
670  if (scan->kill_prior_tuple && so->curPageData > 0)
671  {
672 
673  if (so->killedItems == NULL)
674  {
675  MemoryContext oldCxt =
677 
678  so->killedItems =
680  * sizeof(OffsetNumber));
681 
682  MemoryContextSwitchTo(oldCxt);
683  }
685  so->killedItems[so->numKilled++] =
686  so->pageData[so->curPageData - 1].offnum;
687  }
688  /* continuing to return tuples from a leaf page */
689  scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
690  scan->xs_recheck = so->pageData[so->curPageData].recheck;
691 
692  /* in an index-only scan, also return the reconstructed tuple */
693  if (scan->xs_want_itup)
694  scan->xs_hitup = so->pageData[so->curPageData].recontup;
695 
696  so->curPageData++;
697 
698  return true;
699  }
700 
701  /*
702  * Check the last returned tuple and add it to killitems if
703  * necessary
704  */
705  if (scan->kill_prior_tuple
706  && so->curPageData > 0
707  && so->curPageData == so->nPageData)
708  {
709 
710  if (so->killedItems == NULL)
711  {
712  MemoryContext oldCxt =
714 
715  so->killedItems =
717  * sizeof(OffsetNumber));
718 
719  MemoryContextSwitchTo(oldCxt);
720  }
722  so->killedItems[so->numKilled++] =
723  so->pageData[so->curPageData - 1].offnum;
724  }
725  /* find and process the next index page */
726  do
727  {
728  GISTSearchItem *item;
729 
730  if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
731  gistkillitems(scan);
732 
733  item = getNextGISTSearchItem(so);
734 
735  if (!item)
736  return false;
737 
739 
740  /* save current item BlockNumber for next gistkillitems() call */
741  so->curBlkno = item->blkno;
742 
743  /*
744  * While scanning a leaf page, ItemPointers of matching heap
745  * tuples are stored in so->pageData. If there are any on
746  * this page, we fall out of the inner "do" and loop around to
747  * return them.
748  */
749  gistScanPage(scan, item, item->distances, NULL, NULL);
750 
751  pfree(item);
752  } while (so->nPageData == 0);
753  }
754  }
755 }
BlockNumber blkno
Definition: gist_private.h:132
OffsetNumber * killedItems
Definition: gist_private.h:164
BlockNumber curBlkno
Definition: gist_private.h:166
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
MemoryContext pageDataCxt
Definition: gist_private.h:173
Relation indexRelation
Definition: relscan.h:91
uint16 OffsetNumber
Definition: off.h:24
GISTSTATE * giststate
Definition: gist_private.h:152
double distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:139
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ERROR
Definition: elog.h:43
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:324
ItemPointerData t_self
Definition: htup.h:65
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:177
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1291
OffsetNumber nPageData
Definition: gist_private.h:171
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:520
union GISTSearchItem::@43 data
bool xs_want_itup
Definition: relscan.h:97
ItemPointerData heapPtr
Definition: gist_private.h:119
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:542
HeapTupleData xs_ctup
Definition: relscan.h:121
GISTSearchHeapItem pageData[BLCKSZ/sizeof(IndexTupleData)]
Definition: gist_private.h:170
#define InvalidBlockNumber
Definition: block.h:33
OffsetNumber curPageData
Definition: gist_private.h:172
XLogRecPtr GistNSN
Definition: gist.h:50
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:39
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:924
HeapTuple xs_hitup
Definition: relscan.h:117
OffsetNumber offnum
Definition: gist_private.h:124
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
bool kill_prior_tuple
Definition: relscan.h:101
MemoryContext scanCxt
Definition: gist_private.h:78
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
int numberOfOrderBys
Definition: relscan.h:94
#define elog
Definition: elog.h:219
GistNSN parentlsn
Definition: gist_private.h:135

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 721 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(), gistRedoCreateIndex(), and gistRedoPageSplitRecord().

722 {
723  GISTPageOpaque opaque;
724  Page page;
725  Size pageSize;
726 
727  pageSize = BufferGetPageSize(b);
728  page = BufferGetPage(b);
729  PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
730 
731  opaque = GistPageGetOpaque(page);
732  /* page was already zeroed by PageInit, so this is not needed: */
733  /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
734  opaque->rightlink = InvalidBlockNumber;
735  opaque->flags = f;
736  opaque->gist_page_id = GIST_PAGE_ID;
737 }
uint16 gist_page_id
Definition: gist.h:63
uint16 flags
Definition: gist.h:62
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:147
#define GistPageGetOpaque(page)
Definition: gist.h:130
size_t Size
Definition: c.h:433
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber rightlink
Definition: gist.h:61
#define GIST_PAGE_ID
Definition: gist.h:74
Pointer Page
Definition: bufpage.h:74
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:41

◆ 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:69
#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:328
List ** buffersOnLevels
Definition: gist_private.h:355
Size entrysize
Definition: hsearch.h:73
uint32 BlockNumber
Definition: block.h:31
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:193
List * bufferEmptyingQueue
Definition: gist_private.h:344
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:362
void * palloc(Size size)
Definition: mcxt.c:924
Definition: pg_list.h:45

◆ gistinsert()

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

Definition at line 150 of file gist.c.

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

Referenced by gisthandler().

154 {
155  GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
156  IndexTuple itup;
157  MemoryContext oldCxt;
158 
159  /* Initialize GISTSTATE cache if first call in this statement */
160  if (giststate == NULL)
161  {
162  oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
163  giststate = initGISTstate(r);
164  giststate->tempCxt = createTempGistContext();
165  indexInfo->ii_AmCache = (void *) giststate;
166  MemoryContextSwitchTo(oldCxt);
167  }
168 
169  oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
170 
171  itup = gistFormTuple(giststate, r,
172  values, isnull, true /* size is currently bogus */ );
173  itup->t_tid = *ht_ctid;
174 
175  gistdoinsert(r, itup, 0, giststate);
176 
177  /* cleanup */
178  MemoryContextSwitchTo(oldCxt);
179  MemoryContextReset(giststate->tempCxt);
180 
181  return false;
182 }
MemoryContext ii_Context
Definition: execnodes.h:171
MemoryContext createTempGistContext(void)
Definition: gist.c:113
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)
Definition: gist.c:607
MemoryContext tempCxt
Definition: gist_private.h:79
void * ii_AmCache
Definition: execnodes.h:170
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1436
static Datum values[MAXATTR]
Definition: bootstrap.c:164
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:572

◆ gistjoinvector()

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

Definition at line 113 of file gistutil.c.

References memmove, and repalloc().

Referenced by gistplacetopage().

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

◆ gistKeyIsEQ()

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

Definition at line 279 of file gistutil.c.

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

Referenced by gistgetadjusted(), and gistUserPicksplit().

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

◆ gistMakeUnionItVec()

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

Definition at line 154 of file gistutil.c.

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

Referenced by gistunion(), and gistunionsubkeyvec().

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

◆ gistMakeUnionKey()

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

Definition at line 231 of file gistutil.c.

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

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

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 782 of file gistutil.c.

References DataPageDeleteStack::blkno, buffer, BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), GIST_EXCLUSIVE, GIST_UNLOCK, gistcheckpage(), GistPageIsDeleted, InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), and UnlockRelationForExtension().

Referenced by gistbuild(), and gistplacetopage().

783 {
784  Buffer buffer;
785  bool needLock;
786 
787  /* First, try to get a page from FSM */
788  for (;;)
789  {
790  BlockNumber blkno = GetFreeIndexPage(r);
791 
792  if (blkno == InvalidBlockNumber)
793  break; /* nothing left in FSM */
794 
795  buffer = ReadBuffer(r, blkno);
796 
797  /*
798  * We have to guard against the possibility that someone else already
799  * recycled this page; the buffer may be locked if so.
800  */
801  if (ConditionalLockBuffer(buffer))
802  {
803  Page page = BufferGetPage(buffer);
804 
805  if (PageIsNew(page))
806  return buffer; /* OK to use, if never initialized */
807 
808  gistcheckpage(r, buffer);
809 
810  if (GistPageIsDeleted(page))
811  return buffer; /* OK to use */
812 
813  LockBuffer(buffer, GIST_UNLOCK);
814  }
815 
816  /* Can't use it, so release buffer and try again */
817  ReleaseBuffer(buffer);
818  }
819 
820  /* Must extend the file */
821  needLock = !RELATION_IS_LOCAL(r);
822 
823  if (needLock)
825 
826  buffer = ReadBuffer(r, P_NEW);
827  LockBuffer(buffer, GIST_EXCLUSIVE);
828 
829  if (needLock)
831 
832  return buffer;
833 }
#define GistPageIsDeleted(page)
Definition: gist.h:135
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:528
#define GIST_UNLOCK
Definition: gist_private.h:45
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define P_NEW
Definition: bufmgr.h:82
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3572
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:743
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define InvalidBlockNumber
Definition: block.h:33
#define PageIsNew(page)
Definition: bufpage.h:225
#define GIST_EXCLUSIVE
Definition: gist_private.h:44
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74

◆ gistnospace()

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

Definition at line 58 of file gistutil.c.

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

Referenced by gistplacetopage().

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

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 836 of file gistutil.c.

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

Referenced by gisthandler().

837 {
839  GiSTOptions *rdopts;
840  int numoptions;
841  static const relopt_parse_elt tab[] = {
842  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
843  {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
844  };
845 
846  options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
847  &numoptions);
848 
849  /* if none set, we're done */
850  if (numoptions == 0)
851  return NULL;
852 
853  rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
854 
855  fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
856  validate, tab, lengthof(tab));
857 
858  pfree(options);
859 
860  return (bytea *) rdopts;
861 }
#define lengthof(array)
Definition: c.h:629
void pfree(void *pointer)
Definition: mcxt.c:1031
int fillfactor
Definition: pgbench.c:127
void * allocateReloptStruct(Size base, relopt_value *options, int numoptions)
Definition: reloptions.c:1242
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:1266
Definition: c.h:516
relopt_value * parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts)
Definition: reloptions.c:1048
#define offsetof(type, field)
Definition: c.h:622

◆ gistpenalty()

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

Definition at line 688 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().

691 {
692  float penalty = 0.0;
693 
694  if (giststate->penaltyFn[attno].fn_strict == false ||
695  (isNullOrig == false && isNullAdd == false))
696  {
697  FunctionCall3Coll(&giststate->penaltyFn[attno],
698  giststate->supportCollation[attno],
699  PointerGetDatum(orig),
700  PointerGetDatum(add),
701  PointerGetDatum(&penalty));
702  /* disallow negative or NaN penalty */
703  if (isnan(penalty) || penalty < 0.0)
704  penalty = 0.0;
705  }
706  else if (isNullOrig && isNullAdd)
707  penalty = 0.0;
708  else
709  {
710  /* try to prevent mixing null and non-null values */
711  penalty = get_float4_infinity();
712  }
713 
714  return penalty;
715 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:96
#define PointerGetDatum(X)
Definition: postgres.h:541
float get_float4_infinity(void)
Definition: float.c:143
bool fn_strict
Definition: fmgr.h:61
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1155

◆ gistplacetopage()

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

Definition at line 215 of file gist.c.

References DataPageDeleteStack::blkno, gistxlogPage::blkno, SplitedPageLayout::block, GISTPageSplitInfo::buf, SplitedPageLayout::buffer, buffer, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, GISTPageSplitInfo::downlink, elog, END_CRIT_SECTION, ERROR, F_LEAF, FirstOffsetNumber, GIST_MAX_SPLIT_PAGES, GIST_ROOT_BLKNO, GistClearFollowRight, gistextractpage(), gistfillbuffer(), gistfillitupvec(), GistFollowRight, gistGetFakeLSN(), GISTInitBuffer(), gistjoinvector(), GistMarkFollowRight, gistNewBuffer(), gistnospace(), GistPageGetNSN, GistPageGetOpaque, GistPageHasGarbage, GistPageIsLeaf, GistPageSetNSN, gistSplit(), GistTupleSetValid, gistvacuumpage(), 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().

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

◆ gistPopItupFromNodeBuffer()

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

Definition at line 410 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().

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

References AMPROCNUM, AMPROP_DISTANCE_ORDERABLE, AMPROP_RETURNABLE, Assert, CLAOID, DatumGetPointer, GETSTRUCT, GIST_COMPRESS_PROC, GIST_DISTANCE_PROC, GIST_FETCH_PROC, HeapTupleIsValid, INDEXRELID, Int16GetDatum, ObjectIdGetDatum, PG_USED_FOR_ASSERTS_ONLY, ReleaseSysCache(), SearchSysCache1(), SearchSysCacheExists4, SysCacheGetAttr(), and oidvector::values.

Referenced by gisthandler().

874 {
875  HeapTuple tuple;
877  Form_pg_opclass rd_opclass;
878  Datum datum;
879  bool disnull;
880  oidvector *indclass;
881  Oid opclass,
882  opfamily,
883  opcintype;
884  int16 procno;
885 
886  /* Only answer column-level inquiries */
887  if (attno == 0)
888  return false;
889 
890  /*
891  * Currently, GiST distance-ordered scans require that there be a distance
892  * function in the opclass with the default types (i.e. the one loaded
893  * into the relcache entry, see initGISTstate). So we assume that if such
894  * a function exists, then there's a reason for it (rather than grubbing
895  * through all the opfamily's operators to find an ordered one).
896  *
897  * Essentially the same code can test whether we support returning the
898  * column data, since that's true if the opclass provides a fetch proc.
899  */
900 
901  switch (prop)
902  {
904  procno = GIST_DISTANCE_PROC;
905  break;
906  case AMPROP_RETURNABLE:
907  procno = GIST_FETCH_PROC;
908  break;
909  default:
910  return false;
911  }
912 
913  /* First we need to know the column's opclass. */
914 
915  tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
916  if (!HeapTupleIsValid(tuple))
917  {
918  *isnull = true;
919  return true;
920  }
921  rd_index = (Form_pg_index) GETSTRUCT(tuple);
922 
923  /* caller is supposed to guarantee this */
924  Assert(attno > 0 && attno <= rd_index->indnatts);
925 
926  datum = SysCacheGetAttr(INDEXRELID, tuple,
927  Anum_pg_index_indclass, &disnull);
928  Assert(!disnull);
929 
930  indclass = ((oidvector *) DatumGetPointer(datum));
931  opclass = indclass->values[attno - 1];
932 
933  ReleaseSysCache(tuple);
934 
935  /* Now look up the opclass family and input datatype. */
936 
937  tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
938  if (!HeapTupleIsValid(tuple))
939  {
940  *isnull = true;
941  return true;
942  }
943  rd_opclass = (Form_pg_opclass) GETSTRUCT(tuple);
944 
945  opfamily = rd_opclass->opcfamily;
946  opcintype = rd_opclass->opcintype;
947 
948  ReleaseSysCache(tuple);
949 
950  /* And now we can check whether the function is provided. */
951 
953  ObjectIdGetDatum(opfamily),
954  ObjectIdGetDatum(opcintype),
955  ObjectIdGetDatum(opcintype),
956  Int16GetDatum(procno));
957 
958  /*
959  * Special case: even without a fetch function, AMPROP_RETURNABLE is true
960  * if the opclass has no compress function.
961  */
962  if (prop == AMPROP_RETURNABLE && !*res)
963  {
965  ObjectIdGetDatum(opfamily),
966  ObjectIdGetDatum(opcintype),
967  ObjectIdGetDatum(opcintype),
969  }
970 
971  return true;
972 }
signed short int16
Definition: c.h:312
Definition: c.h:555
#define GIST_FETCH_PROC
Definition: gist.h:36
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
#define Int16GetDatum(X)
Definition: postgres.h:436
#define SearchSysCacheExists4(cacheId, key1, key2, key3, key4)
Definition: syscache.h:188
unsigned int Oid
Definition: postgres_ext.h:31
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define GIST_COMPRESS_PROC
Definition: gist.h:30
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:563
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:699
#define GIST_DISTANCE_PROC
Definition: gist.h:35
#define DatumGetPointer(X)
Definition: postgres.h:534
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:123
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:81

◆ gistPushItupToNodeBuffer()

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

Definition at line 340 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().

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

◆ gistRelocateBuildBuffersOnSplit()

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

Definition at line 537 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(), tupleDesc::natts, RelocationBufferInfo::nodeBuffer, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, palloc(), pfree(), RelationData::rd_att, and RelocationBufferInfo::splitinfo.

Referenced by gistbufferinginserttuples().

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

◆ gistSplit()

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

Definition at line 1351 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, tupleDesc::natts, SplitedPageLayout::next, 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, GistSplitVector::splitVector, and GISTSTATE::tupdesc.

Referenced by gistplacetopage(), and gistSplit().

1356 {
1357  IndexTuple *lvectup,
1358  *rvectup;
1359  GistSplitVector v;
1360  int i;
1361  SplitedPageLayout *res = NULL;
1362 
1363  /* this should never recurse very deeply, but better safe than sorry */
1365 
1366  /* there's no point in splitting an empty page */
1367  Assert(len > 0);
1368 
1369  /*
1370  * If a single tuple doesn't fit on a page, no amount of splitting will
1371  * help.
1372  */
1373  if (len == 1)
1374  ereport(ERROR,
1375  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1376  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1377  IndexTupleSize(itup[0]), GiSTPageSize,
1379 
1380  memset(v.spl_lisnull, true, sizeof(bool) * giststate->tupdesc->natts);
1381  memset(v.spl_risnull, true, sizeof(bool) * giststate->tupdesc->natts);
1382  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1383 
1384  /* form left and right vector */
1385  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1386  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1387 
1388  for (i = 0; i < v.splitVector.spl_nleft; i++)
1389  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1390 
1391  for (i = 0; i < v.splitVector.spl_nright; i++)
1392  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1393 
1394  /* finalize splitting (may need another split) */
1395  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1396  {
1397  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1398  }
1399  else
1400  {
1401  ROTATEDIST(res);
1402  res->block.num = v.splitVector.spl_nright;
1403  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1404  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1405  }
1406 
1407  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1408  {
1409  SplitedPageLayout *resptr,
1410  *subres;
1411 
1412  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1413 
1414  /* install on list's tail */
1415  while (resptr->next)
1416  resptr = resptr->next;
1417 
1418  resptr->next = res;
1419  res = subres;
1420  }
1421  else
1422  {
1423  ROTATEDIST(res);
1424  res->block.num = v.splitVector.spl_nleft;
1425  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1426  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1427  }
1428 
1429  return res;
1430 }
TupleDesc tupdesc
Definition: gist_private.h:81
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:228
OffsetNumber * spl_left
Definition: gist.h:105
GIST_SPLITVEC splitVector
Definition: gist_private.h:226
int errcode(int sqlerrcode)
Definition: elog.c:575
int spl_nleft
Definition: gist.h:106
IndexTupleData * list
Definition: gist_private.h:190
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:232
int natts
Definition: tupdesc.h:82
gistxlogPage block
Definition: gist_private.h:189
#define ERROR
Definition: elog.h:43
int spl_nright
Definition: gist.h:111
void check_stack_depth(void)
Definition: postgres.c:3159
#define RelationGetRelationName(relation)
Definition: rel.h:441
struct SplitedPageLayout * next
Definition: gist_private.h:196
#define ereport(elevel, rest)
Definition: elog.h:122
#define GiSTPageSize
Definition: gist_private.h:432
#define ROTATEDIST(d)
Definition: gist.c:44
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1351
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:78
#define Assert(condition)
Definition: c.h:699
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:126
OffsetNumber * spl_right
Definition: gist.h:110
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:234
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:572
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:230
#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, GistEntryVector::n, tupleDesc::natts, 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, GISTSTATE::tupdesc, 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->tupdesc,
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->tupdesc->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->tupdesc->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->tupdesc->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->tupdesc->natts > 1)
775  {
776  v->spl_dontcare = NULL;
777  gistunionsubkey(giststate, itup, v);
778  }
779 }
TupleDesc tupdesc
Definition: gist_private.h:81
OffsetNumber * spl_left
Definition: gist.h:105
GIST_SPLITVEC splitVector
Definition: gist_private.h:226
int32 n
Definition: gist.h:160
int spl_nleft
Definition: gist.h:106
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
int natts
Definition: tupdesc.h:82
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:161
#define GEVHDRSZ
Definition: gist.h:164
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:544
int spl_nright
Definition: gist.h:111
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:699
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585
OffsetNumber * spl_right
Definition: gist.h:110
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:234
void * palloc(Size size)
Definition: mcxt.c:924
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:230
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 217 of file gistutil.c.

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

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

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 276 of file gistbuildbuffers.c.

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

Referenced by gistProcessEmptyingQueue().

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

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 29 of file gistvacuum.c.

References IndexVacuumInfo::analyze_only, DataPageDeleteStack::blkno, buffer, BufferGetPage, IndexBulkDeleteResult::estimated_count, ExclusiveLock, GIST_ROOT_BLKNO, GIST_SHARE, GistPageIsDeleted, GistPageIsLeaf, IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), LockBuffer(), LockRelationForExtension(), MAIN_FORKNUM, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, PageGetMaxOffsetNumber, PageIsNew, IndexBulkDeleteResult::pages_free, palloc0(), RBM_NORMAL, ReadBufferExtended(), RecordFreeIndexPage(), RELATION_IS_LOCAL, RelationGetNumberOfBlocks, IndexVacuumInfo::strategy, UnlockRelationForExtension(), UnlockReleaseBuffer(), and vacuum_delay_point().

Referenced by gisthandler().

30 {
31  Relation rel = info->index;
32  BlockNumber npages,
33  blkno;
34  BlockNumber totFreePages;
35  double tuplesCount;
36  bool needLock;
37 
38  /* No-op in ANALYZE ONLY mode */
39  if (info->analyze_only)
40  return stats;
41 
42  /* Set up all-zero stats if gistbulkdelete wasn't called */
43  if (stats == NULL)
45 
46  /*
47  * Need lock unless it's local to this backend.
48  */
49  needLock = !RELATION_IS_LOCAL(rel);
50 
51  /* try to find deleted pages */
52  if (needLock)
54  npages = RelationGetNumberOfBlocks(rel);
55  if (needLock)
57 
58  totFreePages = 0;
59  tuplesCount = 0;
60  for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
61  {
62  Buffer buffer;
63  Page page;
64 
66 
67  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
68  info->strategy);
69  LockBuffer(buffer, GIST_SHARE);
70  page = (Page) BufferGetPage(buffer);
71 
72  if (PageIsNew(page) || GistPageIsDeleted(page))
73  {
74  totFreePages++;
75  RecordFreeIndexPage(rel, blkno);
76  }
77  else if (GistPageIsLeaf(page))
78  {
79  /* count tuples in index (considering only leaf tuples) */
80  tuplesCount += PageGetMaxOffsetNumber(page);
81  }
82  UnlockReleaseBuffer(buffer);
83  }
84 
85  /* Finally, vacuum the FSM */
87 
88  /* return statistics */
89  stats->pages_free = totFreePages;
90  if (needLock)
93  if (needLock)
95  stats->num_index_tuples = tuplesCount;
96  stats->estimated_count = false;
97 
98  return stats;
99 }
#define GistPageIsDeleted(page)
Definition: gist.h:135
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:528
bool analyze_only
Definition: genam.h:47
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
BufferAccessStrategy strategy
Definition: genam.h:51
Relation index
Definition: genam.h:46
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
BlockNumber num_pages
Definition: genam.h:73
BlockNumber pages_free
Definition: genam.h:79
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define GistPageIsLeaf(page)
Definition: gist.h:132
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
void * palloc0(Size size)
Definition: mcxt.c:955
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define GIST_SHARE
Definition: gist_private.h:43
#define PageIsNew(page)
Definition: bufpage.h:225
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
void vacuum_delay_point(void)
Definition: vacuum.c:1672
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
double num_index_tuples
Definition: genam.h:76
int Buffer
Definition: buf.h:23
bool estimated_count
Definition: genam.h:75
Pointer Page
Definition: bufpage.h:74

◆ 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 procedure %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:36
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
#define GIST_EQUAL_PROC
Definition: gist.h:34
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:66
int errcode(int sqlerrcode)
Definition: elog.c:575
#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:605
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1127
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1800
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:33
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define ERROR
Definition: elog.h:43
#define GIST_COMPRESS_PROC
Definition: gist.h:30
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:209
#define ereport(elevel, rest)
Definition: elog.h:122
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:49
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#define GIST_CONSISTENT_PROC
Definition: gist.h:28
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
#define GIST_UNION_PROC
<