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)
 
XLogRecPtr gistXLogAssignLSN (void)
 
bool gistgettuple (IndexScanDesc scan, ScanDirection dir)
 
int64 gistgetbitmap (IndexScanDesc scan, TIDBitmap *tbm)
 
bool gistcanreturn (Relation index, int attno)
 
bool gistvalidate (Oid opclassoid)
 
void gistadjustmembers (Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
 
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)
 
void gistCompressValues (GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf, Datum *compatt)
 
OffsetNumber gistchoose (Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
 
void GISTInitBuffer (Buffer b, uint32 f)
 
void gistinitpage (Page page, 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 478 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 477 of file gist_private.h.

◆ GIST_ROOT_BLKNO

◆ GIST_SHARE

◆ GIST_UNLOCK

◆ GiSTPageSize

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

Definition at line 474 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:669

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

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

120 {
122  "GiST temporary context",
124 }
#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 1626 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

1627 {
1628  /* It's sufficient to delete the scanCxt */
1629  MemoryContextDelete(giststate->scanCxt);
1630 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
MemoryContext scanCxt
Definition: gist_private.h:77

◆ gistadjustmembers()

void gistadjustmembers ( Oid  opfamilyoid,
Oid  opclassoid,
List operators,
List functions 
)

Definition at line 291 of file gistvalidate.c.

References ereport, errcode(), errmsg(), ERROR, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_OPTIONS_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_UNION_PROC, lfirst, OpFamilyMember::number, OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, and OpFamilyMember::refobjid.

Referenced by gisthandler().

295 {
296  ListCell *lc;
297 
298  /*
299  * Operator members of a GiST opfamily should never have hard
300  * dependencies, since their connection to the opfamily depends only on
301  * what the support functions think, and that can be altered. For
302  * consistency, we make all soft dependencies point to the opfamily,
303  * though a soft dependency on the opclass would work as well in the
304  * CREATE OPERATOR CLASS case.
305  */
306  foreach(lc, operators)
307  {
308  OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
309 
310  op->ref_is_hard = false;
311  op->ref_is_family = true;
312  op->refobjid = opfamilyoid;
313  }
314 
315  /*
316  * Required support functions should have hard dependencies. Preferably
317  * those are just dependencies on the opclass, but if we're in ALTER
318  * OPERATOR FAMILY, we leave the dependency pointing at the whole
319  * opfamily. (Given that GiST opclasses generally don't share opfamilies,
320  * it seems unlikely to be worth working harder.)
321  */
322  foreach(lc, functions)
323  {
324  OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
325 
326  switch (op->number)
327  {
329  case GIST_UNION_PROC:
330  case GIST_PENALTY_PROC:
331  case GIST_PICKSPLIT_PROC:
332  case GIST_EQUAL_PROC:
333  /* Required support function */
334  op->ref_is_hard = true;
335  break;
336  case GIST_COMPRESS_PROC:
338  case GIST_DISTANCE_PROC:
339  case GIST_FETCH_PROC:
340  case GIST_OPTIONS_PROC:
341  /* Optional, so force it to be a soft family dependency */
342  op->ref_is_hard = false;
343  op->ref_is_family = true;
344  op->refobjid = opfamilyoid;
345  break;
346  default:
347  ereport(ERROR,
348  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
349  errmsg("support function number %d is invalid for access method %s",
350  op->number, "gist")));
351  break;
352  }
353  }
354 }
#define GIST_OPTIONS_PROC
Definition: gist.h:39
bool ref_is_hard
Definition: amapi.h:88
#define GIST_FETCH_PROC
Definition: gist.h:38
int number
Definition: amapi.h:84
#define GIST_EQUAL_PROC
Definition: gist.h:36
int errcode(int sqlerrcode)
Definition: elog.c:610
#define GIST_PICKSPLIT_PROC
Definition: gist.h:35
#define ERROR
Definition: elog.h:43
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define ereport(elevel,...)
Definition: elog.h:144
#define GIST_CONSISTENT_PROC
Definition: gist.h:30
#define GIST_UNION_PROC
Definition: gist.h:31
#define lfirst(lc)
Definition: pg_list.h:169
#define GIST_PENALTY_PROC
Definition: gist.h:34
#define GIST_DISTANCE_PROC
Definition: gist.h:37
bool ref_is_family
Definition: amapi.h:89
int errmsg(const char *fmt,...)
Definition: elog.c:821
Oid refobjid
Definition: amapi.h:90
#define GIST_DECOMPRESS_PROC
Definition: gist.h:33

◆ gistbuild()

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

Definition at line 175 of file gistbuild.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, GiSTOptions::buffering_mode, GISTBuildState::buildMode, 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_indexsortbuild(), GIST_OPTION_BUFFERING_OFF, GIST_OPTION_BUFFERING_ON, GIST_ROOT_BLKNO, GIST_SORTED_BUILD, GIST_SORTSUPPORT_PROC, gistBuildCallback(), GistBuildLSN, gistEmptyAllBuffers(), gistFreeBuildBuffers(), GISTInitBuffer(), gistNewBuffer(), gistSortedBuildCallback(), GISTBuildState::giststate, IndexBuildResult::heap_tuples, GISTBuildState::heaprel, i, index_getprocid(), INDEX_MAX_KEYS, IndexBuildResult::index_tuples, GISTBuildState::indexrel, IndexRelationGetNumberOfKeyAttributes, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), log_newpage_range(), MAIN_FORKNUM, maintenance_work_mem, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, GistSortedBuildPageState::page, PageSetLSN, palloc(), RelationData::rd_options, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

176 {
177  IndexBuildResult *result;
178  double reltuples;
179  GISTBuildState buildstate;
181  int fillfactor;
182  Oid SortSupportFnOids[INDEX_MAX_KEYS];
184 
185  /*
186  * We expect to be called exactly once for any index relation. If that's
187  * not the case, big trouble's what we have.
188  */
189  if (RelationGetNumberOfBlocks(index) != 0)
190  elog(ERROR, "index \"%s\" already contains data",
191  RelationGetRelationName(index));
192 
193  buildstate.indexrel = index;
194  buildstate.heaprel = heap;
195  buildstate.sortstate = NULL;
196  buildstate.giststate = initGISTstate(index);
197 
198  /*
199  * Create a temporary memory context that is reset once for each tuple
200  * processed. (Note: we don't bother to make this a child of the
201  * giststate's scanCxt, so we have to delete it separately at the end.)
202  */
203  buildstate.giststate->tempCxt = createTempGistContext();
204 
205  /*
206  * Choose build strategy. First check whether the user specified to use
207  * buffering mode. (The use-case for that in the field is somewhat
208  * questionable perhaps, but it's important for testing purposes.)
209  */
210  if (options)
211  {
213  buildstate.buildMode = GIST_BUFFERING_STATS;
214  else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
215  buildstate.buildMode = GIST_BUFFERING_DISABLED;
216  else /* must be "auto" */
217  buildstate.buildMode = GIST_BUFFERING_AUTO;
218  }
219  else
220  {
221  buildstate.buildMode = GIST_BUFFERING_AUTO;
222  }
223 
224  /*
225  * Unless buffering mode was forced, see if we can use sorting instead.
226  */
227  if (buildstate.buildMode != GIST_BUFFERING_STATS)
228  {
229  bool hasallsortsupports = true;
230  int keyscount = IndexRelationGetNumberOfKeyAttributes(index);
231 
232  for (int i = 0; i < keyscount; i++)
233  {
234  SortSupportFnOids[i] = index_getprocid(index, i + 1,
236  if (!OidIsValid(SortSupportFnOids[i]))
237  {
238  hasallsortsupports = false;
239  break;
240  }
241  }
242  if (hasallsortsupports)
243  buildstate.buildMode = GIST_SORTED_BUILD;
244  }
245 
246  /*
247  * Calculate target amount of free space to leave on pages.
248  */
249  fillfactor = options ? options->fillfactor : GIST_DEFAULT_FILLFACTOR;
250  buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
251 
252  /*
253  * Build the index using the chosen strategy.
254  */
255  buildstate.indtuples = 0;
256  buildstate.indtuplesSize = 0;
257 
258  if (buildstate.buildMode == GIST_SORTED_BUILD)
259  {
260  /*
261  * Sort all data, build the index from bottom up.
262  */
263  buildstate.sortstate = tuplesort_begin_index_gist(heap,
264  index,
266  NULL,
267  false);
268 
269  /* Scan the table, adding all tuples to the tuplesort */
270  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
272  (void *) &buildstate, NULL);
273 
274  /*
275  * Perform the sort and build index pages.
276  */
277  tuplesort_performsort(buildstate.sortstate);
278 
279  gist_indexsortbuild(&buildstate);
280 
281  tuplesort_end(buildstate.sortstate);
282  }
283  else
284  {
285  /*
286  * Initialize an empty index and insert all tuples, possibly using
287  * buffers on intermediate levels.
288  */
289  Buffer buffer;
290  Page page;
291 
292  /* initialize the root page */
293  buffer = gistNewBuffer(index);
295  page = BufferGetPage(buffer);
296 
298 
299  GISTInitBuffer(buffer, F_LEAF);
300 
301  MarkBufferDirty(buffer);
302  PageSetLSN(page, GistBuildLSN);
303 
304  UnlockReleaseBuffer(buffer);
305 
307 
308  /* Scan the table, inserting all the tuples to the index. */
309  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
311  (void *) &buildstate, NULL);
312 
313  /*
314  * If buffering was used, flush out all the tuples that are still in
315  * the buffers.
316  */
317  if (buildstate.buildMode == GIST_BUFFERING_ACTIVE)
318  {
319  elog(DEBUG1, "all tuples processed, emptying buffers");
320  gistEmptyAllBuffers(&buildstate);
321  gistFreeBuildBuffers(buildstate.gfbb);
322  }
323 
324  /*
325  * We didn't write WAL records as we built the index, so if
326  * WAL-logging is required, write all pages to the WAL now.
327  */
328  if (RelationNeedsWAL(index))
329  {
331  0, RelationGetNumberOfBlocks(index),
332  true);
333  }
334  }
335 
336  /* okay, all heap tuples are indexed */
337  MemoryContextSwitchTo(oldcxt);
339 
340  freeGISTstate(buildstate.giststate);
341 
342  /*
343  * Return statistics
344  */
345  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
346 
347  result->heap_tuples = reltuples;
348  result->index_tuples = (double) buildstate.indtuples;
349 
350  return result;
351 }
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:2021
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
#define GistBuildLSN
Definition: gist.h:61
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:802
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1469
MemoryContext createTempGistContext(void)
Definition: gist.c:119
#define GIST_SORTSUPPORT_PROC
Definition: gist.h:40
#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
unsigned int Oid
Definition: postgres_ext.h:31
static void gist_indexsortbuild(GISTBuildState *state)
Definition: gistbuild.c:396
#define OidIsValid(objectId)
Definition: c.h:652
int64 indtuplesSize
Definition: gistbuild.c:99
static void gistSortedBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:362
Tuplesortstate * tuplesort_begin_index_gist(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, bool randomAccess)
Definition: tuplesort.c:1171
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3534
#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:1552
int64 indtuples
Definition: gistbuild.c:92
MemoryContext tempCxt
Definition: gist_private.h:78
Size freespace
Definition: gistbuild.c:88
#define RelationGetRelationName(relation)
Definition: rel.h:490
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:478
Relation heaprel
Definition: gistbuild.c:85
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1626
GistBuildMode buildMode
Definition: gistbuild.c:90
GISTSTATE * giststate
Definition: gistbuild.c:86
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:211
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1498
int maintenance_work_mem
Definition: globals.c:123
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:1342
#define Assert(condition)
Definition: c.h:746
Relation indexrel
Definition: gistbuild.c:84
#define INDEX_MAX_KEYS
#define RelationNeedsWAL(relation)
Definition: rel.h:562
void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1123
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2661
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
void * palloc(Size size)
Definition: mcxt.c:950
#define F_LEAF
Definition: gist.h:46
GISTBuildBuffers * gfbb
Definition: gistbuild.c:100
#define elog(elevel,...)
Definition: elog.h:214
int i
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:775
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:1445
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:826
bytea * rd_options
Definition: rel.h:157
Pointer Page
Definition: bufpage.h:78
double index_tuples
Definition: genam.h:33
double heap_tuples
Definition: genam.h:32
Tuplesortstate * sortstate
Definition: gistbuild.c:106
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:767

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

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

131 {
132  Buffer buffer;
133 
134  /* Initialize the root page */
135  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
137 
138  /* Initialize and xlog buffer */
140  GISTInitBuffer(buffer, F_LEAF);
141  MarkBufferDirty(buffer);
142  log_newpage_buffer(buffer, true);
144 
145  /* Unlock and release the buffer */
146  UnlockReleaseBuffer(buffer);
147 }
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1090
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1469
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:653
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3534
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3750
#define F_LEAF
Definition: gist.h:46
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:775
int Buffer
Definition: buf.h:23

◆ gistbulkdelete()

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

Definition at line 59 of file gistvacuum.c.

References gistvacuumscan(), and palloc0().

Referenced by gisthandler().

61 {
62  /* allocate stats if first time through, else re-use existing struct */
63  if (stats == NULL)
65 
66  gistvacuumscan(info, stats, callback, callback_state);
67 
68  return stats;
69 }
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
void * palloc0(Size size)
Definition: mcxt.c:981
static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125

◆ 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:38
#define OidIsValid(objectId)
Definition: c.h:652
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:767

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 787 of file gistutil.c.

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

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

788 {
789  Page page = BufferGetPage(buf);
790 
791  /*
792  * ReadBuffer verifies that every newly-read page passes
793  * PageHeaderIsValid, which means it either contains a reasonably sane
794  * page header or is all-zero. We have to defend against the all-zero
795  * case, however.
796  */
797  if (PageIsNew(page))
798  ereport(ERROR,
799  (errcode(ERRCODE_INDEX_CORRUPTED),
800  errmsg("index \"%s\" contains unexpected zero page at block %u",
803  errhint("Please REINDEX it.")));
804 
805  /*
806  * Additionally check that the special area looks sane.
807  */
808  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
809  ereport(ERROR,
810  (errcode(ERRCODE_INDEX_CORRUPTED),
811  errmsg("index \"%s\" contains corrupted page at block %u",
814  errhint("Please REINDEX it.")));
815 }
int errhint(const char *fmt,...)
Definition: elog.c:1068
int errcode(int sqlerrcode)
Definition: elog.c:610
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:68
#define RelationGetRelationName(relation)
Definition: rel.h:490
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define ereport(elevel,...)
Definition: elog.h:144
#define MAXALIGN(LEN)
Definition: c.h:699
#define PageGetSpecialSize(page)
Definition: bufpage.h:300
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2661
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:821
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:723
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:475
TupleDesc leafTupdesc
Definition: gist_private.h:80
#define GistPageIsLeaf(page)
Definition: gist.h:161
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:746
#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

◆ gistCompressValues()

void gistCompressValues ( GISTSTATE giststate,
Relation  r,
Datum attdata,
bool isnull,
bool  isleaf,
Datum compatt 
)

Definition at line 595 of file gistutil.c.

References GISTSTATE::compressFn, DatumGetPointer, FmgrInfo::fn_oid, FunctionCall1Coll(), gistentryinit, i, IndexRelationGetNumberOfKeyAttributes, GISTENTRY::key, TupleDescData::natts, OidIsValid, PointerGetDatum, RelationData::rd_att, and GISTSTATE::supportCollation.

Referenced by gistFormTuple(), and gistSortedBuildCallback().

597 {
598  int i;
599 
600  /*
601  * Call the compress method on each attribute.
602  */
603  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
604  {
605  if (isnull[i])
606  compatt[i] = (Datum) 0;
607  else
608  {
609  GISTENTRY centry;
610  GISTENTRY *cep;
611 
612  gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
613  isleaf);
614  /* there may not be a compress function in opclass */
615  if (OidIsValid(giststate->compressFn[i].fn_oid))
616  cep = (GISTENTRY *)
618  giststate->supportCollation[i],
619  PointerGetDatum(&centry)));
620  else
621  cep = &centry;
622  compatt[i] = cep->key;
623  }
624  }
625 
626  if (isleaf)
627  {
628  /*
629  * Emplace each included attribute if any.
630  */
631  for (; i < r->rd_att->natts; i++)
632  {
633  if (isnull[i])
634  compatt[i] = (Datum) 0;
635  else
636  compatt[i] = attdata[i];
637  }
638  }
639 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
#define OidIsValid(objectId)
Definition: c.h:652
uint16 OffsetNumber
Definition: off.h:24
Datum key
Definition: gist.h:152
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
uintptr_t Datum
Definition: postgres.h:367
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1132
TupleDesc rd_att
Definition: rel.h:110
Oid fn_oid
Definition: fmgr.h:59
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:236
#define DatumGetPointer(X)
Definition: postgres.h:549
int i

◆ 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:475
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:153
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
#define OidIsValid(objectId)
Definition: c.h:652
Page page
Definition: gist.h:154
Datum key
Definition: gist.h:152
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
bool leafkey
Definition: gist.h:156
uintptr_t Datum
Definition: postgres.h:367
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1132
Oid fn_oid
Definition: fmgr.h:59
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:236
#define DatumGetPointer(X)
Definition: postgres.h:549
OffsetNumber offset
Definition: gist.h:155

◆ gistdoinsert()

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

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

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

◆ gistFetchTuple()

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

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

667 {
668  MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
670  bool isnull[INDEX_MAX_KEYS];
671  int i;
672 
673  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
674  {
675  Datum datum;
676 
677  datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
678 
679  if (giststate->fetchFn[i].fn_oid != InvalidOid)
680  {
681  if (!isnull[i])
682  fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
683  else
684  fetchatt[i] = (Datum) 0;
685  }
686  else if (giststate->compressFn[i].fn_oid == InvalidOid)
687  {
688  /*
689  * If opclass does not provide compress method that could change
690  * original value, att is necessarily stored in original form.
691  */
692  if (!isnull[i])
693  fetchatt[i] = datum;
694  else
695  fetchatt[i] = (Datum) 0;
696  }
697  else
698  {
699  /*
700  * Index-only scans not supported for this column. Since the
701  * planner chose an index-only scan anyway, it is not interested
702  * in this column, and we can replace it with a NULL.
703  */
704  isnull[i] = true;
705  fetchatt[i] = (Datum) 0;
706  }
707  }
708 
709  /*
710  * Get each included attribute.
711  */
712  for (; i < r->rd_att->natts; i++)
713  {
714  fetchatt[i] = index_getattr(tuple, i + 1, giststate->leafTupdesc,
715  &isnull[i]);
716  }
717  MemoryContextSwitchTo(oldcxt);
718 
719  return heap_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
720 }
static Datum gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
Definition: gistutil.c:645
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:41
MemoryContext tempCxt
Definition: gist_private.h:78
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc rd_att
Definition: rel.h:110
#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 gist_indexsortbuild_pagestate_add(), gistplacetopage(), and gistRedoPageSplitRecord().

34 {
35  int i;
36 
37  if (off == InvalidOffsetNumber)
38  off = (PageIsEmpty(page)) ? FirstOffsetNumber :
40 
41  for (i = 0; i < len; i++)
42  {
43  Size sz = IndexTupleSize(itup[i]);
44  OffsetNumber l;
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:410
#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:474
#define elog(elevel,...)
Definition: elog.h:214
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:950
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:474
size_t Size
Definition: c.h:474
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistFormTuple()

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

Definition at line 574 of file gistutil.c.

References gistCompressValues(), index_form_tuple(), INDEX_MAX_KEYS, ItemPointerSetOffsetNumber, GISTSTATE::leafTupdesc, GISTSTATE::nonLeafTupdesc, and IndexTupleData::t_tid.

Referenced by gistBuildCallback(), gistgetadjusted(), gistinsert(), gistSplit(), and gistunion().

576 {
577  Datum compatt[INDEX_MAX_KEYS];
578  IndexTuple res;
579 
580  gistCompressValues(giststate, r, attdata, isnull, isleaf, compatt);
581 
582  res = index_form_tuple(isleaf ? giststate->leafTupdesc :
583  giststate->nonLeafTupdesc,
584  compatt, isnull);
585 
586  /*
587  * The offset number on tuples on internal pages is unused. For historical
588  * reasons, it is set to 0xffff.
589  */
590  ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
591  return res;
592 }
ItemPointerData t_tid
Definition: itup.h:37
IndexTuple index_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: indextuple.c:47
void gistCompressValues(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf, Datum *compatt)
Definition: gistutil.c:595
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define INDEX_MAX_KEYS
#define ItemPointerSetOffsetNumber(pointer, offsetNumber)
Definition: itemptr.h:148

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

◆ 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:475
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
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf)
Definition: gistutil.c:574
int i

◆ 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:137
MemoryContext pageDataCxt
Definition: gist_private.h:177
Relation indexRelation
Definition: relscan.h:115
void pfree(void *pointer)
Definition: mcxt.c:1057
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1491
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:54
HeapTuple xs_hitup
Definition: relscan.h:141
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1027 of file gistutil.c.

References Assert, FirstNormalUnloggedLSN, GetFakeLSNForUnloggedRel(), GetXLogInsertRecPtr(), gistXLogAssignLSN(), InvalidXLogRecPtr, RelationData::rd_rel, RelationNeedsWAL, and XLogRecPtrIsInvalid.

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

1028 {
1029  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1030  {
1031  /*
1032  * Temporary relations are only accessible in our session, so a simple
1033  * backend-local counter will do.
1034  */
1035  static XLogRecPtr counter = FirstNormalUnloggedLSN;
1036 
1037  return counter++;
1038  }
1039  else if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
1040  {
1041  /*
1042  * WAL-logging on this relation will start after commit, so its LSNs
1043  * must be distinct numbers smaller than the LSN at the next commit.
1044  * Emit a dummy WAL record if insert-LSN hasn't advanced after the
1045  * last call.
1046  */
1047  static XLogRecPtr lastlsn = InvalidXLogRecPtr;
1048  XLogRecPtr currlsn = GetXLogInsertRecPtr();
1049 
1050  /* Shouldn't be called for WAL-logging relations */
1051  Assert(!RelationNeedsWAL(rel));
1052 
1053  /* No need for an actual record if we already have a distinct LSN */
1054  if (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
1055  currlsn = gistXLogAssignLSN();
1056 
1057  lastlsn = currlsn;
1058  return currlsn;
1059  }
1060  else
1061  {
1062  /*
1063  * Unlogged relations are accessible from other backends, and survive
1064  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1065  */
1066  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1067  return GetFakeLSNForUnloggedRel();
1068  }
1069 }
XLogRecPtr gistXLogAssignLSN(void)
Definition: gistxlog.c:601
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
Form_pg_class rd_rel
Definition: rel.h:109
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4949
XLogRecPtr GetXLogInsertRecPtr(void)
Definition: xlog.c:11519
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
#define FirstNormalUnloggedLSN
Definition: xlogdefs.h:36
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:746
#define RelationNeedsWAL(relation)
Definition: rel.h:562

◆ 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:919
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
bool queuedForEmptying
Definition: gist_private.h:307
List * lcons(void *datum, List *list)
Definition: list.c:453
#define InvalidBlockNumber
Definition: block.h:33
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070
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:137
MemoryContext pageDataCxt
Definition: gist_private.h:177
Relation indexRelation
Definition: relscan.h:115
uint16 OffsetNumber
Definition: off.h:24
GISTSTATE * giststate
Definition: gist_private.h:156
void pfree(void *pointer)
Definition: mcxt.c:1057
#define ERROR
Definition: elog.h:43
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
ItemPointerData xs_heaptid
Definition: relscan.h:144
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:1491
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
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:54
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:39
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:950
HeapTuple xs_hitup
Definition: relscan.h:141
OffsetNumber offnum
Definition: gist_private.h:125
#define elog(elevel,...)
Definition: elog.h:214
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
bool kill_prior_tuple
Definition: relscan.h:125
MemoryContext scanCxt
Definition: gist_private.h:77
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
int numberOfOrderBys
Definition: relscan.h:118
union GISTSearchItem::@42 data
GistNSN parentlsn
Definition: gist_private.h:136

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 775 of file gistutil.c.

References BufferGetPage, gistinitpage(), and GistSortedBuildPageState::page.

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

776 {
777  Page page;
778 
779  page = BufferGetPage(b);
780  gistinitpage(page, f);
781 }
void gistinitpage(Page page, uint32 f)
Definition: gistutil.c:756
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Pointer Page
Definition: bufpage.h:78

◆ 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:91
#define HASH_ELEM
Definition: hsearch.h:85
MemoryContext hcxt
Definition: hsearch.h:77
MemoryContext context
Definition: gist_private.h:341
List ** buffersOnLevels
Definition: gist_private.h:368
Size entrysize
Definition: hsearch.h:72
uint32 BlockNumber
Definition: block.h:31
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:188
List * bufferEmptyingQueue
Definition: gist_private.h:357
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define HASH_BLOBS
Definition: hsearch.h:86
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:326
Size keysize
Definition: hsearch.h:71
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
void * palloc(Size size)
Definition: mcxt.c:950
Definition: pg_list.h:50

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 756 of file gistutil.c.

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

Referenced by gist_indexsortbuild(), gist_indexsortbuild_pagestate_flush(), and GISTInitBuffer().

757 {
758  GISTPageOpaque opaque;
759  Size pageSize = BLCKSZ;
760 
761  PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
762 
763  opaque = GistPageGetOpaque(page);
764  /* page was already zeroed by PageInit, so this is not needed: */
765  /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
766  opaque->rightlink = InvalidBlockNumber;
767  opaque->flags = f;
768  opaque->gist_page_id = GIST_PAGE_ID;
769 }
uint16 gist_page_id
Definition: gist.h:74
uint16 flags
Definition: gist.h:73
#define GistPageGetOpaque(page)
Definition: gist.h:159
size_t Size
Definition: c.h:474
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber rightlink
Definition: gist.h:72
#define GIST_PAGE_ID
Definition: gist.h:103
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ gistinsert()

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

Definition at line 156 of file gist.c.

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

Referenced by gisthandler().

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

◆ gistjoinvector()

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

Definition at line 113 of file gistutil.c.

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

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

◆ 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:227
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:228
#define GEVHDRSZ
Definition: gist.h:231
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:950
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:227
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1152
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:228
#define GEVHDRSZ
Definition: gist.h:231
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 826 of file gistutil.c.

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

Referenced by gistbuild(), and gistplacetopage().

827 {
828  Buffer buffer;
829  bool needLock;
830 
831  /* First, try to get a page from FSM */
832  for (;;)
833  {
834  BlockNumber blkno = GetFreeIndexPage(r);
835 
836  if (blkno == InvalidBlockNumber)
837  break; /* nothing left in FSM */
838 
839  buffer = ReadBuffer(r, blkno);
840 
841  /*
842  * We have to guard against the possibility that someone else already
843  * recycled this page; the buffer may be locked if so.
844  */
845  if (ConditionalLockBuffer(buffer))
846  {
847  Page page = BufferGetPage(buffer);
848 
849  /*
850  * If the page was never initialized, it's OK to use.
851  */
852  if (PageIsNew(page))
853  return buffer;
854 
855  gistcheckpage(r, buffer);
856 
857  /*
858  * Otherwise, recycle it if deleted, and too old to have any
859  * processes interested in it.
860  */
861  if (gistPageRecyclable(page))
862  {
863  /*
864  * If we are generating WAL for Hot Standby then create a WAL
865  * record that will allow us to conflict with queries running
866  * on standby, in case they have snapshots older than the
867  * page's deleteXid.
868  */
870  gistXLogPageReuse(r, blkno, GistPageGetDeleteXid(page));
871 
872  return buffer;
873  }
874 
875  LockBuffer(buffer, GIST_UNLOCK);
876  }
877 
878  /* Can't use it, so release buffer and try again */
879  ReleaseBuffer(buffer);
880  }
881 
882  /* Must extend the file */
883  needLock = !RELATION_IS_LOCAL(r);
884 
885  if (needLock)
887 
888  buffer = ReadBuffer(r, P_NEW);
889  LockBuffer(buffer, GIST_EXCLUSIVE);
890 
891  if (needLock)
893 
894  return buffer;
895 }
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:583
#define GIST_UNLOCK
Definition: gist_private.h:44
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3511
#define P_NEW
Definition: bufmgr.h:91
void gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid)
Definition: gistxlog.c:619
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3776
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:3750
#define XLogStandbyInfoActive()
Definition: xlog.h:205
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:787
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:607
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:562
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:207
bool gistPageRecyclable(Page page)
Definition: gistutil.c:899
#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:782
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 923 of file gistutil.c.

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

Referenced by gisthandler().

924 {
925  static const relopt_parse_elt tab[] = {
926  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
927  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
928  };
929 
930  return (bytea *) build_reloptions(reloptions, validate,
932  sizeof(GiSTOptions),
933  tab, lengthof(tab));
934 }
#define lengthof(array)
Definition: c.h:676
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:1887
int fillfactor
Definition: pgbench.c:160
Definition: c.h:563
#define offsetof(type, field)
Definition: c.h:669

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 899 of file gistutil.c.

References GistPageGetDeleteXid(), GistPageIsDeleted, GlobalVisIsRemovableFullXid(), and PageIsNew.

Referenced by gistNewBuffer(), and gistvacuumpage().

900 {
901  if (PageIsNew(page))
902  return true;
903  if (GistPageIsDeleted(page))
904  {
905  /*
906  * The page was deleted, but when? If it was just deleted, a scan
907  * might have seen the downlink to it, and will read the page later.
908  * As long as that can happen, we must keep the deleted page around as
909  * a tombstone.
910  *
911  * For that check if the deletion XID could still be visible to
912  * anyone. If not, then no scan that's still in progress could have
913  * seen its downlink, and we can recycle it.
914  */
915  FullTransactionId deletexid_full = GistPageGetDeleteXid(page);
916 
917  return GlobalVisIsRemovableFullXid(NULL, deletexid_full);
918  }
919  return false;
920 }
#define GistPageIsDeleted(page)
Definition: gist.h:164
bool GlobalVisIsRemovableFullXid(Relation rel, FullTransactionId fxid)
Definition: procarray.c:4080
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:207
#define PageIsNew(page)
Definition: bufpage.h:229

◆ gistpenalty()

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

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

726 {
727  float penalty = 0.0;
728 
729  if (giststate->penaltyFn[attno].fn_strict == false ||
730  (isNullOrig == false && isNullAdd == false))
731  {
732  FunctionCall3Coll(&giststate->penaltyFn[attno],
733  giststate->supportCollation[attno],
734  PointerGetDatum(orig),
735  PointerGetDatum(add),
736  PointerGetDatum(&penalty));
737  /* disallow negative or NaN penalty */
738  if (isnan(penalty) || penalty < 0.0)
739  penalty = 0.0;
740  }
741  else if (isNullOrig && isNullAdd)
742  penalty = 0.0;
743  else
744  {
745  /* try to prevent mixing null and non-null values */
746  penalty = get_float4_infinity();
747  }
748 
749  return penalty;
750 }
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:1174
static float4 get_float4_infinity(void)
Definition: float.h:73

◆ 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 221 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(), 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().

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

◆ gistproperty()

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

Definition at line 944 of file gistutil.c.

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

Referenced by gisthandler().

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

◆ gistPushItupToNodeBuffer()

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

Definition at line 341 of file gistbuildbuffers.c.

References 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:453
BlockNumber prev
Definition: gist_private.h:48
#define MAXALIGN(LEN)
Definition: c.h:699
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
#define offsetof(type, field)
Definition: c.h:669

◆ 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, IndexRelationGetNumberOfKeyAttributes, InvalidBlockNumber, RelocationBufferInfo::isnull, GISTNodeBuffer::isTemp, LEVEL_HAS_BUFFERS, lfirst, list_length(), RelocationBufferInfo::nodeBuffer, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, palloc(), pfree(), 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 < IndexRelationGetNumberOfKeyAttributes(r); 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 
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:919
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:1057
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
Definition: gistutil.c:723
IndexTuple downlink
Definition: gist_private.h:421
GISTNodeBuffer * gistGetNodeBuffer(GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:475
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
#define INDEX_MAX_KEYS
#define InvalidBlockNumber
Definition: block.h:33
static int list_length(const List *l)
Definition: pg_list.h:149
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2661
void * palloc(Size size)
Definition: mcxt.c:950
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 1411 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().

1416 {
1417  IndexTuple *lvectup,
1418  *rvectup;
1419  GistSplitVector v;
1420  int i;
1421  SplitedPageLayout *res = NULL;
1422 
1423  /* this should never recurse very deeply, but better safe than sorry */
1425 
1426  /* there's no point in splitting an empty page */
1427  Assert(len > 0);
1428 
1429  /*
1430  * If a single tuple doesn't fit on a page, no amount of splitting will
1431  * help.
1432  */
1433  if (len == 1)
1434  ereport(ERROR,
1435  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1436  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1437  IndexTupleSize(itup[0]), GiSTPageSize,
1439 
1440  memset(v.spl_lisnull, true,
1441  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1442  memset(v.spl_risnull, true,
1443  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1444  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1445 
1446  /* form left and right vector */
1447  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1448  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1449 
1450  for (i = 0; i < v.splitVector.spl_nleft; i++)
1451  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1452 
1453  for (i = 0; i < v.splitVector.spl_nright; i++)
1454  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1455 
1456  /* finalize splitting (may need another split) */
1457  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1458  {
1459  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1460  }
1461  else
1462  {
1463  ROTATEDIST(res);
1464  res->block.num = v.splitVector.spl_nright;
1465  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1466  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1467  }
1468 
1469  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1470  {
1471  SplitedPageLayout *resptr,
1472  *subres;
1473 
1474  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1475 
1476  /* install on list's tail */
1477  while (resptr->next)
1478  resptr = resptr->next;
1479 
1480  resptr->next = res;
1481  res = subres;
1482  }
1483  else
1484  {
1485  ROTATEDIST(res);
1486  res->block.num = v.splitVector.spl_nleft;
1487  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1488  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1489  }
1490 
1491  return res;
1492 }
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
OffsetNumber * spl_left
Definition: gist.h:134
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int errcode(int sqlerrcode)
Definition: elog.c:610
int spl_nleft
Definition: gist.h:135
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:140
void check_stack_depth(void)
Definition: postgres.c:3312
#define RelationGetRelationName(relation)
Definition: rel.h:490
struct SplitedPageLayout * next
Definition: gist_private.h:200
#define GiSTPageSize
Definition: gist_private.h:474
#define ROTATEDIST(d)
Definition: gist.c:45
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1411
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define ereport(elevel,...)
Definition: elog.h:144
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:78
#define Assert(condition)
Definition: c.h:746
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf)
Definition: gistutil.c:574
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:126
OffsetNumber * spl_right
Definition: gist.h:139
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
void * palloc(Size size)
Definition: mcxt.c:950
int errmsg(const char *fmt,...)
Definition: elog.c:821
int i
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:134
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int32 n
Definition: gist.h:227
int spl_nleft
Definition: gist.h:135
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:228
#define GEVHDRSZ
Definition: gist.h:231
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:140
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:746
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585
OffsetNumber * spl_right
Definition: gist.h:139
#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:950
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.

Referenced by gist_indexsortbuild_pagestate_flush().

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 75 of file gistvacuum.c.

References IndexVacuumInfo::analyze_only, IndexVacuumInfo::estimated_count, gistvacuumscan(), IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, and palloc0().

Referenced by gisthandler().

76 {
77  /* No-op in ANALYZE ONLY mode */
78  if (info->analyze_only)
79  return stats;
80 
81  /*
82  * If gistbulkdelete was called, we need not do anything, just return the
83  * stats from the latest gistbulkdelete call. If it wasn't called, we
84  * still need to do a pass over the index, to obtain index statistics.
85  */
86  if (stats == NULL)
87  {
89  gistvacuumscan(info, stats, NULL, NULL);
90  }
91 
92  /*
93  * It's quite possible for us to be fooled by concurrent page splits into
94  * double-counting some index tuples, so disbelieve any total that exceeds
95  * the underlying heap's count ... if we know that accurately. Otherwise
96  * this might just make matters worse.
97  */
98  if (!info->estimated_count)
99  {
100  if (stats->num_index_tuples > info->num_heap_tuples)
101  stats->num_index_tuples = info->num_heap_tuples;
102  }
103 
104  return stats;
105 }
bool analyze_only
Definition: genam.h:47
void * palloc0(Size size)
Definition: mcxt.c:981
double num_heap_tuples
Definition: genam.h:51
double num_index_tuples
Definition: genam.h:77
bool estimated_count
Definition: genam.h:49
static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

References AMOPSTRATEGY, AMPROCNUM, check_amop_signature(), check_amoptsproc_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_OPTIONS_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_SORTSUPPORT_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  case GIST_OPTIONS_PROC:
144  ok = check_amoptsproc_signature(procform->amproc);
145  break;
147  ok = check_amproc_signature(procform->amproc, VOIDOID, true,
148  1, 1, INTERNALOID);
149  break;
150  default:
151  ereport(INFO,
152  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
153  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
154  opfamilyname, "gist",
155  format_procedure(procform->amproc),
156  procform->amprocnum)));
157  result = false;
158  continue; /* don't want additional message */
159  }
160 
161  if (!ok)
162  {
163  ereport(INFO,
164  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
165  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
166  opfamilyname, "gist",
167  format_procedure(procform->amproc),
168  procform->amprocnum)));
169  result = false;
170  }
171  }
172 
173  /* Check individual operators */
174  for (i = 0; i < oprlist->n_members; i++)
175  {
176  HeapTuple oprtup = &oprlist->members[i]->tuple;
177  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
178  Oid op_rettype;
179 
180  /* TODO: Check that only allowed strategy numbers exist */
181  if (oprform->amopstrategy < 1)
182  {
183  ereport(INFO,
184  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
185  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
186  opfamilyname, "gist",
187  format_operator(oprform->amopopr),
188  oprform->amopstrategy)));
189  result = false;
190  }
191 
192  /* GiST supports ORDER BY operators */
193  if (oprform->amoppurpose != AMOP_SEARCH)
194  {
195  /* ... but must have matching distance proc */
196  if (!OidIsValid(get_opfamily_proc(opfamilyoid,
197  oprform->amoplefttype,
198  oprform->amoplefttype,
200  {
201  ereport(INFO,
202  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
203  errmsg("operator family \"%s\" of access method %s contains unsupported ORDER BY specification for operator %s",
204  opfamilyname, "gist",
205  format_operator(oprform->amopopr))));
206  result = false;
207  }
208  /* ... and operator result must match the claimed btree opfamily */
209  op_rettype = get_op_rettype(oprform->amopopr);
210  if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
211  {
212  ereport(INFO,
213  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
214  errmsg("operator family \"%s\" of access method %s contains incorrect ORDER BY opfamily specification for operator %s",
215  opfamilyname, "gist",
216  format_operator(oprform->amopopr))));
217  result = false;
218  }
219  }
220  else
221  {
222  /* Search operators must always return bool */
223  op_rettype = BOOLOID;
224  }
225 
226  /* Check operator signature */