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:70

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 110 of file gist.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate(), and CurrentMemoryContext.

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

111 {
113  "GiST temporary context",
115 }
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:165
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1522 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

1523 {
1524  /* It's sufficient to delete the scanCxt */
1525  MemoryContextDelete(giststate->scanCxt);
1526 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
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, 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);
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:200
#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:110
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:114
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:445
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1522
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:1423
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:992
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
Relation indexrel
Definition: gistbuild.c:58
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:214
#define RelationNeedsWAL(relation)
Definition: rel.h:514
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool allow_sync, IndexBuildCallback callback, void *callback_state)
Definition: index.c:2178
void * palloc(Size size)
Definition: mcxt.c:848
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
bytea * rd_options
Definition: rel.h:156
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 121 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().

122 {
123  Buffer buffer;
124 
125  /* Initialize the root page */
126  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
128 
129  /* Initialize and xlog buffer */
131  GISTInitBuffer(buffer, F_LEAF);
132  MarkBufferDirty(buffer);
133  log_newpage_buffer(buffer, true);
135 
136  /* Unlock and release the buffer */
137  UnlockReleaseBuffer(buffer);
138 }
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:214
#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 139 of file gistvacuum.c.

References GistBDItem::blkno, buffer, 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, PageGetLSN, 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().

141 {
142  Relation rel = info->index;
143  GistBDItem *stack,
144  *ptr;
145 
146  /* first time through? */
147  if (stats == NULL)
149  /* we'll re-count the tuples each time */
150  stats->estimated_count = false;
151  stats->num_index_tuples = 0;
152 
153  stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
154  stack->blkno = GIST_ROOT_BLKNO;
155 
156  while (stack)
157  {
158  Buffer buffer;
159  Page page;
160  OffsetNumber i,
161  maxoff;
162  IndexTuple idxtuple;
163  ItemId iid;
164 
165  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
166  RBM_NORMAL, info->strategy);
167  LockBuffer(buffer, GIST_SHARE);
168  gistcheckpage(rel, buffer);
169  page = (Page) BufferGetPage(buffer);
170 
171  if (GistPageIsLeaf(page))
172  {
173  OffsetNumber todelete[MaxOffsetNumber];
174  int ntodelete = 0;
175 
176  LockBuffer(buffer, GIST_UNLOCK);
177  LockBuffer(buffer, GIST_EXCLUSIVE);
178 
179  page = (Page) BufferGetPage(buffer);
180  if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
181  {
182  /* only the root can become non-leaf during relock */
183  UnlockReleaseBuffer(buffer);
184  /* one more check */
185  continue;
186  }
187 
188  /*
189  * check for split proceeded after look at parent, we should check
190  * it after relock
191  */
192  pushStackIfSplited(page, stack);
193 
194  /*
195  * Remove deletable tuples from page
196  */
197 
198  maxoff = PageGetMaxOffsetNumber(page);
199 
200  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
201  {
202  iid = PageGetItemId(page, i);
203  idxtuple = (IndexTuple) PageGetItem(page, iid);
204 
205  if (callback(&(idxtuple->t_tid), callback_state))
206  todelete[ntodelete++] = i;
207  else
208  stats->num_index_tuples += 1;
209  }
210 
211  stats->tuples_removed += ntodelete;
212 
213  if (ntodelete)
214  {
216 
217  MarkBufferDirty(buffer);
218 
219  PageIndexMultiDelete(page, todelete, ntodelete);
220  GistMarkTuplesDeleted(page);
221 
222  if (RelationNeedsWAL(rel))
223  {
224  XLogRecPtr recptr;
225 
226  recptr = gistXLogUpdate(buffer,
227  todelete, ntodelete,
228  NULL, 0, InvalidBuffer);
229  PageSetLSN(page, recptr);
230  }
231  else
232  PageSetLSN(page, gistGetFakeLSN(rel));
233 
235  }
236 
237  }
238  else
239  {
240  /* check for split proceeded after look at parent */
241  pushStackIfSplited(page, stack);
242 
243  maxoff = PageGetMaxOffsetNumber(page);
244 
245  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
246  {
247  iid = PageGetItemId(page, i);
248  idxtuple = (IndexTuple) PageGetItem(page, iid);
249 
250  ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
251  ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
252  ptr->parentlsn = PageGetLSN(page);
253  ptr->next = stack->next;
254  stack->next = ptr;
255 
256  if (GistTupleIsInvalid(idxtuple))
257  ereport(LOG,
258  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
260  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
261  errhint("Please REINDEX it.")));
262  }
263  }
264 
265  UnlockReleaseBuffer(buffer);
266 
267  ptr = stack->next;
268  pfree(stack);
269  stack = ptr;
270 
272  }
273 
274  return stats;
275 }
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:104
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:980
uint16 OffsetNumber
Definition: off.h:24
void pfree(void *pointer)
Definition: mcxt.c:949
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
#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:445
#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:877
BlockNumber blkno
Definition: gistvacuum.c:105
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:214
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:514
#define PageGetLSN(page)
Definition: bufpage.h:362
void * palloc(Size size)
Definition: mcxt.c:848
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
struct GistBDItem * next
Definition: gistvacuum.c:106
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:76
void vacuum_delay_point(void)
Definition: vacuum.c:1658
#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:110

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 807 of file gistget.c.

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

Referenced by gisthandler().

808 {
809  if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
811  return true;
812  else
813  return false;
814 }
#define GIST_FETCH_PROC
Definition: gist.h:36
#define OidIsValid(objectId)
Definition: c.h:576
#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:445
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define MAXALIGN(LEN)
Definition: c.h:623
#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:79
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:372
TupleDesc rd_att
Definition: rel.h:115
#define Assert(condition)
Definition: c.h:670
#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:79
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:372
TupleDesc rd_att
Definition: rel.h:115
#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:562
#define OidIsValid(objectId)
Definition: c.h:576
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:372
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1022
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:555
OffsetNumber offset
Definition: gist.h:126

◆ gistdoinsert()

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

Definition at line 601 of file gist.c.

References Assert, GISTInsertStack::blkno, GISTInsertStack::buffer, 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().

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

◆ 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:848
int i
#define IndexTupleSize(itup)
Definition: itup.h:70

◆ 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:404
int i
#define IndexTupleSize(itup)
Definition: itup.h:70

◆ 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:199

◆ 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:79
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:372
TupleDesc rd_att
Definition: rel.h:115
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 758 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().

759 {
760  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
761  int64 ntids = 0;
762  GISTSearchItem fakeItem;
763 
764  if (!so->qual_ok)
765  return 0;
766 
768 
769  /* Begin the scan by processing the root page */
770  so->curPageData = so->nPageData = 0;
771  scan->xs_hitup = NULL;
772  if (so->pageDataCxt)
774 
775  fakeItem.blkno = GIST_ROOT_BLKNO;
776  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
777  gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
778 
779  /*
780  * While scanning a leaf page, ItemPointers of matching heap tuples will
781  * be stored directly into tbm, so we don't need to deal with them here.
782  */
783  for (;;)
784  {
786 
787  if (!item)
788  break;
789 
791 
792  gistScanPage(scan, item, item->distances, tbm, &ntids);
793 
794  pfree(item);
795  }
796 
797  return ntids;
798 }
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
MemoryContext pageDataCxt
Definition: gist_private.h:173
Relation indexRelation
Definition: relscan.h:90
double distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:139
void pfree(void *pointer)
Definition: mcxt.c:949
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:322
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:177
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1275
OffsetNumber nPageData
Definition: gist_private.h:171
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:517
OffsetNumber curPageData
Definition: gist_private.h:172
XLogRecPtr GistNSN
Definition: gist.h:50
HeapTuple xs_hitup
Definition: relscan.h:116
#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(), RelationData::rd_rel, RELPERSISTENCE_TEMP, and RELPERSISTENCE_UNLOGGED.

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 }
#define RELPERSISTENCE_UNLOGGED
Definition: pg_class.h:171
Form_pg_class rd_rel
Definition: rel.h:114
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4754
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
#define RELPERSISTENCE_TEMP
Definition: pg_class.h:172

◆ 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:902
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:962
int i
Definition: pg_list.h:45

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

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

628 {
629  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
630 
631  if (dir != ForwardScanDirection)
632  elog(ERROR, "GiST only supports forward scan direction");
633 
634  if (!so->qual_ok)
635  return false;
636 
637  if (so->firstCall)
638  {
639  /* Begin the scan by processing the root page */
640  GISTSearchItem fakeItem;
641 
643 
644  so->firstCall = false;
645  so->curPageData = so->nPageData = 0;
646  scan->xs_hitup = NULL;
647  if (so->pageDataCxt)
649 
650  fakeItem.blkno = GIST_ROOT_BLKNO;
651  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
652  gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
653  }
654 
655  if (scan->numberOfOrderBys > 0)
656  {
657  /* Must fetch tuples in strict distance order */
658  return getNextNearest(scan);
659  }
660  else
661  {
662  /* Fetch tuples index-page-at-a-time */
663  for (;;)
664  {
665  if (so->curPageData < so->nPageData)
666  {
667  if (scan->kill_prior_tuple && so->curPageData > 0)
668  {
669 
670  if (so->killedItems == NULL)
671  {
672  MemoryContext oldCxt =
674 
675  so->killedItems =
677  * sizeof(OffsetNumber));
678 
679  MemoryContextSwitchTo(oldCxt);
680  }
682  so->killedItems[so->numKilled++] =
683  so->pageData[so->curPageData - 1].offnum;
684  }
685  /* continuing to return tuples from a leaf page */
686  scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
687  scan->xs_recheck = so->pageData[so->curPageData].recheck;
688 
689  /* in an index-only scan, also return the reconstructed tuple */
690  if (scan->xs_want_itup)
691  scan->xs_hitup = so->pageData[so->curPageData].recontup;
692 
693  so->curPageData++;
694 
695  return true;
696  }
697 
698  /*
699  * Check the last returned tuple and add it to killitems if
700  * necessary
701  */
702  if (scan->kill_prior_tuple
703  && so->curPageData > 0
704  && so->curPageData == so->nPageData)
705  {
706 
707  if (so->killedItems == NULL)
708  {
709  MemoryContext oldCxt =
711 
712  so->killedItems =
714  * sizeof(OffsetNumber));
715 
716  MemoryContextSwitchTo(oldCxt);
717  }
719  so->killedItems[so->numKilled++] =
720  so->pageData[so->curPageData - 1].offnum;
721  }
722  /* find and process the next index page */
723  do
724  {
725  GISTSearchItem *item;
726 
727  if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
728  gistkillitems(scan);
729 
730  item = getNextGISTSearchItem(so);
731 
732  if (!item)
733  return false;
734 
736 
737  /* save current item BlockNumber for next gistkillitems() call */
738  so->curBlkno = item->blkno;
739 
740  /*
741  * While scanning a leaf page, ItemPointers of matching heap
742  * tuples are stored in so->pageData. If there are any on
743  * this page, we fall out of the inner "do" and loop around to
744  * return them.
745  */
746  gistScanPage(scan, item, item->distances, NULL, NULL);
747 
748  pfree(item);
749  } while (so->nPageData == 0);
750  }
751  }
752 }
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:135
MemoryContext pageDataCxt
Definition: gist_private.h:173
Relation indexRelation
Definition: relscan.h:90
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:949
#define ERROR
Definition: elog.h:43
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:322
ItemPointerData t_self
Definition: htup.h:65
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:177
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1275
OffsetNumber nPageData
Definition: gist_private.h:171
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:517
union GISTSearchItem::@43 data
bool xs_want_itup
Definition: relscan.h:96
ItemPointerData heapPtr
Definition: gist_private.h:119
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:539
HeapTupleData xs_ctup
Definition: relscan.h:120
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:37
#define MaxIndexTuplesPerPage
Definition: itup.h:137
void * palloc(Size size)
Definition: mcxt.c:848
HeapTuple xs_hitup
Definition: relscan.h:116
OffsetNumber offnum
Definition: gist_private.h:124
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
bool kill_prior_tuple
Definition: relscan.h:100
MemoryContext scanCxt
Definition: gist_private.h:78
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
int numberOfOrderBys
Definition: relscan.h:93
#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:404
#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:164
List * bufferEmptyingQueue
Definition: gist_private.h:344
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
#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:848
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 147 of file gist.c.

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

Referenced by gisthandler().

151 {
152  GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
153  IndexTuple itup;
154  MemoryContext oldCxt;
155 
156  /* Initialize GISTSTATE cache if first call in this statement */
157  if (giststate == NULL)
158  {
159  oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
160  giststate = initGISTstate(r);
161  giststate->tempCxt = createTempGistContext();
162  indexInfo->ii_AmCache = (void *) giststate;
163  MemoryContextSwitchTo(oldCxt);
164  }
165 
166  oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
167 
168  itup = gistFormTuple(giststate, r,
169  values, isnull, true /* size is currently bogus */ );
170  itup->t_tid = *ht_ctid;
171 
172  gistdoinsert(r, itup, 0, giststate);
173 
174  /* cleanup */
175  MemoryContextSwitchTo(oldCxt);
176  MemoryContextReset(giststate->tempCxt);
177 
178  return false;
179 }
MemoryContext ii_Context
Definition: execnodes.h:151
MemoryContext createTempGistContext(void)
Definition: gist.c:110
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
Definition: gist.c:601
MemoryContext tempCxt
Definition: gist_private.h:79
void * ii_AmCache
Definition: execnodes.h:150
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1423
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:1045
IndexTupleData * IndexTuple
Definition: itup.h:53
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:962

◆ 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:562
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:1064

◆ 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:562
int32 n
Definition: gist.h:160
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
int natts
Definition: tupdesc.h:79
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:372
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
void * palloc(Size size)
Definition: mcxt.c:848
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:562
int32 n
Definition: gist.h:160
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1042
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:161
#define GEVHDRSZ
Definition: gist.h:164
struct GISTENTRY GISTENTRY
uintptr_t Datum
Definition: postgres.h:372
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:532
#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:214
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:70

◆ 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:600
void pfree(void *pointer)
Definition: mcxt.c:949
int fillfactor
Definition: pgbench.c:114
void * allocateReloptStruct(Size base, relopt_value *options, int numoptions)
Definition: reloptions.c:1225
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:1249
Definition: c.h:487
relopt_value * parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts)
Definition: reloptions.c:1030
#define offsetof(type, field)
Definition: c.h:593

◆ 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:562
float get_float4_infinity(void)
Definition: float.c:146
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:1064

◆ 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 212 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(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexTupleData::t_tid, UnlockReleaseBuffer(), and XLogEnsureRecordSpace().

Referenced by gistbufferinginserttuples(), and gistinserttuples().

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

◆ 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:949
static void gistGetItupFromPage(GISTNodeBufferPage *pageBuffer, IndexTuple *item)
BlockNumber prev
Definition: gist_private.h:49
#define Assert(condition)
Definition: c.h:670
#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, Anum_pg_index_indclass, 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:283
Definition: c.h:526
#define GIST_FETCH_PROC
Definition: gist.h:36
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
#define Int16GetDatum(X)
Definition: postgres.h:457
#define Anum_pg_index_indclass
Definition: pg_index.h:89
#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:513
#define GIST_COMPRESS_PROC
Definition: gist.h:30
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:534
FormData_pg_index * Form_pg_index
Definition: pg_index.h:67
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
uintptr_t Datum
Definition: postgres.h:372
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:77
#define Assert(condition)
Definition: c.h:670
#define GIST_DISTANCE_PROC
Definition: gist.h:35
#define DatumGetPointer(X)
Definition: postgres.h:555
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:122
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68

◆ 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:623
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
#define offsetof(type, field)
Definition: c.h:593

◆ 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:902
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:314
int natts
Definition: tupdesc.h:79
#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:949
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:115
#define Assert(condition)
Definition: c.h:670
#define lfirst(lc)
Definition: pg_list.h:106
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:214
#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:848
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 1338 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().

1343 {
1344  IndexTuple *lvectup,
1345  *rvectup;
1346  GistSplitVector v;
1347  int i;
1348  SplitedPageLayout *res = NULL;
1349 
1350  /* this should never recurse very deeply, but better safe than sorry */
1352 
1353  /* there's no point in splitting an empty page */
1354  Assert(len > 0);
1355 
1356  /*
1357  * If a single tuple doesn't fit on a page, no amount of splitting will
1358  * help.
1359  */
1360  if (len == 1)
1361  ereport(ERROR,
1362  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1363  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1364  IndexTupleSize(itup[0]), GiSTPageSize,
1366 
1367  memset(v.spl_lisnull, true, sizeof(bool) * giststate->tupdesc->natts);
1368  memset(v.spl_risnull, true, sizeof(bool) * giststate->tupdesc->natts);
1369  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1370 
1371  /* form left and right vector */
1372  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1373  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1374 
1375  for (i = 0; i < v.splitVector.spl_nleft; i++)
1376  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1377 
1378  for (i = 0; i < v.splitVector.spl_nright; i++)
1379  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1380 
1381  /* finalize splitting (may need another split) */
1382  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1383  {
1384  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1385  }
1386  else
1387  {
1388  ROTATEDIST(res);
1389  res->block.num = v.splitVector.spl_nright;
1390  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1391  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1392  }
1393 
1394  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1395  {
1396  SplitedPageLayout *resptr,
1397  *subres;
1398 
1399  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1400 
1401  /* install on list's tail */
1402  while (resptr->next)
1403  resptr = resptr->next;
1404 
1405  resptr->next = res;
1406  res = subres;
1407  }
1408  else
1409  {
1410  ROTATEDIST(res);
1411  res->block.num = v.splitVector.spl_nleft;
1412  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1413  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1414  }
1415 
1416  return res;
1417 }
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:79
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:3150
#define RelationGetRelationName(relation)
Definition: rel.h:445
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:42
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1338
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:78
#define Assert(condition)
Definition: c.h:670
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:848
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:70

◆ 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:79
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:372
#define Assert(condition)
Definition: c.h:670
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:848
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:372
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, IndexVacuumInfo::estimated_count, IndexBulkDeleteResult::estimated_count, ExclusiveLock, GIST_ROOT_BLKNO, GIST_SHARE, GistPageIsDeleted, IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), LockBuffer(), LockRelationForExtension(), MAIN_FORKNUM, IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, 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  bool needLock;
36 
37  /* No-op in ANALYZE ONLY mode */
38  if (info->analyze_only)
39  return stats;
40 
41  /* Set up all-zero stats if gistbulkdelete wasn't called */
42  if (stats == NULL)
43  {
45  /* use heap's tuple count */
46  stats->num_index_tuples = info->num_heap_tuples;
47  stats->estimated_count = info->estimated_count;
48 
49  /*
50  * XXX the above is wrong if index is partial. Would it be OK to just
51  * return NULL, or is there work we must do below?
52  */
53  }
54 
55  /*
56  * Need lock unless it's local to this backend.
57  */
58  needLock = !RELATION_IS_LOCAL(rel);
59 
60  /* try to find deleted pages */
61  if (needLock)
63  npages = RelationGetNumberOfBlocks(rel);
64  if (needLock)
66 
67  totFreePages = 0;
68  for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
69  {
70  Buffer buffer;
71  Page page;
72 
74 
75  buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
76  info->strategy);
77  LockBuffer(buffer, GIST_SHARE);
78  page = (Page) BufferGetPage(buffer);
79 
80  if (PageIsNew(page) || GistPageIsDeleted(page))
81  {
82  totFreePages++;
83  RecordFreeIndexPage(rel, blkno);
84  }
85  UnlockReleaseBuffer(buffer);
86  }
87 
88  /* Finally, vacuum the FSM */
90 
91  /* return statistics */
92  stats->pages_free = totFreePages;
93  if (needLock)
96  if (needLock)
98 
99  return stats;
100 }
#define GistPageIsDeleted(page)
Definition: gist.h:135
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:532
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
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
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
void * palloc0(Size size)
Definition: mcxt.c:877
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
double num_heap_tuples
Definition: genam.h:50
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:214
#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:1658
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
bool estimated_count
Definition: genam.h:48

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

References AMOP_SEARCH, AMOPSTRATEGY, AMPROCNUM, BOOLOID, check_amop_signature(), check_amproc_signature(), CLAOID, elog, ereport, errcode(), errmsg(), ERROR, FLOAT8OID, 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, INT2OID, INTERNALOID, OpFamilyOpFuncGroup::lefttype, lfirst, catclist::members, catclist::n_members, NameStr, ObjectIdGetDatum, OidIsValid, OIDOID, 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,
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,
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,
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:661
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
#define AMOP_SEARCH
Definition: pg_amop.h:69
#define OIDOID
Definition: pg_type.h:328
#define GIST_EQUAL_PROC
Definition: gist.h:34
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:59
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:576
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1142
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1795
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:513
#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 INT2OID
Definition: pg_type.h:308
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:209