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

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

enum  GistOptBufferingMode { GIST_OPTION_BUFFERING_AUTO , GIST_OPTION_BUFFERING_ON , GIST_OPTION_BUFFERING_OFF }
 

Functions

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

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

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

Definition at line 324 of file gist_private.h.

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

◆ BUFFER_PAGE_DATA_OFFSET

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

Definition at line 53 of file gist_private.h.

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 479 of file gist_private.h.

◆ GIST_EXCLUSIVE

#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE

Definition at line 43 of file gist_private.h.

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 478 of file gist_private.h.

◆ GIST_ROOT_BLKNO

#define GIST_ROOT_BLKNO   0

Definition at line 262 of file gist_private.h.

◆ GIST_SHARE

#define GIST_SHARE   BUFFER_LOCK_SHARE

Definition at line 42 of file gist_private.h.

◆ GIST_UNLOCK

#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK

Definition at line 44 of file gist_private.h.

◆ GiSTPageSize

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

Definition at line 475 of file gist_private.h.

◆ GISTSearchItemIsHeap

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

Definition at line 145 of file gist_private.h.

◆ GistTupleIsInvalid

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

Definition at line 288 of file gist_private.h.

◆ GistTupleSetValid

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

Definition at line 289 of file gist_private.h.

◆ LEVEL_HAS_BUFFERS

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

Definition at line 319 of file gist_private.h.

◆ PAGE_FREE_SPACE

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

Definition at line 55 of file gist_private.h.

◆ PAGE_IS_EMPTY

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

Definition at line 57 of file gist_private.h.

◆ PAGE_NO_SPACE

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

Definition at line 59 of file gist_private.h.

◆ SizeOfGISTSearchItem

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

Definition at line 147 of file gist_private.h.

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 286 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 285 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GistOptBufferingMode

◆ GiSTOptions

typedef struct GiSTOptions GiSTOptions

◆ GISTScanOpaque

Definition at line 181 of file gist_private.h.

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

typedef struct GISTSTATE GISTSTATE

◆ gistxlogPage

typedef struct gistxlogPage gistxlogPage

◆ SplitedPageLayout

Enumeration Type Documentation

◆ GistOptBufferingMode

Enumerator
GIST_OPTION_BUFFERING_AUTO 
GIST_OPTION_BUFFERING_ON 
GIST_OPTION_BUFFERING_OFF 

Definition at line 384 of file gist_private.h.

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

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 119 of file gist.c.

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

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1631 of file gist.c.

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

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

◆ gistadjustmembers()

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

Definition at line 291 of file gistvalidate.c.

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

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

Referenced by gisthandler().

◆ gistbuild()

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

Definition at line 175 of file gistbuild.c.

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

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, GistSortedBuildPageState::page, PageSetLSN, palloc(), RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 130 of file gist.c.

131 {
132  Buffer buffer;
133 
134  /* Initialize the root page */
137 
138  /* Initialize and xlog buffer */
140  GISTInitBuffer(buffer, F_LEAF);
141  MarkBufferDirty(buffer);
142  log_newpage_buffer(buffer, true);
144 
145  /* Unlock and release the buffer */
146  UnlockReleaseBuffer(buffer);
147 }
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4007
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:741
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
@ RBM_NORMAL
Definition: bufmgr.h:39
@ INIT_FORKNUM
Definition: relpath.h:46
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1144

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

Referenced by gisthandler().

◆ gistbulkdelete()

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

Definition at line 59 of file gistvacuum.c.

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

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

Referenced by gisthandler().

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

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

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

Referenced by gisthandler().

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 784 of file gistutil.c.

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

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 373 of file gistutil.c.

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

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

Referenced by gistdoinsert(), and gistProcessItup().

◆ gistCompressValues()

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

Definition at line 595 of file gistutil.c.

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

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

Referenced by gistFormTuple(), and gistSortedBuildCallback().

◆ gistDeCompressAtt()

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

Definition at line 295 of file gistutil.c.

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

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 546 of file gistutil.c.

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

References DatumGetPointer, GISTSTATE::decompressFn, FmgrInfo::fn_oid, FunctionCall1Coll(), gistentryinit, GISTENTRY::key, GISTENTRY::leafkey, GISTENTRY::offset, OidIsValid, GISTENTRY::page, PointerGetDatum, GISTENTRY::rel, and GISTSTATE::supportCollation.

Referenced by gistchoose(), gistDeCompressAtt(), gistindex_keytest(), gistMakeUnionItVec(), and gistSplitByKey().

◆ gistdoinsert()

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

Definition at line 632 of file gist.c.

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

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

Referenced by gistBuildCallback(), and gistinsert().

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 94 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_pagestate_flush(), and gistplacetopage().

◆ gistFetchTuple()

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

Definition at line 666 of file gistutil.c.

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

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

Referenced by gistScanPage().

◆ gistfillbuffer()

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

Definition at line 33 of file gistutil.c.

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

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

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

◆ gistfillitupvec()

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

Definition at line 126 of file gistutil.c.

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

References i, IndexTupleSize, and palloc().

Referenced by gistplacetopage(), and gistSplit().

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 78 of file gistutil.c.

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

References GiSTPageSize, i, IndexTupleSize, and len.

Referenced by gistSplit().

◆ gistFormTuple()

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

Definition at line 574 of file gistutil.c.

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

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

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 511 of file gistbuildbuffers.c.

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

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

◆ gistgetadjusted()

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

Definition at line 315 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

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

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

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

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

◆ gistGetNodeBuffer()

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

Definition at line 117 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 614 of file gistget.c.

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

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

References b, BufferGetPage, and gistinitpage().

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

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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

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

Referenced by gistInitBuffering().

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 756 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild(), gist_indexsortbuild_pagestate_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 156 of file gist.c.

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

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 113 of file gistutil.c.

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

References len, and repalloc().

Referenced by gistplacetopage().

◆ gistKeyIsEQ()

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

Definition at line 280 of file gistutil.c.

281 {
282  bool result;
283 
284  FunctionCall3Coll(&giststate->equalFn[attno],
285  giststate->supportCollation[attno],
286  a, b,
287  PointerGetDatum(&result));
288  return result;
289 }
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1170
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 154 of file gistutil.c.

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

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

Referenced by gistunion(), and gistunionsubkeyvec().

◆ gistMakeUnionKey()

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

Definition at line 232 of file gistutil.c.

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

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 823 of file gistutil.c.

824 {
825  Buffer buffer;
826  bool needLock;
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, 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  needLock = !RELATION_IS_LOCAL(r);
881 
882  if (needLock)
884 
885  buffer = ReadBuffer(r, P_NEW);
886  LockBuffer(buffer, GIST_EXCLUSIVE);
887 
888  if (needLock)
890 
891  return buffer;
892 }
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:4033
static FullTransactionId GistPageGetDeleteXid(Page page)
Definition: gist.h:213
bool gistPageRecyclable(Page page)
Definition: gistutil.c:896
void gistXLogPageReuse(Relation rel, BlockNumber blkno, FullTransactionId latestRemovedXid)
Definition: gistxlog.c:599
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:403
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:453
#define ExclusiveLock
Definition: lockdefs.h:42
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:621
#define XLogStandbyInfoActive()
Definition: xlog.h:178

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

Referenced by gistbuild(), and gistplacetopage().

◆ gistnospace()

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

Definition at line 58 of file gistutil.c.

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

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

Referenced by gistplacetopage().

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 920 of file gistutil.c.

921 {
922  static const relopt_parse_elt tab[] = {
923  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
924  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
925  };
926 
927  return (bytea *) build_reloptions(reloptions, validate,
929  sizeof(GiSTOptions),
930  tab, lengthof(tab));
931 }
#define lengthof(array)
Definition: c.h:734
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1904
@ RELOPT_KIND_GIST
Definition: reloptions.h:47
@ RELOPT_TYPE_ENUM
Definition: reloptions.h:34
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:622

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

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 896 of file gistutil.c.

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

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

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

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

Definition at line 723 of file gistutil.c.

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

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

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

◆ gistplacetopage()

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

Definition at line 222 of file gist.c.

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

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

Referenced by gistbufferinginserttuples(), and gistinserttuples().

◆ gistPopItupFromNodeBuffer()

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

Definition at line 410 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessEmptyingQueue(), and gistRelocateBuildBuffersOnSplit().

◆ gistproperty()

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

Definition at line 941 of file gistutil.c.

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

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

Referenced by gisthandler().

◆ gistPushItupToNodeBuffer()

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

Definition at line 340 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistRelocateBuildBuffersOnSplit()

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

Definition at line 537 of file gistbuildbuffers.c.

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

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

Referenced by gistbufferinginserttuples().

◆ gistSplit()

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

Definition at line 1416 of file gist.c.

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

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

Referenced by gistplacetopage().

◆ gistSplitByKey()

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

Definition at line 623 of file gistsplit.c.

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

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

Referenced by gistSplit().

◆ gistunion()

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

Definition at line 218 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_pagestate_flush().

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 276 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessEmptyingQueue().

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 75 of file gistvacuum.c.

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

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

Referenced by gisthandler().

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

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

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

Referenced by gisthandler().

◆ gistValidateBufferingOption()

void gistValidateBufferingOption ( const char *  value)

◆ gistXLogAssignLSN()

XLogRecPtr gistXLogAssignLSN ( void  )

Definition at line 581 of file gistxlog.c.

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

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

Referenced by gistGetFakeLSN().

◆ gistXLogDelete()

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

Definition at line 673 of file gistxlog.c.

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

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

Referenced by gistprunepage().

◆ gistXLogPageDelete()

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

Definition at line 557 of file gistxlog.c.

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

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

Referenced by gistdeletepage().

◆ gistXLogPageReuse()

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

Definition at line 599 of file gistxlog.c.

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

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

Referenced by gistNewBuffer().

◆ gistXLogSplit()

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

Definition at line 500 of file gistxlog.c.

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

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

Referenced by gistplacetopage().

◆ gistXLogUpdate()

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

Definition at line 632 of file gistxlog.c.

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

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

Referenced by gistplacetopage(), and gistvacuumpage().

◆ initGISTstate()

GISTSTATE* initGISTstate ( Relation  index)

Definition at line 1503 of file gist.c.

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