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

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

enum  GistOptBufferingMode { GIST_OPTION_BUFFERING_AUTO, GIST_OPTION_BUFFERING_ON, GIST_OPTION_BUFFERING_OFF }
 

Functions

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

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

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

Definition at line 324 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

Referenced by gistProcessItup().

◆ BUFFER_PAGE_DATA_OFFSET

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

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 472 of file gist_private.h.

Referenced by gistbuild().

◆ GIST_EXCLUSIVE

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

Referenced by gistplacetopage().

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 471 of file gist_private.h.

◆ GIST_ROOT_BLKNO

◆ GIST_SHARE

◆ GIST_UNLOCK

◆ GiSTPageSize

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

Definition at line 468 of file gist_private.h.

Referenced by gistfitpage(), and gistSplit().

◆ GISTSearchItemIsHeap

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

Definition at line 145 of file gist_private.h.

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

◆ GistTupleIsInvalid

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

Definition at line 288 of file gist_private.h.

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

◆ GistTupleSetValid

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

Definition at line 289 of file gist_private.h.

Referenced by gistformdownlink(), and gistplacetopage().

◆ LEVEL_HAS_BUFFERS

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

Definition at line 319 of file gist_private.h.

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ PAGE_FREE_SPACE

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

◆ PAGE_IS_EMPTY

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

Definition at line 57 of file gist_private.h.

Referenced by gistGetItupFromPage(), and gistPopItupFromNodeBuffer().

◆ PAGE_NO_SPACE

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

Definition at line 59 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ SizeOfGISTSearchItem

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

Definition at line 147 of file gist_private.h.

Referenced by gistScanPage().

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 286 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 285 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GistOptBufferingMode

◆ GiSTOptions

typedef struct GiSTOptions GiSTOptions

◆ GISTScanOpaque

Definition at line 181 of file gist_private.h.

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

typedef struct GISTSTATE GISTSTATE

◆ gistxlogPage

typedef struct gistxlogPage gistxlogPage

◆ SplitedPageLayout

Enumeration Type Documentation

◆ GistOptBufferingMode

Enumerator
GIST_OPTION_BUFFERING_AUTO 
GIST_OPTION_BUFFERING_ON 
GIST_OPTION_BUFFERING_OFF 

Definition at line 384 of file gist_private.h.

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 113 of file gist.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1611 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

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

◆ gistbuild()

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

Definition at line 116 of file gistbuild.c.

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

Referenced by gisthandler().

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

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 124 of file gist.c.

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

Referenced by gisthandler().

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

◆ gistbulkdelete()

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

Definition at line 83 of file gistvacuum.c.

References create_GistBulkDeleteResult(), and gistvacuumscan().

Referenced by gisthandler().

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

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

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

Referenced by gisthandler().

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

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 770 of file gistutil.c.

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

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

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

◆ gistchoose()

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

Definition at line 373 of file gistutil.c.

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

Referenced by gistdoinsert(), and gistProcessItup().

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

◆ gistDeCompressAtt()

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

Definition at line 295 of file gistutil.c.

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

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

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

◆ gistdentryinit()

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

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

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

◆ gistdoinsert()

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

Definition at line 622 of file gist.c.

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

Referenced by gistBuildCallback(), and gistinsert().

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

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 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:357
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
void * palloc(Size size)
Definition: mcxt.c:949
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ gistFetchTuple()

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

Definition at line 658 of file gistutil.c.

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

Referenced by gistScanPage().

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

◆ gistfillbuffer()

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

Definition at line 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:222
Pointer Item
Definition: item.h:17
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:416
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define ERROR
Definition: elog.h:43
#define FirstOffsetNumber
Definition: off.h:27
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
size_t Size
Definition: c.h:467
#define elog(elevel,...)
Definition: elog.h:228
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistfillitupvec()

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

Definition at line 126 of file gistutil.c.

References i, IndexTupleSize, and palloc().

Referenced by gistplacetopage(), and gistSplit().

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

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 78 of file gistutil.c.

References GiSTPageSize, i, and IndexTupleSize.

Referenced by gistSplit().

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

◆ gistFormTuple()

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 512 of file gistbuildbuffers.c.

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

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

◆ gistgetadjusted()

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

Definition at line 315 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

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

Referenced by gisthandler().

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

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1012 of file gistutil.c.

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

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

1013 {
1014  static XLogRecPtr counter = FirstNormalUnloggedLSN;
1015 
1016  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1017  {
1018  /*
1019  * Temporary relations are only accessible in our session, so a simple
1020  * backend-local counter will do.
1021  */
1022  return counter++;
1023  }
1024  else
1025  {
1026  /*
1027  * Unlogged relations are accessible from other backends, and survive
1028  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1029  */
1030  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1031  return GetFakeLSNForUnloggedRel();
1032  }
1033 }
Form_pg_class rd_rel
Definition: rel.h:83
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4835
#define FirstNormalUnloggedLSN
Definition: xlogdefs.h:36
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739

◆ gistGetNodeBuffer()

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

Definition at line 118 of file gistbuildbuffers.c.

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

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

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 614 of file gistget.c.

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

Referenced by gisthandler().

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

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 748 of file gistutil.c.

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

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

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

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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

Referenced by gistInitBuffering().

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

◆ gistinsert()

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

Definition at line 150 of file gist.c.

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

Referenced by gisthandler().

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

◆ 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:1267
IndexTupleData * IndexTuple
Definition: itup.h:53
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069

◆ gistKeyIsEQ()

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

Definition at line 280 of file gistutil.c.

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

Referenced by gistgetadjusted(), and gistUserPicksplit().

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

◆ gistMakeUnionItVec()

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

Definition at line 154 of file gistutil.c.

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

Referenced by gistunion(), and gistunionsubkeyvec().

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

◆ gistMakeUnionKey()

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

Definition at line 232 of file gistutil.c.

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

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

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 809 of file gistutil.c.

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

Referenced by gistbuild(), and gistplacetopage().

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

◆ gistnospace()

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

Definition at line 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:581
IndexTupleData * IndexTuple
Definition: itup.h:53
struct ItemIdData ItemIdData
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 908 of file gistutil.c.

References build_reloptions(), fillfactor, lengthof, offsetof, RELOPT_KIND_GIST, RELOPT_TYPE_ENUM, and RELOPT_TYPE_INT.

Referenced by gisthandler().

909 {
910  static const relopt_parse_elt tab[] = {
911  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
912  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
913  };
914 
915  return (bytea *) build_reloptions(reloptions, validate,
917  sizeof(GiSTOptions),
918  tab, lengthof(tab));
919 }
#define lengthof(array)
Definition: c.h:669
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1552
int fillfactor
Definition: pgbench.c:160
Definition: c.h:556
#define offsetof(type, field)
Definition: c.h:662

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 882 of file gistutil.c.

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

Referenced by gistNewBuffer(), and gistvacuumpage().

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

◆ gistpenalty()

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

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

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

◆ gistplacetopage()

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

Definition at line 215 of file gist.c.

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

Referenced by gistbufferinginserttuples(), and gistinserttuples().

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

◆ gistPopItupFromNodeBuffer()

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

Definition at line 411 of file gistbuildbuffers.c.

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

Referenced by gistProcessEmptyingQueue(), and gistRelocateBuildBuffersOnSplit().

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

◆ gistproperty()

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

Definition at line 929 of file gistutil.c.

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

Referenced by gisthandler().

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

◆ gistPushItupToNodeBuffer()

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

Definition at line 341 of file gistbuildbuffers.c.

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

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

◆ gistRelocateBuildBuffersOnSplit()

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

Definition at line 538 of file gistbuildbuffers.c.

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

Referenced by gistbufferinginserttuples().

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

◆ gistSplit()

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

Definition at line 1396 of file gist.c.

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

Referenced by gistplacetopage(), and gistSplit().

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

◆ gistSplitByKey()

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

Definition at line 623 of file gistsplit.c.

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

Referenced by gistSplit(), and gistSplitByKey().

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

◆ gistunion()

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

Definition at line 218 of file gistutil.c.

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

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

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 277 of file gistbuildbuffers.c.

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

Referenced by gistProcessEmptyingQueue().

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

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 101 of file gistvacuum.c.

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

Referenced by gisthandler().

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

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

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

Referenced by gisthandler().

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

◆ gistValidateBufferingOption()

void gistValidateBufferingOption ( const char *  value)

◆ gistXLogDelete()

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

Definition at line 673 of file gistxlog.c.

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

Referenced by gistprunepage().

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

◆ gistXLogPageDelete()

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

Definition at line 575 of file gistxlog.c.

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

Referenced by gistdeletepage().

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

◆ gistXLogPageReuse()

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

Definition at line 599 of file gistxlog.c.

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

Referenced by gistNewBuffer().

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

◆ gistXLogSplit()