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, Relation heaprel, BlockNumber blkno, FullTransactionId deleteXid)
 
XLogRecPtr gistXLogUpdate (Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
 
XLogRecPtr gistXLogDelete (Buffer buffer, OffsetNumber *todelete, int ntodelete, TransactionId snapshotConflictHorizon, Relation heaprel)
 
XLogRecPtr gistXLogSplit (bool page_is_leaf, 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, Relation heaprel)
 
bool gistPageRecyclable (Page page)
 
void gistfillbuffer (Page page, IndexTuple *itup, int len, OffsetNumber off)
 
IndexTuplegistextractpage (Page page, int *len)
 
IndexTuplegistjoinvector (IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
 
IndexTupleDatagistfillitupvec (IndexTuple *vec, int veclen, int *memlen)
 
IndexTuple gistunion (Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
 
IndexTuple gistgetadjusted (Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
 
IndexTuple gistFormTuple (GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf)
 
void gistCompressValues (GISTSTATE *giststate, Relation r, const Datum *attdata, const bool *isnull, bool isleaf, Datum *compatt)
 
OffsetNumber gistchoose (Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
 
void GISTInitBuffer (Buffer b, uint32 f)
 
void gistinitpage (Page page, uint32 f)
 
void gistdentryinit (GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
 
float gistpenalty (GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
 
void gistMakeUnionItVec (GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
 
bool gistKeyIsEQ (GISTSTATE *giststate, int attno, Datum a, Datum b)
 
void gistDeCompressAtt (GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
 
HeapTuple gistFetchTuple (GISTSTATE *giststate, Relation r, IndexTuple tuple)
 
void gistMakeUnionKey (GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
 
XLogRecPtr gistGetFakeLSN (Relation rel)
 
IndexBulkDeleteResultgistbulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
 
IndexBulkDeleteResultgistvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 
void gistSplitByKey (Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
 
IndexBuildResultgistbuild (Relation heap, Relation index, struct IndexInfo *indexInfo)
 
GISTBuildBuffersgistInitBuildBuffers (int pagesPerBuffer, int levelStep, int maxLevel)
 
GISTNodeBuffergistGetNodeBuffer (GISTBuildBuffers *gfbb, GISTSTATE *giststate, BlockNumber nodeBlocknum, int level)
 
void gistPushItupToNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple itup)
 
bool gistPopItupFromNodeBuffer (GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer, IndexTuple *itup)
 
void gistFreeBuildBuffers (GISTBuildBuffers *gfbb)
 
void gistRelocateBuildBuffersOnSplit (GISTBuildBuffers *gfbb, GISTSTATE *giststate, Relation r, int level, Buffer buffer, List *splitinfo)
 
void gistUnloadNodeBuffers (GISTBuildBuffers *gfbb)
 

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

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

Definition at line 324 of file gist_private.h.

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

◆ BUFFER_PAGE_DATA_OFFSET

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

Definition at line 53 of file gist_private.h.

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 480 of file gist_private.h.

◆ GIST_EXCLUSIVE

#define GIST_EXCLUSIVE   BUFFER_LOCK_EXCLUSIVE

Definition at line 43 of file gist_private.h.

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 479 of file gist_private.h.

◆ GIST_ROOT_BLKNO

#define GIST_ROOT_BLKNO   0

Definition at line 262 of file gist_private.h.

◆ GIST_SHARE

#define GIST_SHARE   BUFFER_LOCK_SHARE

Definition at line 42 of file gist_private.h.

◆ GIST_UNLOCK

#define GIST_UNLOCK   BUFFER_LOCK_UNLOCK

Definition at line 44 of file gist_private.h.

◆ GiSTPageSize

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

Definition at line 476 of file gist_private.h.

◆ GISTSearchItemIsHeap

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

Definition at line 145 of file gist_private.h.

◆ GistTupleIsInvalid

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

Definition at line 288 of file gist_private.h.

◆ GistTupleSetValid

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

Definition at line 289 of file gist_private.h.

◆ LEVEL_HAS_BUFFERS

#define LEVEL_HAS_BUFFERS (   nlevel,
  gfbb 
)
Value:
((nlevel) != 0 && (nlevel) % (gfbb)->levelStep == 0 && \
(nlevel) != (gfbb)->rootlevel)
Datum nlevel(PG_FUNCTION_ARGS)
Definition: ltree_op.c: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 122 of file gist.c.

123 {
125  "GiST temporary context",
127 }
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
#define AllocSetContextCreate
Definition: memutils.h:126
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:150

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1653 of file gist.c.

1654 {
1655  /* It's sufficient to delete the scanCxt */
1656  MemoryContextDelete(giststate->scanCxt);
1657 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
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:356
Oid refobjid
Definition: amapi.h:90
bool ref_is_family
Definition: amapi.h:89
int number
Definition: amapi.h:84
bool ref_is_hard
Definition: amapi.h:88

References ereport, errcode(), errmsg(), ERROR, functions, GIST_COMPRESS_PROC, GIST_CONSISTENT_PROC, GIST_DECOMPRESS_PROC, GIST_DISTANCE_PROC, GIST_EQUAL_PROC, GIST_FETCH_PROC, GIST_OPTIONS_PROC, GIST_PENALTY_PROC, GIST_PICKSPLIT_PROC, GIST_SORTSUPPORT_PROC, GIST_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, heap);
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:3386
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4590
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2198
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:229
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
Pointer Page
Definition: bufpage.h:78
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
#define OidIsValid(objectId)
Definition: c.h:764
#define DEBUG1
Definition: elog.h:30
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1525
MemoryContext createTempGistContext(void)
Definition: gist.c:122
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1653
#define F_LEAF
Definition: gist.h:46
#define GistBuildLSN
Definition: gist.h:67
#define GIST_ROOT_BLKNO
Definition: gist_private.h:262
#define GIST_DEFAULT_FILLFACTOR
Definition: gist_private.h:480
@ GIST_BUFFERING_AUTO
Definition: gistbuild.c:72
@ GIST_SORTED_BUILD
Definition: gistbuild.c:69
@ GIST_BUFFERING_ACTIVE
Definition: gistbuild.c:78
@ GIST_BUFFERING_STATS
Definition: gistbuild.c:75
@ GIST_BUFFERING_DISABLED
Definition: gistbuild.c:70
static void gist_indexsortbuild(GISTBuildState *state)
Definition: gistbuild.c:404
static void gistBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gistbuild.c:886
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:1436
void gistFreeBuildBuffers(GISTBuildBuffers *gfbb)
Buffer gistNewBuffer(Relation r, Relation heaprel)
Definition: gistutil.c:824
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:773
int maintenance_work_mem
Definition: globals.c:129
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:792
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:1226
#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:187
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RelationNeedsWAL(relation)
Definition: rel.h:629
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:523
@ 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:1382
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:969
#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:1271

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

Referenced by gisthandler().

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 133 of file gist.c.

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

References BMR_REL, EB_LOCK_FIRST, EB_SKIP_EXTENSION_LOCK, END_CRIT_SECTION, ExtendBufferedRel(), F_LEAF, GISTInitBuffer(), INIT_FORKNUM, log_newpage_buffer(), MarkBufferDirty(), START_CRIT_SECTION, and UnlockReleaseBuffer().

Referenced by gisthandler().

◆ gistbulkdelete()

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

Definition at line 59 of file gistvacuum.c.

61 {
62  /* allocate stats if first time through, else re-use existing struct */
63  if (stats == NULL)
65 
66  gistvacuumscan(info, stats, callback, callback_state);
67 
68  return stats;
69 }
static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125
void * palloc0(Size size)
Definition: mcxt.c:1257
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 794 of file gistget.c.

795 {
799  return true;
800  else
801  return false;
802 }

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:800
int errhint(const char *fmt,...)
Definition: elog.c:1316
static char * buf
Definition: pg_test_fsync.c:73

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

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

◆ gistchoose()

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

Definition at line 374 of file gistutil.c.

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

Definition at line 596 of file gistutil.c.

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

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

Referenced by gistFormTuple(), and gistSortedBuildCallback().

◆ gistDeCompressAtt()

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

Definition at line 296 of file gistutil.c.

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

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

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

◆ gistdentryinit()

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

Definition at line 547 of file gistutil.c.

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

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

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

◆ gistdoinsert()

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

Definition at line 634 of file gist.c.

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

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

Referenced by gistBuildCallback(), and gistinsert().

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 95 of file gistutil.c.

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

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

Referenced by gist_indexsortbuild_levelstate_flush(), and gistplacetopage().

◆ gistFetchTuple()

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

Definition at line 667 of file gistutil.c.

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

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

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

◆ gistfillitupvec()

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

Definition at line 127 of file gistutil.c.

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

References i, IndexTupleSize, and palloc().

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

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 79 of file gistutil.c.

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

References GiSTPageSize, i, IndexTupleSize, and len.

Referenced by gistSplit().

◆ gistFormTuple()

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

Definition at line 575 of file gistutil.c.

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

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

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

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

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

◆ gistgetadjusted()

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

Definition at line 316 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 744 of file gistget.c.

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

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

Referenced by gisthandler().

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1016 of file gistutil.c.

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

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

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

Referenced by gisthandler().

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 773 of file gistutil.c.

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

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

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

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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

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

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:1154
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:1132
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,
Relation  heaprel 
)

Definition at line 824 of file gistutil.c.

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

References BMR_REL, BufferGetPage(), ConditionalLockBuffer(), EB_LOCK_FIRST, ExtendBufferedRel(), GetFreeIndexPage(), GIST_UNLOCK, gistcheckpage(), GistPageGetDeleteXid(), gistPageRecyclable(), gistXLogPageReuse(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, PageIsNew(), ReadBuffer(), RelationNeedsWAL, ReleaseBuffer(), and XLogStandbyInfoActive.

Referenced by gistbuild(), and gistplacetopage().

◆ gistnospace()

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

Definition at line 59 of file gistutil.c.

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

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

Referenced by gistplacetopage().

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 912 of file gistutil.c.

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

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

Referenced by gisthandler().

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 888 of file gistutil.c.

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

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

Referenced by gistNewBuffer(), and gistvacuumpage().

◆ gistpenalty()

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

Definition at line 724 of file gistutil.c.

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

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

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

◆ gistplacetopage()

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

Definition at line 225 of file gist.c.

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

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

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

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

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

Referenced by gisthandler().

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 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:735
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1840
#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:1337
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:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
@ OPFAMILYOID
Definition: syscache.h:74
@ CLAOID
Definition: syscache.h:48
@ AMOPSTRATEGY
Definition: syscache.h:38
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:218

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 578 of file gistxlog.c.

579 {
580  int dummy = 0;
581 
582  /*
583  * Records other than XLOG_SWITCH must have content. We use an integer 0
584  * to follow the restriction.
585  */
586  XLogBeginInsert();
588  XLogRegisterData((char *) &dummy, sizeof(dummy));
589  return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
590 }
#define XLOG_GIST_ASSIGN_LSN
Definition: gistxlog.h:27
#define XLOG_MARK_UNIMPORTANT
Definition: xlog.h:153
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:365
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:475
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:457
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,
Relation  heaprel 
)

Definition at line 672 of file gistxlog.c.

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

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

Referenced by gistprunepage().

◆ gistXLogPageDelete()

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

Definition at line 554 of file gistxlog.c.

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

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

Referenced by gistdeletepage().

◆ gistXLogPageReuse()

void gistXLogPageReuse ( Relation  rel,
Relation  heaprel,
BlockNumber  blkno,
FullTransactionId  deleteXid 
)

Definition at line 596 of file gistxlog.c.

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

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

Referenced by gistNewBuffer().

◆ gistXLogSplit()

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

Definition at line 497 of file gistxlog.c.

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

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

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

Referenced by gistplacetopage(), and gistvacuumpage().

◆ initGISTstate()

GISTSTATE* initGISTstate ( Relation  index)

Definition at line 1525 of file gist.c.

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