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  SplitPageLayout
 
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 SplitPageLayout SplitPageLayout
 
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)
 
SplitPageLayoutgistSplit (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
 
XLogRecPtr gistXLogPageDelete (Buffer buffer, FullTransactionId xid, Buffer parentBuffer, OffsetNumber downlinkOffset)
 
void gistXLogPageReuse (Relation rel, Relation heaprel, BlockNumber blkno, FullTransactionId deleteXid)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
 
XLogRecPtr gistXLogDelete (Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon, Relation heaprel)
 
XLogRecPtr gistXLogSplit (bool page_is_leaf, SplitPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, 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, Relation heaprel)
 
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, const Datum *attdata, const bool *isnull, bool isleaf)
 
void gistCompressValues (GISTSTATE *giststate, Relation r, const Datum *attdata, const 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 *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
 
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)
 
GISTBuildBuffersgistInitBuildBuffers (int pagesPerBuffer, int levelStep, int maxLevel)
 
GISTNodeBuffergistGetNodeBuffer (GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
 
void gistPushItupToNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
 
bool gistPopItupFromNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)
 
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 480 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 479 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 476 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:202

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

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

◆ SplitPageLayout

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

123 {
125  "GiST temporary context",
127 }
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1653 of file gist.c.

1654 {
1655  /* It's sufficient to delete the scanCxt */
1656  MemoryContextDelete(giststate->scanCxt);
1657 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
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 295 of file gistvalidate.c.

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

180 {
181  IndexBuildResult *result;
182  double reltuples;
183  GISTBuildState buildstate;
185  int fillfactor;
186  Oid SortSupportFnOids[INDEX_MAX_KEYS];
187  GiSTOptions *options = (GiSTOptions *) index->rd_options;
188 
189  /*
190  * We expect to be called exactly once for any index relation. If that's
191  * not the case, big trouble's what we have.
192  */
194  elog(ERROR, "index \"%s\" already contains data",
196 
197  buildstate.indexrel = index;
198  buildstate.heaprel = heap;
199  buildstate.sortstate = NULL;
200  buildstate.giststate = initGISTstate(index);
201 
202  /*
203  * Create a temporary memory context that is reset once for each tuple
204  * processed. (Note: we don't bother to make this a child of the
205  * giststate's scanCxt, so we have to delete it separately at the end.)
206  */
207  buildstate.giststate->tempCxt = createTempGistContext();
208 
209  /*
210  * Choose build strategy. First check whether the user specified to use
211  * buffering mode. (The use-case for that in the field is somewhat
212  * questionable perhaps, but it's important for testing purposes.)
213  */
214  if (options)
215  {
216  if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
217  buildstate.buildMode = GIST_BUFFERING_STATS;
218  else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
219  buildstate.buildMode = GIST_BUFFERING_DISABLED;
220  else /* must be "auto" */
221  buildstate.buildMode = GIST_BUFFERING_AUTO;
222  }
223  else
224  {
225  buildstate.buildMode = GIST_BUFFERING_AUTO;
226  }
227 
228  /*
229  * Unless buffering mode was forced, see if we can use sorting instead.
230  */
231  if (buildstate.buildMode != GIST_BUFFERING_STATS)
232  {
233  bool hasallsortsupports = true;
235 
236  for (int i = 0; i < keyscount; i++)
237  {
238  SortSupportFnOids[i] = index_getprocid(index, i + 1,
240  if (!OidIsValid(SortSupportFnOids[i]))
241  {
242  hasallsortsupports = false;
243  break;
244  }
245  }
246  if (hasallsortsupports)
247  buildstate.buildMode = GIST_SORTED_BUILD;
248  }
249 
250  /*
251  * Calculate target amount of free space to leave on pages.
252  */
254  buildstate.freespace = BLCKSZ * (100 - fillfactor) / 100;
255 
256  /*
257  * Build the index using the chosen strategy.
258  */
259  buildstate.indtuples = 0;
260  buildstate.indtuplesSize = 0;
261 
262  if (buildstate.buildMode == GIST_SORTED_BUILD)
263  {
264  /*
265  * Sort all data, build the index from bottom up.
266  */
267  buildstate.sortstate = tuplesort_begin_index_gist(heap,
268  index,
270  NULL,
272 
273  /* Scan the table, adding all tuples to the tuplesort */
274  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
276  (void *) &buildstate, NULL);
277 
278  /*
279  * Perform the sort and build index pages.
280  */
281  tuplesort_performsort(buildstate.sortstate);
282 
283  gist_indexsortbuild(&buildstate);
284 
285  tuplesort_end(buildstate.sortstate);
286  }
287  else
288  {
289  /*
290  * Initialize an empty index and insert all tuples, possibly using
291  * buffers on intermediate levels.
292  */
293  Buffer buffer;
294  Page page;
295 
296  /* initialize the root page */
297  buffer = gistNewBuffer(index, heap);
299  page = BufferGetPage(buffer);
300 
302 
303  GISTInitBuffer(buffer, F_LEAF);
304 
305  MarkBufferDirty(buffer);
306  PageSetLSN(page, GistBuildLSN);
307 
308  UnlockReleaseBuffer(buffer);
309 
311 
312  /* Scan the table, inserting all the tuples to the index. */
313  reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
315  (void *) &buildstate, NULL);
316 
317  /*
318  * If buffering was used, flush out all the tuples that are still in
319  * the buffers.
320  */
321  if (buildstate.buildMode == GIST_BUFFERING_ACTIVE)
322  {
323  elog(DEBUG1, "all tuples processed, emptying buffers");
324  gistEmptyAllBuffers(&buildstate);
325  gistFreeBuildBuffers(buildstate.gfbb);
326  }
327 
328  /*
329  * We didn't write WAL records as we built the index, so if
330  * WAL-logging is required, write all pages to the WAL now.
331  */
332  if (RelationNeedsWAL(index))
333  {
336  true);
337  }
338  }
339 
340  /* okay, all heap tuples are indexed */
341  MemoryContextSwitchTo(oldcxt);
343 
344  freeGISTstate(buildstate.giststate);
345 
346  /*
347  * Return statistics
348  */
349  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
350 
351  result->heap_tuples = reltuples;
352  result->index_tuples = (double) buildstate.indtuples;
353 
354  return result;
355 }
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3667
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4867
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2474
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:281
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:408
Pointer Page
Definition: bufpage.h:78
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
#define Assert(condition)
Definition: c.h:858
#define OidIsValid(objectId)
Definition: c.h:775
#define DEBUG1
Definition: elog.h:30
#define elog(elevel,...)
Definition: elog.h:224
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1525
MemoryContext createTempGistContext(void)
Definition: gist.c:122
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1653
#define F_LEAF
Definition: gist.h:48
#define GistBuildLSN
Definition: gist.h:69
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:480
@ 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:400
static void gistBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:820
static void gistSortedBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:366
static void gistEmptyAllBuffers(GISTBuildState *buildstate)
Definition: gistbuild.c:1370
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
Buffer gistNewBuffer(Relation r, Relation heaprel)
Definition: gistutil.c:824
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
int maintenance_work_mem
Definition: globals.c:130
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:826
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
void * palloc(Size size)
Definition: mcxt.c:1316
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
#define INDEX_MAX_KEYS
int fillfactor
Definition: pgbench.c:187
unsigned int Oid
Definition: postgres_ext.h:31
MemoryContextSwitchTo(old_ctx)
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define RelationNeedsWAL(relation)
Definition: rel.h:628
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
@ MAIN_FORKNUM
Definition: relpath.h:50
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:95
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:1785
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:1385
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:971
#define TUPLESORT_NONE
Definition: tuplesort.h:93
Tuplesortstate * tuplesort_begin_index_gist(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
void log_newpage_range(Relation rel, ForkNumber forknum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1270

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

134 {
135  Buffer buffer;
136 
137  /* Initialize the root page */
138  buffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
140 
141  /* Initialize and xlog buffer */
143  GISTInitBuffer(buffer, F_LEAF);
144  MarkBufferDirty(buffer);
145  log_newpage_buffer(buffer, true);
147 
148  /* Unlock and release the buffer */
149  UnlockReleaseBuffer(buffer);
150 }
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:845
@ EB_SKIP_EXTENSION_LOCK
Definition: bufmgr.h:74
@ EB_LOCK_FIRST
Definition: bufmgr.h:86
#define BMR_REL(p_rel)
Definition: bufmgr.h:107
@ INIT_FORKNUM
Definition: relpath.h:53
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1237

References BMR_REL, EB_LOCK_FIRST, EB_SKIP_EXTENSION_LOCK, END_CRIT_SECTION, ExtendBufferedRel(), F_LEAF, GISTInitBuffer(), INIT_FORKNUM, log_newpage_buffer(), MarkBufferDirty(), 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:1346
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46

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

Referenced by gisthandler().

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 793 of file gistget.c.

794 {
798  return true;
799  else
800  return false;
801 }

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 }
static bool PageIsNew(Page page)
Definition: bufpage.h:230
static uint16 PageGetSpecialSize(Page page)
Definition: bufpage.h:313
#define MAXALIGN(LEN)
Definition: c.h:811
int errhint(const char *fmt,...)
Definition: elog.c:1319
static char * buf
Definition: pg_test_fsync.c:73

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 }
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
#define GistPageIsLeaf(page)
Definition: gist.h:169
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
IndexTupleData * IndexTuple
Definition: itup.h:53
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: itup.h:117
#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:34
bool pg_prng_bool(pg_prng_state *state)
Definition: pg_prng.c:313
uintptr_t Datum
Definition: postgres.h:64
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,
const Datum attdata,
const 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:1129
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:244
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
Oid fn_oid
Definition: fmgr.h:59
Datum key
Definition: gist.h:160
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:112

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:163
Page page
Definition: gist.h:162
Relation rel
Definition: gist.h:161
bool leafkey
Definition: gist.h:164
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:4850
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:3928
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5085
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:745
static XLogRecPtr PageGetLSN(Page page)
Definition: bufpage.h:383
int errdetail(const char *fmt,...)
Definition: elog.c:1205
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1188
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1248
#define GistFollowRight(page)
Definition: gist.h:182
#define GistPageIsDeleted(page)
Definition: gist.h:172
#define GistPageGetNSN(page)
Definition: gist.h:186
#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
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
#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:323
#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, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
#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:46

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 }
static bool PageIsEmpty(Page page)
Definition: bufpage.h:220
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:468
size_t Size
Definition: c.h:605
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:476
struct ItemIdData ItemIdData
static pg_noinline void Size size
Definition: slab.c:607

References GiSTPageSize, i, IndexTupleSize, len, and size.

Referenced by gistSplit().

◆ gistFormTuple()

IndexTuple gistFormTuple ( GISTSTATE giststate,
Relation  r,
const Datum attdata,
const 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, const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
Definition: gistutil.c:596
IndexTuple index_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: indextuple.c:44
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition: itemptr.h:158
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 507 of file gistbuildbuffers.c.

508 {
509  /* Close buffers file. */
510  BufFileClose(gfbb->pfile);
511 
512  /* All other things will be freed on memory context release */
513 }
void BufFileClose(BufFile *file)
Definition: buffile.c:412

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 }
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
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
Definition: gistutil.c:575

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 743 of file gistget.c.

744 {
745  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
746  int64 ntids = 0;
747  GISTSearchItem fakeItem;
748 
749  if (!so->qual_ok)
750  return 0;
751 
753 
754  /* Begin the scan by processing the root page */
755  so->curPageData = so->nPageData = 0;
756  scan->xs_hitup = NULL;
757  if (so->pageDataCxt)
759 
760  fakeItem.blkno = GIST_ROOT_BLKNO;
761  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
762  gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
763 
764  /*
765  * While scanning a leaf page, ItemPointers of matching heap tuples will
766  * be stored directly into tbm, so we don't need to deal with them here.
767  */
768  for (;;)
769  {
771 
772  if (!item)
773  break;
774 
776 
777  gistScanPage(scan, item, item->distances, tbm, &ntids);
778 
779  pfree(item);
780  }
781 
782  return ntids;
783 }
XLogRecPtr GistNSN
Definition: gist.h:62
GISTScanOpaqueData * GISTScanOpaque
Definition: gist_private.h:181
static GISTSearchItem * getNextGISTSearchItem(GISTScanOpaque so)
Definition: gistget.c:538
static void gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
Definition: gistget.c:328
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
void pfree(void *pointer)
Definition: mcxt.c:1520
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:625
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 1016 of file gistutil.c.

1017 {
1018  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
1019  {
1020  /*
1021  * Temporary relations are only accessible in our session, so a simple
1022  * backend-local counter will do.
1023  */
1024  static XLogRecPtr counter = FirstNormalUnloggedLSN;
1025 
1026  return counter++;
1027  }
1028  else if (RelationIsPermanent(rel))
1029  {
1030  /*
1031  * WAL-logging on this relation will start after commit, so its LSNs
1032  * must be distinct numbers smaller than the LSN at the next commit.
1033  * Emit a dummy WAL record if insert-LSN hasn't advanced after the
1034  * last call.
1035  */
1036  static XLogRecPtr lastlsn = InvalidXLogRecPtr;
1037  XLogRecPtr currlsn = GetXLogInsertRecPtr();
1038 
1039  /* Shouldn't be called for WAL-logging relations */
1040  Assert(!RelationNeedsWAL(rel));
1041 
1042  /* No need for an actual record if we already have a distinct LSN */
1043  if (!XLogRecPtrIsInvalid(lastlsn) && lastlsn == currlsn)
1044  currlsn = gistXLogAssignLSN();
1045 
1046  lastlsn = currlsn;
1047  return currlsn;
1048  }
1049  else
1050  {
1051  /*
1052  * Unlogged relations are accessible from other backends, and survive
1053  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
1054  */
1055  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
1056  return GetFakeLSNForUnloggedRel();
1057  }
1058 }
XLogRecPtr gistXLogAssignLSN(void)
Definition: gistxlog.c:576
#define RelationIsPermanent(relation)
Definition: rel.h:617
Form_pg_class rd_rel
Definition: rel.h:111
XLogRecPtr GetXLogInsertRecPtr(void)
Definition: xlog.c:9355
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4571
#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  nodeBlocknum,
int  level 
)

Definition at line 113 of file gistbuildbuffers.c.

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

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 612 of file gistget.c.

613 {
614  GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
615 
616  if (dir != ForwardScanDirection)
617  elog(ERROR, "GiST only supports forward scan direction");
618 
619  if (!so->qual_ok)
620  return false;
621 
622  if (so->firstCall)
623  {
624  /* Begin the scan by processing the root page */
625  GISTSearchItem fakeItem;
626 
628 
629  so->firstCall = false;
630  so->curPageData = so->nPageData = 0;
631  scan->xs_hitup = NULL;
632  if (so->pageDataCxt)
634 
635  fakeItem.blkno = GIST_ROOT_BLKNO;
636  memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
637  gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
638  }
639 
640  if (scan->numberOfOrderBys > 0)
641  {
642  /* Must fetch tuples in strict distance order */
643  return getNextNearest(scan);
644  }
645  else
646  {
647  /* Fetch tuples index-page-at-a-time */
648  for (;;)
649  {
650  if (so->curPageData < so->nPageData)
651  {
652  if (scan->kill_prior_tuple && so->curPageData > 0)
653  {
654 
655  if (so->killedItems == NULL)
656  {
657  MemoryContext oldCxt =
659 
660  so->killedItems =
662  * sizeof(OffsetNumber));
663 
664  MemoryContextSwitchTo(oldCxt);
665  }
667  so->killedItems[so->numKilled++] =
668  so->pageData[so->curPageData - 1].offnum;
669  }
670  /* continuing to return tuples from a leaf page */
671  scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;
672  scan->xs_recheck = so->pageData[so->curPageData].recheck;
673 
674  /* in an index-only scan, also return the reconstructed tuple */
675  if (scan->xs_want_itup)
676  scan->xs_hitup = so->pageData[so->curPageData].recontup;
677 
678  so->curPageData++;
679 
680  return true;
681  }
682 
683  /*
684  * Check the last returned tuple and add it to killedItems if
685  * necessary
686  */
687  if (scan->kill_prior_tuple
688  && so->curPageData > 0
689  && so->curPageData == so->nPageData)
690  {
691 
692  if (so->killedItems == NULL)
693  {
694  MemoryContext oldCxt =
696 
697  so->killedItems =
699  * sizeof(OffsetNumber));
700 
701  MemoryContextSwitchTo(oldCxt);
702  }
704  so->killedItems[so->numKilled++] =
705  so->pageData[so->curPageData - 1].offnum;
706  }
707  /* find and process the next index page */
708  do
709  {
710  GISTSearchItem *item;
711 
712  if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
713  gistkillitems(scan);
714 
715  item = getNextGISTSearchItem(so);
716 
717  if (!item)
718  return false;
719 
721 
722  /* save current item BlockNumber for next gistkillitems() call */
723  so->curBlkno = item->blkno;
724 
725  /*
726  * While scanning a leaf page, ItemPointers of matching heap
727  * tuples are stored in so->pageData. If there are any on
728  * this page, we fall out of the inner "do" and loop around to
729  * return them.
730  */
731  gistScanPage(scan, item, item->distances, NULL, NULL);
732 
733  pfree(item);
734  } while (so->nPageData == 0);
735  }
736  }
737 }
static bool getNextNearest(IndexScanDesc scan)
Definition: gistget.c:560
static void gistkillitems(IndexScanDesc scan)
Definition: gistget.c:38
#define MaxIndexTuplesPerPage
Definition: itup.h:165
@ ForwardScanDirection
Definition: sdir.h:28
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
union GISTSearchItem::@46 data
BlockNumber blkno
Definition: gist_private.h:133
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 44 of file gistbuildbuffers.c.

45 {
46  GISTBuildBuffers *gfbb;
47  HASHCTL hashCtl;
48 
49  gfbb = palloc(sizeof(GISTBuildBuffers));
50  gfbb->pagesPerBuffer = pagesPerBuffer;
51  gfbb->levelStep = levelStep;
52 
53  /*
54  * Create a temporary file to hold buffer pages that are swapped out of
55  * memory.
56  */
57  gfbb->pfile = BufFileCreateTemp(false);
58  gfbb->nFileBlocks = 0;
59 
60  /* Initialize free page management. */
61  gfbb->nFreeBlocks = 0;
62  gfbb->freeBlocksLen = 32;
63  gfbb->freeBlocks = (long *) palloc(gfbb->freeBlocksLen * sizeof(long));
64 
65  /*
66  * Current memory context will be used for all in-memory data structures
67  * of buffers which are persistent during buffering build.
68  */
70 
71  /*
72  * nodeBuffersTab hash is association between index blocks and it's
73  * buffers.
74  */
75  hashCtl.keysize = sizeof(BlockNumber);
76  hashCtl.entrysize = sizeof(GISTNodeBuffer);
77  hashCtl.hcxt = CurrentMemoryContext;
78  gfbb->nodeBuffersTab = hash_create("gistbuildbuffers",
79  1024,
80  &hashCtl,
82 
83  gfbb->bufferEmptyingQueue = NIL;
84 
85  /*
86  * Per-level node buffers lists for final buffers emptying process. Node
87  * buffers are inserted here when they are created.
88  */
89  gfbb->buffersOnLevelsLen = 1;
90  gfbb->buffersOnLevels = (List **) palloc(sizeof(List *) *
91  gfbb->buffersOnLevelsLen);
92  gfbb->buffersOnLevels[0] = NIL;
93 
94  /*
95  * Block numbers of node buffers which last pages are currently loaded
96  * into main memory.
97  */
98  gfbb->loadedBuffersLen = 32;
100  sizeof(GISTNodeBuffer *));
101  gfbb->loadedBuffersCount = 0;
102 
103  gfbb->rootlevel = maxLevel;
104 
105  return gfbb;
106 }
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:193
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#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:111
#define GistPageGetOpaque(page)
Definition: gist.h:167
uint16 gist_page_id
Definition: gist.h:82
BlockNumber rightlink
Definition: gist.h:80
uint16 flags
Definition: gist.h:81

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

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

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(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 = false; /* silence compiler warning */
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:1171
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:1149
struct GISTENTRY GISTENTRY
#define GEVHDRSZ
Definition: gist.h:239
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:236
int32 n
Definition: gist.h:235

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 }
#define storage
Definition: indent_codes.h:68

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r,
Relation  heaprel 
)

Definition at line 824 of file gistutil.c.

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

References BMR_REL, BufferGetPage(), ConditionalLockBuffer(), EB_LOCK_FIRST, ExtendBufferedRel(), GetFreeIndexPage(), GIST_UNLOCK, gistcheckpage(), GistPageGetDeleteXid(), gistPageRecyclable(), gistXLogPageReuse(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, PageIsNew(), ReadBuffer(), RelationNeedsWAL, ReleaseBuffer(), 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(), PageGetItemId(), and size.

Referenced by gistplacetopage().

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 912 of file gistutil.c.

913 {
914  static const relopt_parse_elt tab[] = {
915  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
916  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
917  };
918 
919  return (bytea *) build_reloptions(reloptions, validate,
921  sizeof(GiSTOptions),
922  tab, lengthof(tab));
923 }
#define lengthof(array)
Definition: c.h:788
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:1908
@ RELOPT_KIND_GIST
Definition: reloptions.h:47
@ RELOPT_TYPE_ENUM
Definition: reloptions.h:34
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:687

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

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 888 of file gistutil.c.

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

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

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

float gistpenalty ( GISTSTATE giststate,
int  attno,
GISTENTRY orig,
bool  isNullOrig,
GISTENTRY add,
bool  isNullAdd 
)

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:74
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 225 of file gist.c.

234 {
235  BlockNumber blkno = BufferGetBlockNumber(buffer);
236  Page page = BufferGetPage(buffer);
237  bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
238  XLogRecPtr recptr;
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  SplitPageLayout *dist = NULL,
286  *ptr;
287  BlockNumber oldrlink = InvalidBlockNumber;
288  GistNSN oldnsn = 0;
289  SplitPageLayout 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, heapRel);
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 (int 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 }
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:359
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:1664
SplitPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1438
#define GistMarkFollowRight(page)
Definition: gist.h:183
#define GistClearFollowRight(page)
Definition: gist.h:184
#define GistPageSetNSN(page, val)
Definition: gist.h:187
#define GistPageHasGarbage(page)
Definition: gist.h:178
#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:1016
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 gistXLogSplit(bool page_is_leaf, SplitPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:495
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:629
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static void ItemPointerSetBlockNumber(ItemPointerData *pointer, BlockNumber blockNumber)
Definition: itemptr.h:147
List * lappend(List *list, void *datum)
Definition: list.c:339
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
const void * data
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3129
IndexTuple downlink
Definition: gist_private.h:422
gistxlogPage block
Definition: gist_private.h:193
struct SplitPageLayout * next
Definition: gist_private.h:200
IndexTuple itup
Definition: gist_private.h:196
IndexTupleData * list
Definition: gist_private.h:194
BlockNumber blkno
Definition: gist_private.h:186
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:175

References Assert, gistxlogPage::blkno, SplitPageLayout::block, GISTPageSplitInfo::buf, SplitPageLayout::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(), SplitPageLayout::itup, lappend(), SplitPageLayout::lenlist, SplitPageLayout::list, MarkBufferDirty(), SplitPageLayout::next, NIL, gistxlogPage::num, OffsetNumberIsValid, SplitPageLayout::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 itup 
)

Definition at line 406 of file gistbuildbuffers.c.

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

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

References 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  itup 
)

Definition at line 336 of file gistbuildbuffers.c.

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

References GISTNodeBuffer::blocksCount, BUFFER_HALF_FILLED, GISTBuildBuffers::bufferEmptyingQueue, GISTBuildBuffers::context, gistAddLoadedBuffer(), gistAllocateNewPageBuffer(), gistBuffersGetFreeBlock(), gistLoadNodeBuffer(), gistPlaceItupToPage(), lcons(), MAXALIGN, MemoryContextSwitchTo(), 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 533 of file gistbuildbuffers.c.

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

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

Definition at line 1438 of file gist.c.

1443 {
1444  IndexTuple *lvectup,
1445  *rvectup;
1446  GistSplitVector v;
1447  int i;
1448  SplitPageLayout *res = NULL;
1449 
1450  /* this should never recurse very deeply, but better safe than sorry */
1452 
1453  /* there's no point in splitting an empty page */
1454  Assert(len > 0);
1455 
1456  /*
1457  * If a single tuple doesn't fit on a page, no amount of splitting will
1458  * help.
1459  */
1460  if (len == 1)
1461  ereport(ERROR,
1462  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1463  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1464  IndexTupleSize(itup[0]), GiSTPageSize,
1466 
1467  memset(v.spl_lisnull, true,
1468  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1469  memset(v.spl_risnull, true,
1470  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1471  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1472 
1473  /* form left and right vector */
1474  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1475  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1476 
1477  for (i = 0; i < v.splitVector.spl_nleft; i++)
1478  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1479 
1480  for (i = 0; i < v.splitVector.spl_nright; i++)
1481  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1482 
1483  /* finalize splitting (may need another split) */
1484  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1485  {
1486  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1487  }
1488  else
1489  {
1490  ROTATEDIST(res);
1491  res->block.num = v.splitVector.spl_nright;
1492  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1493  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1494  }
1495 
1496  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1497  {
1498  SplitPageLayout *resptr,
1499  *subres;
1500 
1501  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1502 
1503  /* install on list's tail */
1504  while (resptr->next)
1505  resptr = resptr->next;
1506 
1507  resptr->next = res;
1508  res = subres;
1509  }
1510  else
1511  {
1512  ROTATEDIST(res);
1513  res->block.num = v.splitVector.spl_nleft;
1514  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1515  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1516  }
1517 
1518  return res;
1519 }
#define ROTATEDIST(d)
Definition: gist.c:45
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
for(;;)
void check_stack_depth(void)
Definition: postgres.c:3531
int spl_nleft
Definition: gist.h:143
OffsetNumber * spl_right
Definition: gist.h:147
int spl_nright
Definition: gist.h:148
OffsetNumber * spl_left
Definition: gist.h:142
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, for(), gistfillitupvec(), gistfitpage(), gistFormTuple(), GiSTPageSize, gistSplitByKey(), i, if(), IndexTupleSize, len, TupleDescData::natts, SplitPageLayout::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, for(), 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 272 of file gistbuildbuffers.c.

273 {
274  int i;
275 
276  /* Unload all the buffers that have a page loaded in memory. */
277  for (i = 0; i < gfbb->loadedBuffersCount; i++)
278  gistUnloadNodeBuffer(gfbb, gfbb->loadedBuffers[i]);
279 
280  /* Now there are no node buffers with loaded last page */
281  gfbb->loadedBuffersCount = 0;
282 }
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:79
double num_heap_tuples
Definition: genam.h:52
bool analyze_only
Definition: genam.h:48
bool estimated_count
Definition: genam.h:50

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 33 of file gistvalidate.c.

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

References check_amop_signature(), check_amoptsproc_signature(), check_amproc_signature(), 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_STRATNUM_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(), ReleaseCatCacheList(), ReleaseSysCache(), OpFamilyOpFuncGroup::righttype, SearchSysCache1(), SearchSysCacheList1, and catctup::tuple.

Referenced by gisthandler().

◆ gistXLogAssignLSN()

XLogRecPtr gistXLogAssignLSN ( void  )

Definition at line 576 of file gistxlog.c.

577 {
578  int dummy = 0;
579 
580  /*
581  * Records other than XLOG_SWITCH must have content. We use an integer 0
582  * to follow the restriction.
583  */
584  XLogBeginInsert();
586  XLogRegisterData((char *) &dummy, sizeof(dummy));
587  return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
588 }
#define XLOG_GIST_ASSIGN_LSN
Definition: gistxlog.h:27
#define XLOG_MARK_UNIMPORTANT
Definition: xlog.h:153
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:364
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149

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  snapshotConflictHorizon,
Relation  heaprel 
)

Definition at line 670 of file gistxlog.c.

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

References gistxlogDelete::isCatalogRel, gistxlogDelete::ntodelete, REGBUF_STANDARD, RelationIsAccessibleInLogicalDecoding, SizeOfGistxlogDelete, gistxlogDelete::snapshotConflictHorizon, 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 552 of file gistxlog.c.

554 {
555  gistxlogPageDelete xlrec;
556  XLogRecPtr recptr;
557 
558  xlrec.deleteXid = xid;
559  xlrec.downlinkOffset = downlinkOffset;
560 
561  XLogBeginInsert();
562  XLogRegisterData((char *) &xlrec, SizeOfGistxlogPageDelete);
563 
565  XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
566 
567  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
568 
569  return recptr;
570 }
#define SizeOfGistxlogPageDelete
Definition: gistxlog.h:91
#define XLOG_GIST_PAGE_DELETE
Definition: gistxlog.h:26
FullTransactionId deleteXid
Definition: gistxlog.h:86
OffsetNumber downlinkOffset
Definition: gistxlog.h:87

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,
Relation  heaprel,
BlockNumber  blkno,
FullTransactionId  deleteXid 
)

Definition at line 594 of file gistxlog.c.

596 {
597  gistxlogPageReuse xlrec_reuse;
598 
599  /*
600  * Note that we don't register the buffer with the record, because this
601  * operation doesn't modify the page. This record only exists to provide a
602  * conflict point for Hot Standby.
603  */
604 
605  /* XLOG stuff */
607  xlrec_reuse.locator = rel->rd_locator;
608  xlrec_reuse.block = blkno;
609  xlrec_reuse.snapshotConflictHorizon = deleteXid;
610 
611  XLogBeginInsert();
612  XLogRegisterData((char *) &xlrec_reuse, SizeOfGistxlogPageReuse);
613 
614  XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_REUSE);
615 }
#define XLOG_GIST_PAGE_REUSE
Definition: gistxlog.h:22
#define SizeOfGistxlogPageReuse
Definition: gistxlog.h:106
RelFileLocator rd_locator
Definition: rel.h:57
RelFileLocator locator
Definition: gistxlog.h:99
BlockNumber block
Definition: gistxlog.h:100
FullTransactionId snapshotConflictHorizon
Definition: gistxlog.h:101

References gistxlogPageReuse::block, gistxlogPageReuse::isCatalogRel, gistxlogPageReuse::locator, RelationData::rd_locator, RelationIsAccessibleInLogicalDecoding, SizeOfGistxlogPageReuse, gistxlogPageReuse::snapshotConflictHorizon, XLOG_GIST_PAGE_REUSE, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by gistNewBuffer().

◆ gistXLogSplit()

XLogRecPtr gistXLogSplit ( bool  page_is_leaf,
SplitPageLayout dist,
BlockNumber  origrlink,
GistNSN  orignsn,
Buffer  leftchildbuf,
bool  markfollowright 
)

Definition at line 495 of file gistxlog.c.

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

References SplitPageLayout::block, SplitPageLayout::buffer, BufferIsValid(), i, SplitPageLayout::lenlist, SplitPageLayout::list, gistxlogPageSplit::markfollowright, SplitPageLayout::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  ituplen,
Buffer  leftchildbuf 
)

Definition at line 629 of file gistxlog.c.

633 {
634  gistxlogPageUpdate xlrec;
635  int i;
636  XLogRecPtr recptr;
637 
638  xlrec.ntodelete = ntodelete;
639  xlrec.ntoinsert = ituplen;
640 
641  XLogBeginInsert();
642  XLogRegisterData((char *) &xlrec, sizeof(gistxlogPageUpdate));
643 
645  XLogRegisterBufData(0, (char *) todelete, sizeof(OffsetNumber) * ntodelete);
646 
647  /* new tuples */
648  for (i = 0; i < ituplen; i++)
649  XLogRegisterBufData(0, (char *) (itup[i]), IndexTupleSize(itup[i]));
650 
651  /*
652  * Include a full page image of the child buf. (only necessary if a
653  * checkpoint happened since the child page was split)
654  */
655  if (BufferIsValid(leftchildbuf))
656  XLogRegisterBuffer(1, leftchildbuf, REGBUF_STANDARD);
657 
658  recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE);
659 
660  return recptr;
661 }
#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 1525 of file gist.c.

1526 {
1527  GISTSTATE *giststate;
1528  MemoryContext scanCxt;
1529  MemoryContext oldCxt;
1530  int i;
1531 
1532  /* safety check to protect fixed-size arrays in GISTSTATE */
1533  if (index->rd_att->natts > INDEX_MAX_KEYS)
1534  elog(ERROR, "numberOfAttributes %d > %d",
1535  index->rd_att->natts, INDEX_MAX_KEYS);
1536 
1537  /* Create the memory context that will hold the GISTSTATE */
1539  "GiST scan context",
1541  oldCxt = MemoryContextSwitchTo(scanCxt);
1542 
1543  /* Create and fill in the GISTSTATE */
1544  giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1545 
1546  giststate->scanCxt = scanCxt;
1547  giststate->tempCxt = scanCxt; /* caller must change this if needed */
1548  giststate->leafTupdesc = index->rd_att;
1549 
1550  /*
1551  * The truncated tupdesc for non-leaf index tuples, which doesn't contain
1552  * the INCLUDE attributes.
1553  *
1554  * It is used to form tuples during tuple adjustment and page split.
1555  * B-tree creates shortened tuple descriptor for every truncated tuple,
1556  * because it is doing this less often: it does not have to form truncated
1557  * tuples during page split. Also, B-tree is not adjusting tuples on
1558  * internal pages the way GiST does.
1559  */
1560  giststate->nonLeafTupdesc = CreateTupleDescCopyConstr(index->rd_att);
1561  giststate->nonLeafTupdesc->natts =
1563 
1565  {
1566  fmgr_info_copy(&(giststate->consistentFn[i]),
1568  scanCxt);
1569  fmgr_info_copy(&(giststate->unionFn[i]),
1571  scanCxt);
1572 
1573  /* opclasses are not required to provide a Compress method */
1575  fmgr_info_copy(&(giststate->compressFn[i]),
1577  scanCxt);
1578  else
1579  giststate->compressFn[i].fn_oid = InvalidOid;
1580 
1581  /* opclasses are not required to provide a Decompress method */
1583  fmgr_info_copy(&(giststate->decompressFn[i]),
1585  scanCxt);
1586  else
1587  giststate->decompressFn[i].fn_oid = InvalidOid;
1588 
1589  fmgr_info_copy(&(giststate->penaltyFn[i]),
1591  scanCxt);
1592  fmgr_info_copy(&(giststate->picksplitFn[i]),
1594  scanCxt);
1595  fmgr_info_copy(&(giststate->equalFn[i]),
1597  scanCxt);
1598 
1599  /* opclasses are not required to provide a Distance method */
1601  fmgr_info_copy(&(giststate->distanceFn[i]),
1603  scanCxt);
1604  else
1605  giststate->distanceFn[i].fn_oid = InvalidOid;
1606 
1607  /* opclasses are not required to provide a Fetch method */
1609  fmgr_info_copy(&(giststate->fetchFn[i]),
1611  scanCxt);
1612  else
1613  giststate->fetchFn[i].fn_oid = InvalidOid;
1614 
1615  /*
1616  * If the index column has a specified collation, we should honor that
1617  * while doing comparisons. However, we may have a collatable storage
1618  * type for a noncollatable indexed data type. If there's no index
1619  * collation then specify default collation in case the support
1620  * functions need collation. This is harmless if the support
1621  * functions don't care about collation, so we just do it
1622  * unconditionally. (We could alternatively call get_typcollation,
1623  * but that seems like expensive overkill --- there aren't going to be
1624  * any cases where a GiST storage type has a nondefault collation.)
1625  */
1626  if (OidIsValid(index->rd_indcollation[i]))
1627  giststate->supportCollation[i] = index->rd_indcollation[i];
1628  else
1629  giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1630  }
1631 
1632  /* No opclass information for INCLUDE attributes */
1633  for (; i < index->rd_att->natts; i++)
1634  {
1635  giststate->consistentFn[i].fn_oid = InvalidOid;
1636  giststate->unionFn[i].fn_oid = InvalidOid;
1637  giststate->compressFn[i].fn_oid = InvalidOid;
1638  giststate->decompressFn[i].fn_oid = InvalidOid;
1639  giststate->penaltyFn[i].fn_oid = InvalidOid;
1640  giststate->picksplitFn[i].fn_oid = InvalidOid;
1641  giststate->equalFn[i].fn_oid = InvalidOid;
1642  giststate->distanceFn[i].fn_oid = InvalidOid;
1643  giststate->fetchFn[i].fn_oid = InvalidOid;
1644  giststate->supportCollation[i] = InvalidOid;
1645  }
1646 
1647  MemoryContextSwitchTo(oldCxt);
1648 
1649  return giststate;
1650 }
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:580
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:860
FmgrInfo distanceFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
FmgrInfo consistentFn[INDEX_MAX_KEYS]
Definition: gist_private.h:86
FmgrInfo picksplitFn[INDEX_MAX_KEYS]
Definition: gist_private.h:91
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:173

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, GISTSTATE::compressFn, GISTSTATE::consistentFn, CreateTupleDescCopyConstr(), CurrentMemoryContext, GISTSTATE::decompressFn, GISTSTATE::distanceFn, elog, GISTSTATE::equalFn, ERROR, GISTSTATE::fetchFn, fmgr_info_copy(), FmgrInfo::fn_oid, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_UNION_PROC, i, index_getprocid(), index_getprocinfo(), INDEX_MAX_KEYS, IndexRelationGetNumberOfKeyAttributes, InvalidOid, GISTSTATE::leafTupdesc, MemoryContextSwitchTo(), TupleDescData::natts, GISTSTATE::nonLeafTupdesc, OidIsValid, palloc(), GISTSTATE::penaltyFn, GISTSTATE::picksplitFn, GISTSTATE::scanCxt, GISTSTATE::supportCollation, GISTSTATE::tempCxt, and GISTSTATE::unionFn.

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