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 deleteXid)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
 
XLogRecPtr gistXLogDelete (Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon)
 
XLogRecPtr gistXLogSplit (bool page_is_leaf, SplitedPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
 
XLogRecPtr gistXLogAssignLSN (void)
 
bool gistgettuple (IndexScanDesc scan, ScanDirection dir)
 
int64 gistgetbitmap (IndexScanDesc scan, TIDBitmap *tbm)
 
bool gistcanreturn (Relation index, int attno)
 
bool gistvalidate (Oid opclassoid)
 
void gistadjustmembers (Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
 
byteagistoptions (Datum reloptions, bool validate)
 
bool gistproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 
bool gistfitpage (IndexTuple *itvec, int len)
 
bool gistnospace (Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
 
void gistcheckpage (Relation rel, Buffer buf)
 
Buffer gistNewBuffer (Relation r)
 
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 *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
 
void gistMakeUnionItVec (GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
 
bool gistKeyIsEQ (GISTSTATE *giststate, int attno, Datum a, Datum b)
 
void gistDeCompressAtt (GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
 
HeapTuple gistFetchTuple (GISTSTATE *giststate, Relation r, IndexTuple tuple)
 
void gistMakeUnionKey (GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
 
XLogRecPtr gistGetFakeLSN (Relation rel)
 
IndexBulkDeleteResultgistbulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
 
IndexBulkDeleteResultgistvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 
void gistSplitByKey (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
 
IndexBuildResultgistbuild (Relation heap, Relation index, struct IndexInfo *indexInfo)
 
GISTBuildBuffersgistInitBuildBuffers (int pagesPerBuffer, int levelStep, int maxLevel)
 
GISTNodeBuffergistGetNodeBuffer (GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
 
void gistPushItupToNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
 
bool gistPopItupFromNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)
 
void gistFreeBuildBuffers (GISTBuildBuffers *gfbb)
 
void gistRelocateBuildBuffersOnSplit (GISTBuildBuffers *gfbb, GISTSTATE *giststate, Relation r, int level, Buffer buffer, List *splitinfo)
 
void gistUnloadNodeBuffers (GISTBuildBuffers *gfbb)
 

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

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

Definition at line 324 of file gist_private.h.

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

◆ BUFFER_PAGE_DATA_OFFSET

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

Definition at line 53 of file gist_private.h.

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

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

Definition at line 147 of file gist_private.h.

◆ TUPLE_IS_INVALID

#define TUPLE_IS_INVALID   0xfffe

Definition at line 286 of file gist_private.h.

◆ TUPLE_IS_VALID

#define TUPLE_IS_VALID   0xffff

Definition at line 285 of file gist_private.h.

Typedef Documentation

◆ GISTBuildBuffers

◆ GISTInsertStack

◆ GistOptBufferingMode

◆ GiSTOptions

typedef struct GiSTOptions GiSTOptions

◆ GISTScanOpaque

Definition at line 181 of file gist_private.h.

◆ GISTScanOpaqueData

◆ GISTSearchHeapItem

◆ GISTSearchItem

◆ GistSplitVector

◆ GISTSTATE

typedef struct GISTSTATE GISTSTATE

◆ gistxlogPage

typedef struct gistxlogPage gistxlogPage

◆ SplitedPageLayout

Enumeration Type Documentation

◆ GistOptBufferingMode

Enumerator
GIST_OPTION_BUFFERING_AUTO 
GIST_OPTION_BUFFERING_ON 
GIST_OPTION_BUFFERING_OFF 

Definition at line 384 of file gist_private.h.

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

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 121 of file gist.c.

122 {
124  "GiST temporary context",
126 }
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1632 of file gist.c.

1633 {
1634  /* It's sufficient to delete the scanCxt */
1635  MemoryContextDelete(giststate->scanCxt);
1636 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
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:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#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:172
static const struct fns functions
Definition: regcomp.c:357
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 183 of file gistbuild.c.

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

References Assert(), BufferGetBlockNumber(), BufferGetPage(), GISTBuildState::buildMode, createTempGistContext(), CurrentMemoryContext, DEBUG1, elog(), END_CRIT_SECTION, ERROR, F_LEAF, fillfactor, freeGISTstate(), GISTBuildState::freespace, GIST_BUFFERING_ACTIVE, GIST_BUFFERING_AUTO, GIST_BUFFERING_DISABLED, GIST_BUFFERING_STATS, GIST_DEFAULT_FILLFACTOR, gist_indexsortbuild(), GIST_OPTION_BUFFERING_OFF, GIST_OPTION_BUFFERING_ON, GIST_ROOT_BLKNO, GIST_SORTED_BUILD, GIST_SORTSUPPORT_PROC, gistBuildCallback(), GistBuildLSN, gistEmptyAllBuffers(), gistFreeBuildBuffers(), GISTInitBuffer(), gistNewBuffer(), gistSortedBuildCallback(), GISTBuildState::giststate, IndexBuildResult::heap_tuples, GISTBuildState::heaprel, i, if(), index_getprocid(), INDEX_MAX_KEYS, IndexBuildResult::index_tuples, GISTBuildState::indexrel, IndexRelationGetNumberOfKeyAttributes, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), log_newpage_range(), MAIN_FORKNUM, maintenance_work_mem, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, PageSetLSN(), palloc(), RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), TUPLESORT_NONE, tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 132 of file gist.c.

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

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:1241
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46

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

Referenced by gisthandler().

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

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

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

Referenced by gisthandler().

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 785 of file gistutil.c.

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

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

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

◆ gistchoose()

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

Definition at line 374 of file gistutil.c.

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

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

Referenced by gistdoinsert(), and gistProcessItup().

◆ gistCompressValues()

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

Definition at line 596 of file gistutil.c.

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

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

Referenced by gistFormTuple(), and gistSortedBuildCallback().

◆ gistDeCompressAtt()

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

Definition at line 296 of file gistutil.c.

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

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

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

◆ gistdentryinit()

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

Definition at line 547 of file gistutil.c.

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

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

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

◆ gistdoinsert()

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

Definition at line 633 of file gist.c.

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

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

Referenced by gistBuildCallback(), and gistinsert().

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 95 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistFetchTuple()

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

Definition at line 667 of file gistutil.c.

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

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

Referenced by gistScanPage().

◆ gistfillbuffer()

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

Definition at line 34 of file gistutil.c.

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

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

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

◆ gistfillitupvec()

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

Definition at line 127 of file gistutil.c.

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

References i, IndexTupleSize, and palloc().

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

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 79 of file gistutil.c.

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

References GiSTPageSize, i, IndexTupleSize, and len.

Referenced by gistSplit().

◆ gistFormTuple()

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

Definition at line 575 of file gistutil.c.

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

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

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 511 of file gistbuildbuffers.c.

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

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

◆ gistgetadjusted()

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

Definition at line 316 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

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

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

Referenced by gisthandler().

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1025 of file gistutil.c.

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

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

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

◆ gistGetNodeBuffer()

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

Definition at line 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  &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:953
@ HASH_ENTER
Definition: hsearch.h:114
List * lcons(void *datum, List *list)
Definition: list.c:494
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1456
#define NIL
Definition: pg_list.h:68
List ** buffersOnLevels
Definition: gist_private.h:368
MemoryContext context
Definition: gist_private.h:341
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
BlockNumber pageBlocknum
Definition: gist_private.h:303
bool queuedForEmptying
Definition: gist_private.h:307
Definition: pg_list.h:54

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 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:165
@ ForwardScanDirection
Definition: sdir.h:28
OffsetNumber * killedItems
Definition: gist_private.h:168
GISTSTATE * giststate
Definition: gist_private.h:156
GISTSearchHeapItem pageData[BLCKSZ/sizeof(IndexTupleData)]
Definition: gist_private.h:174
BlockNumber curBlkno
Definition: gist_private.h:170
ItemPointerData heapPtr
Definition: gist_private.h:120
OffsetNumber offnum
Definition: gist_private.h:125
BlockNumber blkno
Definition: gist_private.h:133
GistNSN parentlsn
Definition: gist_private.h:136
union GISTSearchItem::@42 data
int numberOfOrderBys
Definition: relscan.h:121
bool kill_prior_tuple
Definition: relscan.h:128
ItemPointerData xs_heaptid
Definition: relscan.h:147

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

Referenced by gisthandler().

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 773 of file gistutil.c.

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

References b, BufferGetPage(), and gistinitpage().

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

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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

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

Referenced by gistInitBuffering().

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 757 of file gistutil.c.

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

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

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

◆ gistinsert()

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

Definition at line 158 of file gist.c.

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

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

Referenced by gisthandler().

◆ gistjoinvector()

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

Definition at line 114 of file gistutil.c.

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

References len, and repalloc().

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistKeyIsEQ()

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

Definition at line 281 of file gistutil.c.

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

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

Referenced by gistgetadjusted(), and gistUserPicksplit().

◆ gistMakeUnionItVec()

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

Definition at line 155 of file gistutil.c.

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

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

Referenced by gistunion(), and gistunionsubkeyvec().

◆ gistMakeUnionKey()

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

Definition at line 233 of file gistutil.c.

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

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 824 of file gistutil.c.

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

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

Referenced by gistbuild(), and gistplacetopage().

◆ gistnospace()

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

Definition at line 59 of file gistutil.c.

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

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

Referenced by gistplacetopage().

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 921 of file gistutil.c.

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

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

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 897 of file gistutil.c.

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

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

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

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

Definition at line 724 of file gistutil.c.

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

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

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

◆ gistplacetopage()

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

Definition at line 224 of file gist.c.

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

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

Referenced by gistbufferinginserttuples(), and gistinserttuples().

◆ gistPopItupFromNodeBuffer()

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

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 *itup)
BlockNumber prev
Definition: gist_private.h:48

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

Referenced by gistProcessEmptyingQueue(), and gistRelocateBuildBuffersOnSplit().

◆ gistproperty()

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

Definition at line 942 of file gistutil.c.

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

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

Referenced by gisthandler().

◆ gistPushItupToNodeBuffer()

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

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 WriteTempFileBlock(BufFile *file, long blknum, const void *ptr)
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
static void gistPlaceItupToPage(GISTNodeBufferPage *pageBuffer, IndexTuple itup)
static GISTNodeBufferPage * gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
static long gistBuffersGetFreeBlock(GISTBuildBuffers *gfbb)

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ gistRelocateBuildBuffersOnSplit()

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

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

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

Referenced by gistbufferinginserttuples().

◆ gistSplit()

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

Definition at line 1417 of file gist.c.

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

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

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistSplitByKey()

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

Definition at line 623 of file gistsplit.c.

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

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

Referenced by gistSplit().

◆ gistunion()

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

Definition at line 219 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_levelstate_flush().

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 276 of file gistbuildbuffers.c.

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

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

Referenced by gistProcessEmptyingQueue().

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 75 of file gistvacuum.c.

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

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

Referenced by gisthandler().

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

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

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

Referenced by gisthandler().

◆ gistXLogAssignLSN()

XLogRecPtr gistXLogAssignLSN ( void  )

Definition at line 582 of file gistxlog.c.

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

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

Referenced by gistGetFakeLSN().

◆ gistXLogDelete()

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

Definition at line 674 of file gistxlog.c.

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

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

Referenced by gistprunepage().

◆ gistXLogPageDelete()

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

Definition at line 558 of file gistxlog.c.

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

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

Definition at line 600 of file gistxlog.c.

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

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

Referenced by gistNewBuffer().

◆ gistXLogSplit()

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

Definition at line 501 of file gistxlog.c.

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

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

Referenced by gistplacetopage().

◆ gistXLogUpdate()

XLogRecPtr gistXLogUpdate ( Buffer  buffer,
OffsetNumber todelete,
int  ntodelete,
IndexTuple itup,
int  ituplen,
Buffer  leftchildbuf 
)

Definition at line 633 of file gistxlog.c.

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

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