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, bool indexUnchanged, 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.

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

◆ BUFFER_PAGE_DATA_OFFSET

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

Definition at line 53 of file gist_private.h.

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 479 of file gist_private.h.

◆ GIST_EXCLUSIVE

#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE

Definition at line 43 of file gist_private.h.

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 478 of file gist_private.h.

◆ GIST_ROOT_BLKNO

#define GIST_ROOT_BLKNO   0

Definition at line 262 of file gist_private.h.

◆ GIST_SHARE

#define GIST_SHARE   BUFFER_LOCK_SHARE

Definition at line 42 of file gist_private.h.

◆ GIST_UNLOCK

#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK

Definition at line 44 of file gist_private.h.

◆ GiSTPageSize

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

Definition at line 475 of file gist_private.h.

◆ GISTSearchItemIsHeap

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

Definition at line 145 of file gist_private.h.

◆ GistTupleIsInvalid

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

Definition at line 288 of file gist_private.h.

◆ GistTupleSetValid

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

Definition at line 289 of file gist_private.h.

◆ 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.

◆ PAGE_FREE_SPACE

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

Definition at line 55 of file gist_private.h.

◆ PAGE_IS_EMPTY

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

Definition at line 57 of file gist_private.h.

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

Definition at line 59 of file gist_private.h.

◆ SizeOfGISTSearchItem

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

Definition at line 147 of file gist_private.h.

◆ 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.

385 {
GistOptBufferingMode
Definition: gist_private.h:385
@ GIST_OPTION_BUFFERING_OFF
Definition: gist_private.h:388
@ GIST_OPTION_BUFFERING_AUTO
Definition: gist_private.h:386
@ GIST_OPTION_BUFFERING_ON
Definition: gist_private.h:387

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 121 of file gist.c.

122 {
124  "GiST temporary context",
126 }
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1633 of file gist.c.

1634 {
1635  /* It's sufficient to delete the scanCxt */
1636  MemoryContextDelete(giststate->scanCxt);
1637 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
MemoryContext scanCxt
Definition: gist_private.h:77

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

◆ gistadjustmembers()

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

Definition at line 291 of file gistvalidate.c.

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:
342  /* Optional, so force it to be a soft family dependency */
343  op->ref_is_hard = false;
344  op->ref_is_family = true;
345  op->refobjid = opfamilyoid;
346  break;
347  default:
348  ereport(ERROR,
349  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
350  errmsg("support function number %d is invalid for access method %s",
351  op->number, "gist")));
352  break;
353  }
354  }
355 }
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
#define GIST_DECOMPRESS_PROC
Definition: gist.h:33
#define GIST_PICKSPLIT_PROC
Definition: gist.h:35
#define GIST_CONSISTENT_PROC
Definition: gist.h:30
#define GIST_UNION_PROC
Definition: gist.h:31
#define GIST_FETCH_PROC
Definition: gist.h:38
#define GIST_SORTSUPPORT_PROC
Definition: gist.h:40
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define GIST_PENALTY_PROC
Definition: gist.h:34
#define GIST_OPTIONS_PROC
Definition: gist.h:39
#define GIST_DISTANCE_PROC
Definition: gist.h:37
#define GIST_EQUAL_PROC
Definition: gist.h:36
#define lfirst(lc)
Definition: pg_list.h:169
static const struct fns functions
Definition: regcomp.c:313
Oid refobjid
Definition: amapi.h:90
bool ref_is_family
Definition: amapi.h:89
int number
Definition: amapi.h:84
bool ref_is_hard
Definition: amapi.h:88

References ereport, errcode(), errmsg(), ERROR, functions, 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, lfirst, OpFamilyMember::number, OpFamilyMember::ref_is_family, OpFamilyMember::ref_is_hard, and OpFamilyMember::refobjid.

Referenced by gisthandler().

◆ gistbuild()

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

Definition at line 182 of file gistbuild.c.

183 {
184  IndexBuildResult *result;
185  double reltuples;
186  GISTBuildState buildstate;
188  int fillfactor;
189  Oid SortSupportFnOids[INDEX_MAX_KEYS];
190  GiSTOptions *options = (GiSTOptions *) index->rd_options;
191 
192  /*
193  * We expect to be called exactly once for any index relation. If that's
194  * not the case, big trouble's what we have.
195  */
197  elog(ERROR, "index \"%s\" already contains data",
199 
200  buildstate.indexrel = index;
201  buildstate.heaprel = heap;
202  buildstate.sortstate = NULL;
203  buildstate.giststate = initGISTstate(index);
204 
205  /*
206  * Create a temporary memory context that is reset once for each tuple
207  * processed. (Note: we don't bother to make this a child of the
208  * giststate's scanCxt, so we have to delete it separately at the end.)
209  */
210  buildstate.giststate->tempCxt = createTempGistContext();
211 
212  /*
213  * Choose build strategy. First check whether the user specified to use
214  * buffering mode. (The use-case for that in the field is somewhat
215  * questionable perhaps, but it's important for testing purposes.)
216  */
217  if (options)
218  {
219  if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
220  buildstate.buildMode = GIST_BUFFERING_STATS;
221  else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
222  buildstate.buildMode = GIST_BUFFERING_DISABLED;
223  else /* must be "auto" */
224  buildstate.buildMode = GIST_BUFFERING_AUTO;
225  }
226  else
227  {
228  buildstate.buildMode = GIST_BUFFERING_AUTO;
229  }
230 
231  /*
232  * Unless buffering mode was forced, see if we can use sorting instead.
233  */
234  if (buildstate.buildMode != GIST_BUFFERING_STATS)
235  {
236  bool hasallsortsupports = true;
238 
239  for (int i = 0; i < keyscount; i++)
240  {
241  SortSupportFnOids[i] = index_getprocid(index, i + 1,
243  if (!OidIsValid(SortSupportFnOids[i]))
244  {
245  hasallsortsupports = false;
246  break;
247  }
248  }
249  if (hasallsortsupports)
250  buildstate.buildMode = GIST_SORTED_BUILD;
251  }
252 
253  /*
254  * Calculate target amount of free space to leave on pages.
255  */
257  buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
258 
259  /*
260  * Build the index using the chosen strategy.
261  */
262  buildstate.indtuples = 0;
263  buildstate.indtuplesSize = 0;
264 
265  if (buildstate.buildMode == GIST_SORTED_BUILD)
266  {
267  /*
268  * Sort all data, build the index from bottom up.
269  */
270  buildstate.sortstate = tuplesort_begin_index_gist(heap,
271  index,
273  NULL,
275 
276  /* Scan the table, adding all tuples to the tuplesort */
277  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
279  (void *) &buildstate, NULL);
280 
281  /*
282  * Perform the sort and build index pages.
283  */
284  tuplesort_performsort(buildstate.sortstate);
285 
286  gist_indexsortbuild(&buildstate);
287 
288  tuplesort_end(buildstate.sortstate);
289  }
290  else
291  {
292  /*
293  * Initialize an empty index and insert all tuples, possibly using
294  * buffers on intermediate levels.
295  */
296  Buffer buffer;
297  Page page;
298 
299  /* initialize the root page */
300  buffer = gistNewBuffer(index);
302  page = BufferGetPage(buffer);
303 
305 
306  GISTInitBuffer(buffer, F_LEAF);
307 
308  MarkBufferDirty(buffer);
309  PageSetLSN(page, GistBuildLSN);
310 
311  UnlockReleaseBuffer(buffer);
312 
314 
315  /* Scan the table, inserting all the tuples to the index. */
316  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
318  (void *) &buildstate, NULL);
319 
320  /*
321  * If buffering was used, flush out all the tuples that are still in
322  * the buffers.
323  */
324  if (buildstate.buildMode == GIST_BUFFERING_ACTIVE)
325  {
326  elog(DEBUG1, "all tuples processed, emptying buffers");
327  gistEmptyAllBuffers(&buildstate);
328  gistFreeBuildBuffers(buildstate.gfbb);
329  }
330 
331  /*
332  * We didn't write WAL records as we built the index, so if
333  * WAL-logging is required, write all pages to the WAL now.
334  */
335  if (RelationNeedsWAL(index))
336  {
339  true);
340  }
341  }
342 
343  /* okay, all heap tuples are indexed */
344  MemoryContextSwitchTo(oldcxt);
346 
347  freeGISTstate(buildstate.giststate);
348 
349  /*
350  * Return statistics
351  */
352  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
353 
354  result->heap_tuples = reltuples;
355  result->index_tuples = (double) buildstate.indtuples;
356 
357  return result;
358 }
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2755
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3938
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1573
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:216
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Pointer Page
Definition: bufpage.h:78
#define PageSetLSN(page, lsn)
Definition: bufpage.h:367
#define OidIsValid(objectId)
Definition: c.h:710
#define DEBUG1
Definition: elog.h:24
#define elog(elevel,...)
Definition: elog.h:218
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1505
MemoryContext createTempGistContext(void)
Definition: gist.c:121
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1633
#define F_LEAF
Definition: gist.h:46
#define GistBuildLSN
Definition: gist.h:67
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:479
@ GIST_BUFFERING_AUTO
Definition: gistbuild.c:72
@ GIST_SORTED_BUILD
Definition: gistbuild.c:69
@ GIST_BUFFERING_ACTIVE
Definition: gistbuild.c:78
@ GIST_BUFFERING_STATS
Definition: gistbuild.c:75
@ GIST_BUFFERING_DISABLED
Definition: gistbuild.c:70
static void gist_indexsortbuild(GISTBuildState *state)
Definition: gistbuild.c:403
static void gistBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:884
static void gistSortedBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:369
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:1425
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:824
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
int maintenance_work_mem
Definition: globals.c:127
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:769
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void * palloc(Size size)
Definition: mcxt.c:1068
#define START_CRIT_SECTION()
Definition: miscadmin.h:148
#define END_CRIT_SECTION()
Definition: miscadmin.h:150
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define INDEX_MAX_KEYS
int fillfactor
Definition: pgbench.c:200
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:523
#define RelationNeedsWAL(relation)
Definition: rel.h:613
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:508
@ MAIN_FORKNUM
Definition: relpath.h:43
GISTBuildBuffers * gfbb
Definition: gistbuild.c:100
Relation indexrel
Definition: gistbuild.c:84
GISTSTATE * giststate
Definition: gistbuild.c:86
int64 indtuples
Definition: gistbuild.c:92
Size freespace
Definition: gistbuild.c:88
Relation heaprel
Definition: gistbuild.c:85
int64 indtuplesSize
Definition: gistbuild.c:99
Tuplesortstate * sortstate
Definition: gistbuild.c:106
GistBuildMode buildMode
Definition: gistbuild.c:90
MemoryContext tempCxt
Definition: gist_private.h:78
double heap_tuples
Definition: genam.h:32
double index_tuples
Definition: genam.h:33
Definition: type.h:90
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:1747
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:2196
Tuplesortstate * tuplesort_begin_index_gist(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
Definition: tuplesort.c:1338
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:1620
#define TUPLESORT_NONE
Definition: tuplesort.h:90
void log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1210

References Assert(), BufferGetBlockNumber(), BufferGetPage, GISTBuildState::buildMode, createTempGistContext(), CurrentMemoryContext, DEBUG1, elog, END_CRIT_SECTION, ERROR, F_LEAF, 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, if(), 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, PageSetLSN, palloc(), RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), TUPLESORT_NONE, tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 132 of file gist.c.

133 {
134  Buffer buffer;
135 
136  /* Initialize the root page */
139 
140  /* Initialize and xlog buffer */
142  GISTInitBuffer(buffer, F_LEAF);
143  MarkBufferDirty(buffer);
144  log_newpage_buffer(buffer, true);
146 
147  /* Unlock and release the buffer */
148  UnlockReleaseBuffer(buffer);
149 }
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4156
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:749
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
@ RBM_NORMAL
Definition: bufmgr.h:39
@ INIT_FORKNUM
Definition: relpath.h:46
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1177

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().

◆ gistbulkdelete()

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

Definition at line 59 of file gistvacuum.c.

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 gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125
void * palloc0(Size size)
Definition: mcxt.c:1099
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48

References callback(), gistvacuumscan(), and palloc0().

Referenced by gisthandler().

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

796 {
800  return true;
801  else
802  return false;
803 }

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

Referenced by gisthandler().

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 785 of file gistutil.c.

786 {
787  Page page = BufferGetPage(buf);
788 
789  /*
790  * ReadBuffer verifies that every newly-read page passes
791  * PageHeaderIsValid, which means it either contains a reasonably sane
792  * page header or is all-zero. We have to defend against the all-zero
793  * case, however.
794  */
795  if (PageIsNew(page))
796  ereport(ERROR,
797  (errcode(ERRCODE_INDEX_CORRUPTED),
798  errmsg("index \"%s\" contains unexpected zero page at block %u",
801  errhint("Please REINDEX it.")));
802 
803  /*
804  * Additionally check that the special area looks sane.
805  */
806  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
807  ereport(ERROR,
808  (errcode(ERRCODE_INDEX_CORRUPTED),
809  errmsg("index \"%s\" contains corrupted page at block %u",
812  errhint("Please REINDEX it.")));
813 }
#define PageGetSpecialSize(page)
Definition: bufpage.h:299
#define PageIsNew(page)
Definition: bufpage.h:228
#define MAXALIGN(LEN)
Definition: c.h:757
int errhint(const char *fmt,...)
Definition: elog.c:1151
static char * buf
Definition: pg_test_fsync.c:67

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

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

◆ gistchoose()

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

Definition at line 374 of file gistutil.c.

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

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

Referenced by gistdoinsert(), and gistProcessItup().

◆ gistCompressValues()

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

Definition at line 596 of file gistutil.c.

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

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().

◆ gistDeCompressAtt()

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

Definition at line 296 of file gistutil.c.

298 {
299  int i;
300 
301  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
302  {
303  Datum datum;
304 
305  datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
306  gistdentryinit(giststate, i, &attdata[i],
307  datum, r, p, o,
308  false, isnull[i]);
309  }
310 }

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

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

◆ gistdentryinit()

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

Definition at line 547 of file gistutil.c.

550 {
551  if (!isNull)
552  {
553  GISTENTRY *dep;
554 
555  gistentryinit(*e, k, r, pg, o, l);
556 
557  /* there may not be a decompress function in opclass */
558  if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
559  return;
560 
561  dep = (GISTENTRY *)
563  giststate->supportCollation[nkey],
564  PointerGetDatum(e)));
565  /* decompressFn may just return the given pointer */
566  if (dep != e)
567  gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
568  dep->leafkey);
569  }
570  else
571  gistentryinit(*e, (Datum) 0, r, pg, o, l);
572 }
e
Definition: preproc-init.c:82
OffsetNumber offset
Definition: gist.h:161
Page page
Definition: gist.h:160
Relation rel
Definition: gist.h:159
bool leafkey
Definition: gist.h:162
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89

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().

◆ gistdoinsert()

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

Definition at line 634 of file gist.c.

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

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

Referenced by gistBuildCallback(), and gistinsert().

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 95 of file gistutil.c.

96 {
98  maxoff;
99  IndexTuple *itvec;
100 
101  maxoff = PageGetMaxOffsetNumber(page);
102  *len = maxoff;
103  itvec = palloc(sizeof(IndexTuple) * maxoff);
104  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
105  itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
106 
107  return itvec;
108 }
const void size_t len

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

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistFetchTuple()

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

Definition at line 667 of file gistutil.c.

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

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().

◆ gistfillbuffer()

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

Definition at line 34 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_levelstate_add(), gistplacetopage(), and gistRedoPageSplitRecord().

◆ gistfillitupvec()

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

Definition at line 127 of file gistutil.c.

128 {
129  char *ptr,
130  *ret;
131  int i;
132 
133  *memlen = 0;
134 
135  for (i = 0; i < veclen; i++)
136  *memlen += IndexTupleSize(vec[i]);
137 
138  ptr = ret = palloc(*memlen);
139 
140  for (i = 0; i < veclen; i++)
141  {
142  memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
143  ptr += IndexTupleSize(vec[i]);
144  }
145 
146  return (IndexTupleData *) ret;
147 }

References i, IndexTupleSize, and palloc().

Referenced by gist_indexsortbuild_levelstate_flush(), gistplacetopage(), and gistSplit().

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 79 of file gistutil.c.

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

References GiSTPageSize, i, IndexTupleSize, and len.

Referenced by gistSplit().

◆ gistFormTuple()

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

Definition at line 575 of file gistutil.c.

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

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

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 511 of file gistbuildbuffers.c.

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

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

◆ gistgetadjusted()

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

Definition at line 316 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

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 }
XLogRecPtr GistNSN
Definition: gist.h:60
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:540
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:329
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
void pfree(void *pointer)
Definition: mcxt.c:1175
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:540
OffsetNumber nPageData
Definition: gist_private.h:175
OffsetNumber curPageData
Definition: gist_private.h:176
MemoryContext pageDataCxt
Definition: gist_private.h:177
IndexOrderByDistance distances[FLEXIBLE_ARRAY_MEMBER]
Definition: gist_private.h:142
HeapTuple xs_hitup
Definition: relscan.h:144
Relation indexRelation
Definition: relscan.h:118

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

Referenced by gisthandler().

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1025 of file gistutil.c.

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

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

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

◆ gistGetNodeBuffer()

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

Definition at line 117 of file gistbuildbuffers.c.

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

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().

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 614 of file gistget.c.

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 }
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:562
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:39
#define MaxIndexTuplesPerPage
Definition: itup.h:144
@ ForwardScanDirection
Definition: sdir.h:26
OffsetNumber * killedItems
Definition: gist_private.h:168
GISTSTATE * giststate
Definition: gist_private.h:156
GISTSearchHeapItem pageData[BLCKSZ/sizeof(IndexTupleData)]
Definition: gist_private.h:174
BlockNumber curBlkno
Definition: gist_private.h:170
ItemPointerData heapPtr
Definition: gist_private.h:120
OffsetNumber offnum
Definition: gist_private.h:125
BlockNumber blkno
Definition: gist_private.h:133
union GISTSearchItem::@44 data
GistNSN parentlsn
Definition: gist_private.h:136
int numberOfOrderBys
Definition: relscan.h:121
bool kill_prior_tuple
Definition: relscan.h:128
ItemPointerData xs_heaptid
Definition: relscan.h:147

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

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 773 of file gistutil.c.

774 {
775  Page page;
776 
777  page = BufferGetPage(b);
778  gistinitpage(page, f);
779 }
void gistinitpage(Page page, uint32 f)
Definition: gistutil.c:757
int b
Definition: isn.c:70

References b, BufferGetPage, and gistinitpage().

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

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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  hashCtl.keysize = sizeof(BlockNumber);
80  hashCtl.entrysize = sizeof(GISTNodeBuffer);
81  hashCtl.hcxt = CurrentMemoryContext;
82  gfbb->nodeBuffersTab = hash_create("gistbuildbuffers",
83  1024,
84  &hashCtl,
86 
87  gfbb->bufferEmptyingQueue = NIL;
88 
89  /*
90  * Per-level node buffers lists for final buffers emptying process. Node
91  * buffers are inserted here when they are created.
92  */
93  gfbb->buffersOnLevelsLen = 1;
94  gfbb->buffersOnLevels = (List **) palloc(sizeof(List *) *
95  gfbb->buffersOnLevelsLen);
96  gfbb->buffersOnLevels[0] = NIL;
97 
98  /*
99  * Block numbers of node buffers which last pages are currently loaded
100  * into main memory.
101  */
102  gfbb->loadedBuffersLen = 32;
104  sizeof(GISTNodeBuffer *));
105  gfbb->loadedBuffersCount = 0;
106 
107  gfbb->rootlevel = maxLevel;
108 
109  return gfbb;
110 }
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:188
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:349
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
List * bufferEmptyingQueue
Definition: gist_private.h:357
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86

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().

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 757 of file gistutil.c.

758 {
759  GISTPageOpaque opaque;
760 
761  PageInit(page, BLCKSZ, sizeof(GISTPageOpaqueData));
762 
763  opaque = GistPageGetOpaque(page);
764  opaque->rightlink = InvalidBlockNumber;
765  opaque->flags = f;
766  opaque->gist_page_id = GIST_PAGE_ID;
767 }
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42
#define GIST_PAGE_ID
Definition: gist.h:109
#define GistPageGetOpaque(page)
Definition: gist.h:165
uint16 gist_page_id
Definition: gist.h:80
BlockNumber rightlink
Definition: gist.h:78
uint16 flags
Definition: gist.h:79

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

Referenced by gist_indexsortbuild(), gist_indexsortbuild_levelstate_add(), gist_indexsortbuild_levelstate_flush(), and GISTInitBuffer().

◆ gistinsert()

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

Definition at line 158 of file gist.c.

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

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

Referenced by gisthandler().

◆ gistjoinvector()

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

Definition at line 114 of file gistutil.c.

115 {
116  itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen));
117  memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
118  *len += addlen;
119  return itvec;
120 }

References len, and repalloc().

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistKeyIsEQ()

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

Definition at line 281 of file gistutil.c.

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

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

Referenced by gistgetadjusted(), and gistUserPicksplit().

◆ gistMakeUnionItVec()

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

Definition at line 155 of file gistutil.c.

157 {
158  int i;
159  GistEntryVector *evec;
160  int attrsize;
161 
162  evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
163 
164  for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
165  {
166  int j;
167 
168  /* Collect non-null datums for this column */
169  evec->n = 0;
170  for (j = 0; j < len; j++)
171  {
172  Datum datum;
173  bool IsNull;
174 
175  datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
176  &IsNull);
177  if (IsNull)
178  continue;
179 
180  gistdentryinit(giststate, i,
181  evec->vector + evec->n,
182  datum,
183  NULL, NULL, (OffsetNumber) 0,
184  false, IsNull);
185  evec->n++;
186  }
187 
188  /* If this column was all NULLs, the union is NULL */
189  if (evec->n == 0)
190  {
191  attr[i] = (Datum) 0;
192  isnull[i] = true;
193  }
194  else
195  {
196  if (evec->n == 1)
197  {
198  /* unionFn may expect at least two inputs */
199  evec->n = 2;
200  evec->vector[1] = evec->vector[0];
201  }
202 
203  /* Make union and store in attr array */
204  attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
205  giststate->supportCollation[i],
206  PointerGetDatum(evec),
207  PointerGetDatum(&attrsize));
208 
209  isnull[i] = false;
210  }
211  }
212 }
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1134
struct GISTENTRY GISTENTRY
#define GEVHDRSZ
Definition: gist.h:237
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:234
int32 n
Definition: gist.h:233

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

Referenced by gistunion(), and gistunionsubkeyvec().

◆ gistMakeUnionKey()

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

Definition at line 233 of file gistutil.c.

237 {
238  /* we need a GistEntryVector with room for exactly 2 elements */
239  union
240  {
241  GistEntryVector gev;
242  char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
243  } storage;
244  GistEntryVector *evec = &storage.gev;
245  int dstsize;
246 
247  evec->n = 2;
248 
249  if (isnull1 && isnull2)
250  {
251  *dstisnull = true;
252  *dst = (Datum) 0;
253  }
254  else
255  {
256  if (isnull1 == false && isnull2 == false)
257  {
258  evec->vector[0] = *entry1;
259  evec->vector[1] = *entry2;
260  }
261  else if (isnull1 == false)
262  {
263  evec->vector[0] = *entry1;
264  evec->vector[1] = *entry1;
265  }
266  else
267  {
268  evec->vector[0] = *entry2;
269  evec->vector[1] = *entry2;
270  }
271 
272  *dstisnull = false;
273  *dst = FunctionCall2Coll(&giststate->unionFn[attno],
274  giststate->supportCollation[attno],
275  PointerGetDatum(evec),
276  PointerGetDatum(&dstsize));
277  }
278 }

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 824 of file gistutil.c.

825 {
826  Buffer buffer;
827  bool needLock;
828 
829  /* First, try to get a page from FSM */
830  for (;;)
831  {
832  BlockNumber blkno = GetFreeIndexPage(r);
833 
834  if (blkno == InvalidBlockNumber)
835  break; /* nothing left in FSM */
836 
837  buffer = ReadBuffer(r, blkno);
838 
839  /*
840  * We have to guard against the possibility that someone else already
841  * recycled this page; the buffer may be locked if so.
842  */
843  if (ConditionalLockBuffer(buffer))
844  {
845  Page page = BufferGetPage(buffer);
846 
847  /*
848  * If the page was never initialized, it's OK to use.
849  */
850  if (PageIsNew(page))
851  return buffer;
852 
853  gistcheckpage(r, buffer);
854 
855  /*
856  * Otherwise, recycle it if deleted, and too old to have any
857  * processes interested in it.
858  */
859  if (gistPageRecyclable(page))
860  {
861  /*
862  * If we are generating WAL for Hot Standby then create a WAL
863  * record that will allow us to conflict with queries running
864  * on standby, in case they have snapshots older than the
865  * page's deleteXid.
866  */
868  gistXLogPageReuse(r, blkno, GistPageGetDeleteXid(page));
869 
870  return buffer;
871  }
872 
873  LockBuffer(buffer, GIST_UNLOCK);
874  }
875 
876  /* Can't use it, so release buffer and try again */
877  ReleaseBuffer(buffer);
878  }
879 
880  /* Must extend the file */
881  needLock = !RELATION_IS_LOCAL(r);
882 
883  if (needLock)
885 
886  buffer = ReadBuffer(r, P_NEW);
887  LockBuffer(buffer, GIST_EXCLUSIVE);
888 
889  if (needLock)
891 
892  return buffer;
893 }
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:4182
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:213
bool gistPageRecyclable(Page page)
Definition: gistutil.c:897
void gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid)
Definition: gistxlog.c:599
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:431
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:481
#define ExclusiveLock
Definition: lockdefs.h:42
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:633
#define XLogStandbyInfoActive()
Definition: xlog.h:118

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

Referenced by gistbuild(), and gistplacetopage().

◆ gistnospace()

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

Definition at line 59 of file gistutil.c.

60 {
61  unsigned int size = freespace,
62  deleted = 0;
63  int i;
64 
65  for (i = 0; i < len; i++)
66  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
67 
68  if (todelete != InvalidOffsetNumber)
69  {
70  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
71 
72  deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
73  }
74 
75  return (PageGetFreeSpace(page) + deleted < size);
76 }
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:907

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

Referenced by gistplacetopage().

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 921 of file gistutil.c.

922 {
923  static const relopt_parse_elt tab[] = {
924  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
925  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
926  };
927 
928  return (bytea *) build_reloptions(reloptions, validate,
930  sizeof(GiSTOptions),
931  tab, lengthof(tab));
932 }
#define lengthof(array)
Definition: c.h:734
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:1913
@ RELOPT_KIND_GIST
Definition: reloptions.h:47
@ RELOPT_TYPE_ENUM
Definition: reloptions.h:34
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:622

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

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 897 of file gistutil.c.

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

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

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

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

Definition at line 724 of file gistutil.c.

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

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

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

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

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

References Assert(), gistxlogPage::blkno, SplitedPageLayout::block, GISTPageSplitInfo::buf, SplitedPageLayout::buffer, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, data, 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, GistPageIsDeleted, 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().

◆ gistPopItupFromNodeBuffer()

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

Definition at line 410 of file gistbuildbuffers.c.

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

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().

◆ gistproperty()

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

Definition at line 942 of file gistutil.c.

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

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, res, and SearchSysCacheExists4.

Referenced by gisthandler().

◆ gistPushItupToNodeBuffer()

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

Definition at line 340 of file gistbuildbuffers.c.

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

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().

◆ gistRelocateBuildBuffersOnSplit()

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

Definition at line 537 of file gistbuildbuffers.c.

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

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, j, LEVEL_HAS_BUFFERS, lfirst, list_length(), RelocationBufferInfo::nodeBuffer, GISTBuildBuffers::nodeBuffersTab, GISTNodeBuffer::pageBlocknum, GISTNodeBuffer::pageBuffer, palloc(), pfree(), and RelocationBufferInfo::splitinfo.

Referenced by gistbufferinginserttuples().

◆ gistSplit()

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

Definition at line 1418 of file gist.c.

1423 {
1424  IndexTuple *lvectup,
1425  *rvectup;
1426  GistSplitVector v;
1427  int i;
1428  SplitedPageLayout *res = NULL;
1429 
1430  /* this should never recurse very deeply, but better safe than sorry */
1432 
1433  /* there's no point in splitting an empty page */
1434  Assert(len > 0);
1435 
1436  /*
1437  * If a single tuple doesn't fit on a page, no amount of splitting will
1438  * help.
1439  */
1440  if (len == 1)
1441  ereport(ERROR,
1442  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1443  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1444  IndexTupleSize(itup[0]), GiSTPageSize,
1446 
1447  memset(v.spl_lisnull, true,
1448  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1449  memset(v.spl_risnull, true,
1450  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1451  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1452 
1453  /* form left and right vector */
1454  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1455  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1456 
1457  for (i = 0; i < v.splitVector.spl_nleft; i++)
1458  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1459 
1460  for (i = 0; i < v.splitVector.spl_nright; i++)
1461  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1462 
1463  /* finalize splitting (may need another split) */
1464  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1465  {
1466  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1467  }
1468  else
1469  {
1470  ROTATEDIST(res);
1471  res->block.num = v.splitVector.spl_nright;
1472  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1473  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1474  }
1475 
1476  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1477  {
1478  SplitedPageLayout *resptr,
1479  *subres;
1480 
1481  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1482 
1483  /* install on list's tail */
1484  while (resptr->next)
1485  resptr = resptr->next;
1486 
1487  resptr->next = res;
1488  res = subres;
1489  }
1490  else
1491  {
1492  ROTATEDIST(res);
1493  res->block.num = v.splitVector.spl_nleft;
1494  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1495  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1496  }
1497 
1498  return res;
1499 }
#define ROTATEDIST(d)
Definition: gist.c:46
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:79
void check_stack_depth(void)
Definition: postgres.c:3500
int spl_nleft
Definition: gist.h:141
OffsetNumber * spl_right
Definition: gist.h:145
int spl_nright
Definition: gist.h:146
OffsetNumber * spl_left
Definition: gist.h:140
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245

References Assert(), check_stack_depth(), ereport, errcode(), errmsg(), ERROR, gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplitByKey(), i, if(), IndexTupleSize, len, TupleDescData::natts, SplitedPageLayout::next, GISTSTATE::nonLeafTupdesc, palloc(), RelationGetRelationName, res, 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 gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ 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.

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
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 
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 }
static void gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GistSplitVector *spl)
Definition: gistsplit.c:80
static bool gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVector *v, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gistsplit.c:415
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585

References Assert(), GEVHDRSZ, gistdentryinit(), gistSplitHalf(), gistunionsubkey(), gistUserPicksplit(), i, index_getattr, j, GISTSTATE::leafTupdesc, len, 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().

◆ gistunion()

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

Definition at line 219 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_levelstate_flush().

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 276 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessEmptyingQueue().

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 75 of file gistvacuum.c.

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 }
double num_index_tuples
Definition: genam.h:78
double num_heap_tuples
Definition: genam.h:51
bool analyze_only
Definition: genam.h:47
bool estimated_count
Definition: genam.h:49

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

Referenced by gisthandler().

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

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 */
227  if (!check_amop_signature(oprform->amopopr, op_rettype,
228  oprform->amoplefttype,
229  oprform->amoprighttype))
230  {
231  ereport(INFO,
232  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
233  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
234  opfamilyname, "gist",
235  format_operator(oprform->amopopr))));
236  result = false;
237  }
238  }
239 
240  /* Now check for inconsistent groups of operators/functions */
241  grouplist = identify_opfamily_groups(oprlist, proclist);
242  opclassgroup = NULL;
243  foreach(lc, grouplist)
244  {
245  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
246 
247  /* Remember the group exactly matching the test opclass */
248  if (thisgroup->lefttype == opcintype &&
249  thisgroup->righttype == opcintype)
250  opclassgroup = thisgroup;
251 
252  /*
253  * There is not a lot we can do to check the operator sets, since each
254  * GiST opclass is more or less a law unto itself, and some contain
255  * only operators that are binary-compatible with the opclass datatype
256  * (meaning that empty operator sets can be OK). That case also means
257  * that we shouldn't insist on nonempty function sets except for the
258  * opclass's own group.
259  */
260  }
261 
262  /* Check that the originally-named opclass is complete */
263  for (i = 1; i <= GISTNProcs; i++)
264  {
265  if (opclassgroup &&
266  (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
267  continue; /* got it */
268  if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
271  continue; /* optional methods */
272  ereport(INFO,
273  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
274  errmsg("operator class \"%s\" of access method %s is missing support function %d",
275  opclassname, "gist", i)));
276  result = false;
277  }
278 
279  ReleaseCatCacheList(proclist);
280  ReleaseCatCacheList(oprlist);
281  ReleaseSysCache(familytup);
282  ReleaseSysCache(classtup);
283 
284  return result;
285 }
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:152
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:206
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:43
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:271
bool check_amoptsproc_signature(Oid funcid)
Definition: amvalidate.c:192
#define NameStr(name)
Definition: c.h:681
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1777
#define INFO
Definition: elog.h:28
#define GISTNProcs
Definition: gist.h:41
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:794
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1314
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
char * format_operator(Oid operator_oid)
Definition: regproc.c:852
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
int n_members
Definition: catcache.h:176
HeapTupleData tuple
Definition: catcache.h:121
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1173
@ OPFAMILYOID
Definition: syscache.h:74
@ CLAOID
Definition: syscache.h:48
@ AMOPSTRATEGY
Definition: syscache.h:38
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:215

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().

◆ gistValidateBufferingOption()

void gistValidateBufferingOption ( const char *  value)

◆ gistXLogAssignLSN()

XLogRecPtr gistXLogAssignLSN ( void  )

Definition at line 581 of file gistxlog.c.

582 {
583  int dummy = 0;
584 
585  /*
586  * Records other than SWITCH_WAL must have content. We use an integer 0 to
587  * follow the restriction.
588  */
589  XLogBeginInsert();
591  XLogRegisterData((char *) &dummy, sizeof(dummy));
592  return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
593 }
#define XLOG_GIST_ASSIGN_LSN
Definition: gistxlog.h:27
#define XLOG_MARK_UNIMPORTANT
Definition: xlog.h:150
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:443
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:425
void XLogBeginInsert(void)
Definition: xloginsert.c:150
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:351

References XLOG_GIST_ASSIGN_LSN, XLOG_MARK_UNIMPORTANT, XLogBeginInsert(), XLogInsert(), XLogRegisterData(), and XLogSetRecordFlags().

Referenced by gistGetFakeLSN().

◆ gistXLogDelete()

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

Definition at line 673 of file gistxlog.c.

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

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

Referenced by gistprunepage().

◆ gistXLogPageDelete()

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

Definition at line 557 of file gistxlog.c.

559 {
560  gistxlogPageDelete xlrec;
561  XLogRecPtr recptr;
562 
563  xlrec.deleteXid = xid;
564  xlrec.downlinkOffset = downlinkOffset;
565 
566  XLogBeginInsert();
567  XLogRegisterData((char *) &xlrec, SizeOfGistxlogPageDelete);
568 
570  XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
571 
572  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
573 
574  return recptr;
575 }
#define SizeOfGistxlogPageDelete
Definition: gistxlog.h:90
#define XLOG_GIST_PAGE_DELETE
Definition: gistxlog.h:26
FullTransactionId deleteXid
Definition: gistxlog.h:85
OffsetNumber downlinkOffset
Definition: gistxlog.h:86

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

Referenced by gistdeletepage().

◆ gistXLogPageReuse()

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

Definition at line 599 of file gistxlog.c.

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

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

Referenced by gistNewBuffer().

◆ gistXLogSplit()

XLogRecPtr gistXLogSplit ( bool  page_is_leaf,
SplitedPageLayout dist,
BlockNumber  origrlink,
GistNSN  oldnsn,
Buffer  leftchild,
bool  markfollowright 
)

Definition at line 500 of file gistxlog.c.

504 {
505  gistxlogPageSplit xlrec;
506  SplitedPageLayout *ptr;
507  int npage = 0;
508  XLogRecPtr recptr;
509  int i;
510 
511  for (ptr = dist; ptr; ptr = ptr->next)
512  npage++;
513 
514  xlrec.origrlink = origrlink;
515  xlrec.orignsn = orignsn;
516  xlrec.origleaf = page_is_leaf;
517  xlrec.npage = (uint16) npage;
518  xlrec.markfollowright = markfollowright;
519 
520  XLogBeginInsert();
521 
522  /*
523  * Include a full page image of the child buf. (only necessary if a
524  * checkpoint happened since the child page was split)
525  */
526  if (BufferIsValid(leftchildbuf))
527  XLogRegisterBuffer(0, leftchildbuf, REGBUF_STANDARD);
528 
529  /*
530  * NOTE: We register a lot of data. The caller must've called
531  * XLogEnsureRecordSpace() to prepare for that. We cannot do it here,
532  * because we're already in a critical section. If you change the number
533  * of buffer or data registrations here, make sure you modify the
534  * XLogEnsureRecordSpace() calls accordingly!
535  */
536  XLogRegisterData((char *) &xlrec, sizeof(gistxlogPageSplit));
537 
538  i = 1;
539  for (ptr = dist; ptr; ptr = ptr->next)
540  {
542  XLogRegisterBufData(i, (char *) &(ptr->block.num), sizeof(int));
543  XLogRegisterBufData(i, (char *) ptr->list, ptr->lenlist);
544  i++;
545  }
546 
547  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT);
548 
549  return recptr;
550 }
unsigned short uint16
Definition: c.h:440
#define XLOG_GIST_PAGE_SPLIT
Definition: gistxlog.h:23
GistNSN orignsn
Definition: gistxlog.h:68
BlockNumber origrlink
Definition: gistxlog.h:67
bool markfollowright
Definition: gistxlog.h:72
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:389
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33

References SplitedPageLayout::block, SplitedPageLayout::buffer, BufferIsValid, i, SplitedPageLayout::lenlist, SplitedPageLayout::list, gistxlogPageSplit::markfollowright, SplitedPageLayout::next, gistxlogPageSplit::npage, gistxlogPage::num, gistxlogPageSplit::origleaf, gistxlogPageSplit::orignsn, gistxlogPageSplit::origrlink, REGBUF_STANDARD, REGBUF_WILL_INIT, XLOG_GIST_PAGE_SPLIT, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistplacetopage().

◆ gistXLogUpdate()

XLogRecPtr gistXLogUpdate ( Buffer  buffer,
OffsetNumber todelete,
int  ntodelete,
IndexTuple itup,
int  ntup,
Buffer  leftchild 
)

Definition at line 632 of file gistxlog.c.

636 {
637  gistxlogPageUpdate xlrec;
638  int i;
639  XLogRecPtr recptr;
640 
641  xlrec.ntodelete = ntodelete;
642  xlrec.ntoinsert = ituplen;
643 
644  XLogBeginInsert();
645  XLogRegisterData((char *) &xlrec, sizeof(gistxlogPageUpdate));
646 
648  XLogRegisterBufData(0, (char *) todelete, sizeof(OffsetNumber) * ntodelete);
649 
650  /* new tuples */
651  for (i = 0; i < ituplen; i++)
652  XLogRegisterBufData(0, (char *) (itup[i]), IndexTupleSize(itup[i]));
653 
654  /*
655  * Include a full page image of the child buf. (only necessary if a
656  * checkpoint happened since the child page was split)
657  */
658  if (BufferIsValid(leftchildbuf))
659  XLogRegisterBuffer(1, leftchildbuf, REGBUF_STANDARD);
660 
661  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE);
662 
663  return recptr;
664 }
#define XLOG_GIST_PAGE_UPDATE
Definition: gistxlog.h:20
uint16 ntodelete
Definition: gistxlog.h:37
uint16 ntoinsert
Definition: gistxlog.h:38

References BufferIsValid, i, IndexTupleSize, gistxlogPageUpdate::ntodelete, gistxlogPageUpdate::ntoinsert, REGBUF_STANDARD, XLOG_GIST_PAGE_UPDATE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gistplacetopage(), and gistvacuumpage().

◆ initGISTstate()

GISTSTATE* initGISTstate ( Relation  index)

Definition at line 1505 of file gist.c.

1506 {
1507  GISTSTATE *giststate;
1508  MemoryContext scanCxt;
1509  MemoryContext oldCxt;
1510  int i;
1511 
1512  /* safety check to protect fixed-size arrays in GISTSTATE */
1513  if (index->rd_att->natts > INDEX_MAX_KEYS)
1514  elog(ERROR, "numberOfAttributes %d > %d",
1515  index->rd_att->natts, INDEX_MAX_KEYS);
1516 
1517  /* Create the memory context that will hold the GISTSTATE */
1519  "GiST scan context",
1521  oldCxt = MemoryContextSwitchTo(scanCxt);
1522 
1523  /* Create and fill in the GISTSTATE */
1524  giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1525 
1526  giststate->scanCxt = scanCxt;
1527  giststate->tempCxt = scanCxt; /* caller must change this if needed */
1528  giststate->leafTupdesc = index->rd_att;
1529 
1530  /*
1531  * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1532  * the INCLUDE attributes.
1533  *
1534  * It is used to form tuples during tuple adjustment and page split.
1535  * B-tree creates shortened tuple descriptor for every truncated tuple,
1536  * because it is doing this less often: it does not have to form truncated
1537  * tuples during page split. Also, B-tree is not adjusting tuples on
1538  * internal pages the way GiST does.
1539  */
1540  giststate->nonLeafTupdesc = CreateTupleDescCopyConstr(index->rd_att);
1541  giststate->nonLeafTupdesc->natts =
1543 
1545  {
1546  fmgr_info_copy(&(giststate->consistentFn[i]),
1548  scanCxt);
1549  fmgr_info_copy(&(giststate->unionFn[i]),
1551  scanCxt);
1552 
1553  /* opclasses are not required to provide a Compress method */
1555  fmgr_info_copy(&(giststate->compressFn[i]),
1557  scanCxt);
1558  else
1559  giststate->compressFn[i].fn_oid = InvalidOid;
1560 
1561  /* opclasses are not required to provide a Decompress method */
1563  fmgr_info_copy(&(giststate->decompressFn[i]),
1565  scanCxt);
1566  else
1567  giststate->decompressFn[i].fn_oid = InvalidOid;
1568 
1569  fmgr_info_copy(&(giststate->penaltyFn[i]),
1571  scanCxt);
1572  fmgr_info_copy(&(giststate->picksplitFn[i]),
1574  scanCxt);
1575  fmgr_info_copy(&(giststate->equalFn[i]),
1577  scanCxt);
1578 
1579  /* opclasses are not required to provide a Distance method */
1581  fmgr_info_copy(&(giststate->distanceFn[i]),
1583  scanCxt);
1584  else
1585  giststate->distanceFn[i].fn_oid = InvalidOid;
1586 
1587  /* opclasses are not required to provide a Fetch method */
1589  fmgr_info_copy(&(giststate->fetchFn[i]),
1591  scanCxt);
1592  else
1593  giststate->fetchFn[i].fn_oid = InvalidOid;
1594 
1595  /*
1596  * If the index column has a specified collation, we should honor that
1597  * while doing comparisons. However, we may have a collatable storage
1598  * type for a noncollatable indexed data type. If there's no index
1599  * collation then specify default collation in case the support
1600  * functions need collation. This is harmless if the support
1601  * functions don't care about collation, so we just do it
1602  * unconditionally. (We could alternatively call get_typcollation,
1603  * but that seems like expensive overkill --- there aren't going to be
1604  * any cases where a GiST storage type has a nondefault collation.)
1605  */
1606  if (OidIsValid(index->rd_indcollation[i]))
1607  giststate->supportCollation[i] = index->rd_indcollation[i];
1608  else
1609  giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1610  }
1611 
1612  /* No opclass information for INCLUDE attributes */
1613  for (; i < index->rd_att->natts; i++)
1614  {
1615  giststate->consistentFn[i].fn_oid = InvalidOid;
1616  giststate->unionFn[i].fn_oid = InvalidOid;
1617  giststate->compressFn[i].fn_oid = InvalidOid;
1618  giststate->decompressFn[i].fn_oid = InvalidOid;
1619  giststate->penaltyFn[i].fn_oid = InvalidOid;
1620  giststate->picksplitFn[i].fn_oid = InvalidOid;
1621  giststate->equalFn[i].fn_oid = InvalidOid;
1622  giststate->distanceFn[i].