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

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

enum  GistOptBufferingMode { GIST_OPTION_BUFFERING_AUTO, GIST_OPTION_BUFFERING_ON, GIST_OPTION_BUFFERING_OFF }
 

Functions

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

Macro Definition Documentation

◆ BUFFER_HALF_FILLED

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

Definition at line 324 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ BUFFER_OVERFLOWED

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

Definition at line 332 of file gist_private.h.

Referenced by gistProcessItup().

◆ BUFFER_PAGE_DATA_OFFSET

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

◆ GIST_DEFAULT_FILLFACTOR

#define GIST_DEFAULT_FILLFACTOR   90

Definition at line 479 of file gist_private.h.

Referenced by gistbuild().

◆ GIST_EXCLUSIVE

◆ GIST_MAX_SPLIT_PAGES

#define GIST_MAX_SPLIT_PAGES   75

Definition at line 39 of file gist_private.h.

Referenced by gistplacetopage().

◆ GIST_MIN_FILLFACTOR

#define GIST_MIN_FILLFACTOR   10

Definition at line 478 of file gist_private.h.

◆ GIST_ROOT_BLKNO

◆ GIST_SHARE

◆ GIST_UNLOCK

◆ GiSTPageSize

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

Definition at line 475 of file gist_private.h.

Referenced by gistfitpage(), and gistSplit().

◆ GISTSearchItemIsHeap

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

Definition at line 145 of file gist_private.h.

Referenced by getNextNearest(), gistScanPage(), and pairingheap_GISTSearchItem_cmp().

◆ GistTupleIsInvalid

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

Definition at line 288 of file gist_private.h.

Referenced by gistdoinsert(), gistindex_keytest(), and gistvacuumpage().

◆ GistTupleSetValid

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

Definition at line 289 of file gist_private.h.

Referenced by gistformdownlink(), and gistplacetopage().

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

◆ PAGE_FREE_SPACE

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

◆ PAGE_IS_EMPTY

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

Definition at line 57 of file gist_private.h.

Referenced by gistGetItupFromPage(), and gistPopItupFromNodeBuffer().

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

Definition at line 59 of file gist_private.h.

Referenced by gistPushItupToNodeBuffer().

◆ SizeOfGISTSearchItem

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

Definition at line 147 of file gist_private.h.

Referenced by gistScanPage().

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

Function Documentation

◆ createTempGistContext()

MemoryContext createTempGistContext ( void  )

Definition at line 119 of file gist.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and CurrentMemoryContext.

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

120 {
122  "GiST temporary context",
124 }
#define AllocSetContextCreate
Definition: memutils.h:170
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38

◆ freeGISTstate()

void freeGISTstate ( GISTSTATE giststate)

Definition at line 1628 of file gist.c.

References MemoryContextDelete(), and GISTSTATE::scanCxt.

Referenced by gistbuild(), and gistendscan().

1629 {
1630  /* It's sufficient to delete the scanCxt */
1631  MemoryContextDelete(giststate->scanCxt);
1632 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
MemoryContext scanCxt
Definition: gist_private.h:77

◆ gistadjustmembers()

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

Definition at line 291 of file gistvalidate.c.

References ereport, errcode(), errmsg(), ERROR, 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().

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 }
#define GIST_OPTIONS_PROC
Definition: gist.h:39
bool ref_is_hard
Definition: amapi.h:88
#define GIST_FETCH_PROC
Definition: gist.h:38
int number
Definition: amapi.h:84
#define GIST_EQUAL_PROC
Definition: gist.h:36
#define GIST_SORTSUPPORT_PROC
Definition: gist.h:40
int errcode(int sqlerrcode)
Definition: elog.c:704
#define GIST_PICKSPLIT_PROC
Definition: gist.h:35
#define ERROR
Definition: elog.h:45
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define ereport(elevel,...)
Definition: elog.h:155
#define GIST_CONSISTENT_PROC
Definition: gist.h:30
#define GIST_UNION_PROC
Definition: gist.h:31
#define lfirst(lc)
Definition: pg_list.h:169
#define GIST_PENALTY_PROC
Definition: gist.h:34
#define GIST_DISTANCE_PROC
Definition: gist.h:37
bool ref_is_family
Definition: amapi.h:89
int errmsg(const char *fmt,...)
Definition: elog.c:915
Oid refobjid
Definition: amapi.h:90
#define GIST_DECOMPRESS_PROC
Definition: gist.h:33

◆ gistbuild()

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

Definition at line 175 of file gistbuild.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, GiSTOptions::buffering_mode, GISTBuildState::buildMode, createTempGistContext(), CurrentMemoryContext, DEBUG1, elog, END_CRIT_SECTION, ERROR, F_LEAF, fillfactor, GiSTOptions::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, index_getprocid(), INDEX_MAX_KEYS, IndexBuildResult::index_tuples, GISTBuildState::indexrel, IndexRelationGetNumberOfKeyAttributes, GISTBuildState::indtuples, GISTBuildState::indtuplesSize, initGISTstate(), log_newpage_range(), MAIN_FORKNUM, maintenance_work_mem, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, GistSortedBuildPageState::page, PageSetLSN, palloc(), RelationData::rd_options, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, GISTBuildState::sortstate, START_CRIT_SECTION, table_index_build_scan(), GISTSTATE::tempCxt, tuplesort_begin_index_gist(), tuplesort_end(), tuplesort_performsort(), and UnlockReleaseBuffer().

Referenced by gisthandler().

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

◆ gistbuildempty()

void gistbuildempty ( Relation  index)

Definition at line 130 of file gist.c.

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

Referenced by gisthandler().

131 {
132  Buffer buffer;
133 
134  /* Initialize the root page */
135  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
137 
138  /* Initialize and xlog buffer */
140  GISTInitBuffer(buffer, F_LEAF);
141  MarkBufferDirty(buffer);
142  log_newpage_buffer(buffer, true);
144 
145  /* Unlock and release the buffer */
146  UnlockReleaseBuffer(buffer);
147 }
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1090
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:666
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3723
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define F_LEAF
Definition: gist.h:46
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:775
int Buffer
Definition: buf.h:23

◆ gistbulkdelete()

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

Definition at line 59 of file gistvacuum.c.

References gistvacuumscan(), and palloc0().

Referenced by gisthandler().

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

◆ gistcanreturn()

bool gistcanreturn ( Relation  index,
int  attno 
)

Definition at line 795 of file gistget.c.

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

Referenced by gisthandler().

796 {
797  if (attno > IndexRelationGetNumberOfKeyAttributes(index) ||
798  OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
800  return true;
801  else
802  return false;
803 }
#define GIST_FETCH_PROC
Definition: gist.h:38
#define OidIsValid(objectId)
Definition: c.h:698
#define GIST_COMPRESS_PROC
Definition: gist.h:32
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:476
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:769

◆ gistcheckpage()

void gistcheckpage ( Relation  rel,
Buffer  buf 
)

Definition at line 787 of file gistutil.c.

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

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

788 {
789  Page page = BufferGetPage(buf);
790 
791  /*
792  * ReadBuffer verifies that every newly-read page passes
793  * PageHeaderIsValid, which means it either contains a reasonably sane
794  * page header or is all-zero. We have to defend against the all-zero
795  * case, however.
796  */
797  if (PageIsNew(page))
798  ereport(ERROR,
799  (errcode(ERRCODE_INDEX_CORRUPTED),
800  errmsg("index \"%s\" contains unexpected zero page at block %u",
803  errhint("Please REINDEX it.")));
804 
805  /*
806  * Additionally check that the special area looks sane.
807  */
808  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
809  ereport(ERROR,
810  (errcode(ERRCODE_INDEX_CORRUPTED),
811  errmsg("index \"%s\" contains corrupted page at block %u",
814  errhint("Please REINDEX it.")));
815 }
int errhint(const char *fmt,...)
Definition: elog.c:1162
int errcode(int sqlerrcode)
Definition: elog.c:704
#define ERROR
Definition: elog.h:45
static char * buf
Definition: pg_test_fsync.c:68
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define ereport(elevel,...)
Definition: elog.h:155
#define MAXALIGN(LEN)
Definition: c.h:745
#define PageGetSpecialSize(page)
Definition: bufpage.h:300
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:915
Pointer Page
Definition: bufpage.h:78

◆ gistchoose()

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

Definition at line 373 of file gistutil.c.

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

Referenced by gistdoinsert(), and gistProcessItup().

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

◆ gistCompressValues()

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

Definition at line 595 of file gistutil.c.

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

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

◆ gistDeCompressAtt()

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

Definition at line 295 of file gistutil.c.

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

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

297 {
298  int i;
299 
300  for (i = 0; i < IndexRelationGetNumberOfKeyAttributes(r); i++)
301  {
302  Datum datum;
303 
304  datum = index_getattr(tuple, i + 1, giststate->leafTupdesc, &isnull[i]);
305  gistdentryinit(giststate, i, &attdata[i],
306  datum, r, p, o,
307  false, isnull[i]);
308  }
309 }
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:546
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:476
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
int i

◆ gistdentryinit()

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

Definition at line 546 of file gistutil.c.

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

549 {
550  if (!isNull)
551  {
552  GISTENTRY *dep;
553 
554  gistentryinit(*e, k, r, pg, o, l);
555 
556  /* there may not be a decompress function in opclass */
557  if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
558  return;
559 
560  dep = (GISTENTRY *)
562  giststate->supportCollation[nkey],
563  PointerGetDatum(e)));
564  /* decompressFn may just return the given pointer */
565  if (dep != e)
566  gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
567  dep->leafkey);
568  }
569  else
570  gistentryinit(*e, (Datum) 0, r, pg, o, l);
571 }
Relation rel
Definition: gist.h:153
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
#define OidIsValid(objectId)
Definition: c.h:698
Page page
Definition: gist.h:154
Datum key
Definition: gist.h:152
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
bool leafkey
Definition: gist.h:156
uintptr_t Datum
Definition: postgres.h:367
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1131
Oid fn_oid
Definition: fmgr.h:59
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:236
#define DatumGetPointer(X)
Definition: postgres.h:549
OffsetNumber offset
Definition: gist.h:155

◆ gistdoinsert()

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

Definition at line 629 of file gist.c.

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

Referenced by gistBuildCallback(), and gistinsert().

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

◆ gistextractpage()

IndexTuple* gistextractpage ( Page  page,
int *  len 
)

Definition at line 94 of file gistutil.c.

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

Referenced by gist_indexsortbuild_pagestate_flush(), and gistplacetopage().

95 {
97  maxoff;
98  IndexTuple *itvec;
99 
100  maxoff = PageGetMaxOffsetNumber(page);
101  *len = maxoff;
102  itvec = palloc(sizeof(IndexTuple) * maxoff);
103  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
104  itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
105 
106  return itvec;
107 }
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
void * palloc(Size size)
Definition: mcxt.c:950
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ gistFetchTuple()

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

Definition at line 666 of file gistutil.c.

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

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

◆ gistfillbuffer()

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

Definition at line 33 of file gistutil.c.

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

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

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

◆ gistfillitupvec()

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

Definition at line 126 of file gistutil.c.

References i, IndexTupleSize, and palloc().

Referenced by gistplacetopage(), and gistSplit().

127 {
128  char *ptr,
129  *ret;
130  int i;
131 
132  *memlen = 0;
133 
134  for (i = 0; i < veclen; i++)
135  *memlen += IndexTupleSize(vec[i]);
136 
137  ptr = ret = palloc(*memlen);
138 
139  for (i = 0; i < veclen; i++)
140  {
141  memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
142  ptr += IndexTupleSize(vec[i]);
143  }
144 
145  return (IndexTupleData *) ret;
146 }
void * palloc(Size size)
Definition: mcxt.c:950
int i
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistfitpage()

bool gistfitpage ( IndexTuple itvec,
int  len 
)

Definition at line 78 of file gistutil.c.

References GiSTPageSize, i, and IndexTupleSize.

Referenced by gistSplit().

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

◆ gistFormTuple()

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

Definition at line 574 of file gistutil.c.

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

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

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

◆ gistFreeBuildBuffers()

void gistFreeBuildBuffers ( GISTBuildBuffers gfbb)

Definition at line 511 of file gistbuildbuffers.c.

References BufFileClose(), and GISTBuildBuffers::pfile.

Referenced by gistbuild().

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:395

◆ gistgetadjusted()

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

Definition at line 315 of file gistutil.c.

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

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

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

◆ gistgetbitmap()

int64 gistgetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 745 of file gistget.c.

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

Referenced by gisthandler().

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

◆ gistGetFakeLSN()

XLogRecPtr gistGetFakeLSN ( Relation  rel)

Definition at line 1027 of file gistutil.c.

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

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

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

◆ gistGetNodeBuffer()

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

Definition at line 117 of file gistbuildbuffers.c.

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

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

◆ gistgettuple()

bool gistgettuple ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 614 of file gistget.c.

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

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

◆ GISTInitBuffer()

void GISTInitBuffer ( Buffer  b,
uint32  f 
)

Definition at line 775 of file gistutil.c.

References BufferGetPage, gistinitpage(), and GistSortedBuildPageState::page.

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

776 {
777  Page page;
778 
779  page = BufferGetPage(b);
780  gistinitpage(page, f);
781 }
void gistinitpage(Page page, uint32 f)
Definition: gistutil.c:756
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Pointer Page
Definition: bufpage.h:78

◆ gistInitBuildBuffers()

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

Definition at line 48 of file gistbuildbuffers.c.

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

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 }
#define NIL
Definition: pg_list.h:65
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
MemoryContext hcxt
Definition: hsearch.h:86
MemoryContext context
Definition: gist_private.h:341
List ** buffersOnLevels
Definition: gist_private.h:368
Size entrysize
Definition: hsearch.h:76
uint32 BlockNumber
Definition: block.h:31
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:188
List * bufferEmptyingQueue
Definition: gist_private.h:357
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:349
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define HASH_BLOBS
Definition: hsearch.h:97
Size keysize
Definition: hsearch.h:75
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
void * palloc(Size size)
Definition: mcxt.c:950
Definition: pg_list.h:50

◆ gistinitpage()

void gistinitpage ( Page  page,
uint32  f 
)

Definition at line 756 of file gistutil.c.

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

Referenced by gist_indexsortbuild(), gist_indexsortbuild_pagestate_flush(), and GISTInitBuffer().

757 {
758  GISTPageOpaque opaque;
759  Size pageSize = BLCKSZ;
760 
761  PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
762 
763  opaque = GistPageGetOpaque(page);
764  /* page was already zeroed by PageInit, so this is not needed: */
765  /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
766  opaque->rightlink = InvalidBlockNumber;
767  opaque->flags = f;
768  opaque->gist_page_id = GIST_PAGE_ID;
769 }
uint16 gist_page_id
Definition: gist.h:74
uint16 flags
Definition: gist.h:73
#define GistPageGetOpaque(page)
Definition: gist.h:159
size_t Size
Definition: c.h:528
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber rightlink
Definition: gist.h:72
#define GIST_PAGE_ID
Definition: gist.h:103
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ gistinsert()

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

Definition at line 156 of file gist.c.

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

Referenced by gisthandler().

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

◆ gistjoinvector()

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

Definition at line 113 of file gistutil.c.

References repalloc().

Referenced by gistplacetopage().

114 {
115  itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen));
116  memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
117  *len += addlen;
118  return itvec;
119 }
IndexTupleData * IndexTuple
Definition: itup.h:53
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1070

◆ gistKeyIsEQ()

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

Definition at line 280 of file gistutil.c.

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

Referenced by gistgetadjusted(), and gistUserPicksplit().

281 {
282  bool result;
283 
284  FunctionCall3Coll(&giststate->equalFn[attno],
285  giststate->supportCollation[attno],
286  a, b,
287  PointerGetDatum(&result));
288  return result;
289 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1173

◆ gistMakeUnionItVec()

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

Definition at line 154 of file gistutil.c.

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

Referenced by gistunion(), and gistunionsubkeyvec().

156 {
157  int i;
158  GistEntryVector *evec;
159  int attrsize;
160 
161  evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
162 
163  for (i = 0; i < giststate->nonLeafTupdesc->natts; i++)
164  {
165  int j;
166 
167  /* Collect non-null datums for this column */
168  evec->n = 0;
169  for (j = 0; j < len; j++)
170  {
171  Datum datum;
172  bool IsNull;
173 
174  datum = index_getattr(itvec[j], i + 1, giststate->leafTupdesc,
175  &IsNull);
176  if (IsNull)
177  continue;
178 
179  gistdentryinit(giststate, i,
180  evec->vector + evec->n,
181  datum,
182  NULL, NULL, (OffsetNumber) 0,
183  false, IsNull);
184  evec->n++;
185  }
186 
187  /* If this column was all NULLs, the union is NULL */
188  if (evec->n == 0)
189  {
190  attr[i] = (Datum) 0;
191  isnull[i] = true;
192  }
193  else
194  {
195  if (evec->n == 1)
196  {
197  /* unionFn may expect at least two inputs */
198  evec->n = 2;
199  evec->vector[1] = evec->vector[0];
200  }
201 
202  /* Make union and store in attr array */
203  attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
204  giststate->supportCollation[i],
205  PointerGetDatum(evec),
206  PointerGetDatum(&attrsize));
207 
208  isnull[i] = false;
209  }
210  }
211 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
int32 n
Definition: gist.h:227
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1151
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:228
#define GEVHDRSZ
Definition: gist.h:231
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:546
struct GISTENTRY GISTENTRY
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
void * palloc(Size size)
Definition: mcxt.c:950
int i
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87

◆ gistMakeUnionKey()

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

Definition at line 232 of file gistutil.c.

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

Referenced by gistgetadjusted(), and supportSecondarySplit().

236 {
237  /* we need a GistEntryVector with room for exactly 2 elements */
238  union
239  {
240  GistEntryVector gev;
241  char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
242  } storage;
243  GistEntryVector *evec = &storage.gev;
244  int dstsize;
245 
246  evec->n = 2;
247 
248  if (isnull1 && isnull2)
249  {
250  *dstisnull = true;
251  *dst = (Datum) 0;
252  }
253  else
254  {
255  if (isnull1 == false && isnull2 == false)
256  {
257  evec->vector[0] = *entry1;
258  evec->vector[1] = *entry2;
259  }
260  else if (isnull1 == false)
261  {
262  evec->vector[0] = *entry1;
263  evec->vector[1] = *entry1;
264  }
265  else
266  {
267  evec->vector[0] = *entry2;
268  evec->vector[1] = *entry2;
269  }
270 
271  *dstisnull = false;
272  *dst = FunctionCall2Coll(&giststate->unionFn[attno],
273  giststate->supportCollation[attno],
274  PointerGetDatum(evec),
275  PointerGetDatum(&dstsize));
276  }
277 }
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:556
int32 n
Definition: gist.h:227
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1151
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:228
#define GEVHDRSZ
Definition: gist.h:231
struct GISTENTRY GISTENTRY
uintptr_t Datum
Definition: postgres.h:367
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87

◆ gistNewBuffer()

Buffer gistNewBuffer ( Relation  r)

Definition at line 826 of file gistutil.c.

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

Referenced by gistbuild(), and gistplacetopage().

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

◆ gistnospace()

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

Definition at line 58 of file gistutil.c.

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

Referenced by gistplacetopage().

59 {
60  unsigned int size = freespace,
61  deleted = 0;
62  int i;
63 
64  for (i = 0; i < len; i++)
65  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
66 
67  if (todelete != InvalidOffsetNumber)
68  {
69  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
70 
71  deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
72  }
73 
74  return (PageGetFreeSpace(page) + deleted < size);
75 }
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:790
IndexTupleData * IndexTuple
Definition: itup.h:53
struct ItemIdData ItemIdData
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
#define InvalidOffsetNumber
Definition: off.h:26
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ gistoptions()

bytea* gistoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 923 of file gistutil.c.

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

Referenced by gisthandler().

924 {
925  static const relopt_parse_elt tab[] = {
926  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
927  {"buffering", RELOPT_TYPE_ENUM, offsetof(GiSTOptions, buffering_mode)}
928  };
929 
930  return (bytea *) build_reloptions(reloptions, validate,
932  sizeof(GiSTOptions),
933  tab, lengthof(tab));
934 }
#define lengthof(array)
Definition: c.h:722
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:1887
int fillfactor
Definition: pgbench.c:160
Definition: c.h:609
#define offsetof(type, field)
Definition: c.h:715

◆ gistPageRecyclable()

bool gistPageRecyclable ( Page  page)

Definition at line 899 of file gistutil.c.

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

Referenced by gistNewBuffer(), and gistvacuumpage().

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

◆ gistpenalty()

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

Definition at line 723 of file gistutil.c.

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

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

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

◆ gistplacetopage()

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

Definition at line 222 of file gist.c.

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

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

◆ gistPopItupFromNodeBuffer()

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

Definition at line 410 of file gistbuildbuffers.c.

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

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 }
uint32 BlockNumber
Definition: block.h:31
static void gistBuffersReleaseBlock(GISTBuildBuffers *gfbb, long blocknum)
static void gistLoadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
void pfree(void *pointer)
Definition: mcxt.c:1057
static void gistGetItupFromPage(GISTNodeBufferPage *pageBuffer, IndexTuple *item)
BlockNumber prev
Definition: gist_private.h:48
#define Assert(condition)
Definition: c.h:792
#define InvalidBlockNumber
Definition: block.h:33
#define PAGE_IS_EMPTY(nbp)
Definition: gist_private.h:57
static void ReadTempFileBlock(BufFile *file, long blknum, void *ptr)

◆ gistproperty()

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

Definition at line 944 of file gistutil.c.

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

Referenced by gisthandler().

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

◆ gistPushItupToNodeBuffer()

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

Definition at line 340 of file gistbuildbuffers.c.

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

Referenced by gistProcessItup(), and gistRelocateBuildBuffersOnSplit().

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 PAGE_FREE_SPACE(nbp)
Definition: gist_private.h:55
static void gistPlaceItupToPage(GISTNodeBufferPage *pageBuffer, IndexTuple item)
static GISTNodeBufferPage * gistAllocateNewPageBuffer(GISTBuildBuffers *gfbb)
MemoryContext context
Definition: gist_private.h:341
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define BUFFER_HALF_FILLED(nodeBuffer, gfbb)
Definition: gist_private.h:324
uint32 BlockNumber
Definition: block.h:31
static void gistLoadNodeBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
GISTNodeBufferPage * pageBuffer
Definition: gist_private.h:304
static long gistBuffersGetFreeBlock(GISTBuildBuffers *gfbb)
List * bufferEmptyingQueue
Definition: gist_private.h:357
#define PAGE_NO_SPACE(nbp, itup)
Definition: gist_private.h:59
bool queuedForEmptying
Definition: gist_private.h:307
static void WriteTempFileBlock(BufFile *file, long blknum, void *ptr)
List * lcons(void *datum, List *list)
Definition: list.c:453
BlockNumber prev
Definition: gist_private.h:48
#define MAXALIGN(LEN)
Definition: c.h:745
static void gistAddLoadedBuffer(GISTBuildBuffers *gfbb, GISTNodeBuffer *nodeBuffer)
#define offsetof(type, field)
Definition: c.h:715

◆ gistRelocateBuildBuffersOnSplit()

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

Definition at line 537 of file gistbuildbuffers.c.

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

Referenced by gistbufferinginserttuples().

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

◆ gistSplit()

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

Definition at line 1413 of file gist.c.

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

Referenced by gistplacetopage(), and gistSplit().

1418 {
1419  IndexTuple *lvectup,
1420  *rvectup;
1421  GistSplitVector v;
1422  int i;
1423  SplitedPageLayout *res = NULL;
1424 
1425  /* this should never recurse very deeply, but better safe than sorry */
1427 
1428  /* there's no point in splitting an empty page */
1429  Assert(len > 0);
1430 
1431  /*
1432  * If a single tuple doesn't fit on a page, no amount of splitting will
1433  * help.
1434  */
1435  if (len == 1)
1436  ereport(ERROR,
1437  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1438  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1439  IndexTupleSize(itup[0]), GiSTPageSize,
1441 
1442  memset(v.spl_lisnull, true,
1443  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1444  memset(v.spl_risnull, true,
1445  sizeof(bool) * giststate->nonLeafTupdesc->natts);
1446  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1447 
1448  /* form left and right vector */
1449  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1450  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1451 
1452  for (i = 0; i < v.splitVector.spl_nleft; i++)
1453  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1454 
1455  for (i = 0; i < v.splitVector.spl_nright; i++)
1456  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1457 
1458  /* finalize splitting (may need another split) */
1459  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1460  {
1461  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1462  }
1463  else
1464  {
1465  ROTATEDIST(res);
1466  res->block.num = v.splitVector.spl_nright;
1467  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1468  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1469  }
1470 
1471  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1472  {
1473  SplitedPageLayout *resptr,
1474  *subres;
1475 
1476  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1477 
1478  /* install on list's tail */
1479  while (resptr->next)
1480  resptr = resptr->next;
1481 
1482  resptr->next = res;
1483  res = subres;
1484  }
1485  else
1486  {
1487  ROTATEDIST(res);
1488  res->block.num = v.splitVector.spl_nleft;
1489  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1490  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1491  }
1492 
1493  return res;
1494 }
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:239
OffsetNumber * spl_left
Definition: gist.h:134
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int errcode(int sqlerrcode)
Definition: elog.c:704
int spl_nleft
Definition: gist.h:135
IndexTupleData * list
Definition: gist_private.h:194
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:243
gistxlogPage block
Definition: gist_private.h:193
#define ERROR
Definition: elog.h:45
int spl_nright
Definition: gist.h:140
void check_stack_depth(void)
Definition: postgres.c:3377
#define RelationGetRelationName(relation)
Definition: rel.h:491
struct SplitedPageLayout * next
Definition: gist_private.h:200
#define GiSTPageSize
Definition: gist_private.h:475
#define ROTATEDIST(d)
Definition: gist.c:45
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1413
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define ereport(elevel,...)
Definition: elog.h:155
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:78
#define Assert(condition)
Definition: c.h:792
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum *attdata, bool *isnull, bool isleaf)
Definition: gistutil.c:574
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:126
OffsetNumber * spl_right
Definition: gist.h:139
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
void * palloc(Size size)
Definition: mcxt.c:950
int errmsg(const char *fmt,...)
Definition: elog.c:915
int i
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
#define IndexTupleSize(itup)
Definition: itup.h:71

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

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

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
663  gistSplitHalf(&v->splitVector, len);
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 
677  v->splitVector.spl_left = (OffsetNumber *) palloc(len * sizeof(OffsetNumber));
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 }
OffsetNumber * spl_left
Definition: gist.h:134
GIST_SPLITVEC splitVector
Definition: gist_private.h:237
int32 n
Definition: gist.h:227
int spl_nleft
Definition: gist.h:135
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
uint16 OffsetNumber
Definition: off.h:24
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:228
#define GEVHDRSZ
Definition: gist.h:231
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:546
int spl_nright
Definition: gist.h:140
TupleDesc leafTupdesc
Definition: gist_private.h:80
uintptr_t Datum
Definition: postgres.h:367
TupleDesc nonLeafTupdesc
Definition: gist_private.h:81
#define Assert(condition)
Definition: c.h:792
static void gistSplitHalf(GIST_SPLITVEC *v, int len)
Definition: gistsplit.c:585
OffsetNumber * spl_right
Definition: gist.h:139
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:245
void * palloc(Size size)
Definition: mcxt.c:950
int i
static void gistunionsubkey(GISTSTATE *giststate, IndexTuple *itvec, GistSplitVector *spl)
Definition: gistsplit.c:80
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:241
static bool gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVector *v, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gistsplit.c:415

◆ gistunion()

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

Definition at line 218 of file gistutil.c.

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

Referenced by gist_indexsortbuild_pagestate_flush().

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

◆ gistUnloadNodeBuffers()

void gistUnloadNodeBuffers ( GISTBuildBuffers gfbb)

Definition at line 276 of file gistbuildbuffers.c.

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

Referenced by gistProcessEmptyingQueue().

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)
GISTNodeBuffer ** loadedBuffers
Definition: gist_private.h:375
int i

◆ gistvacuumcleanup()

IndexBulkDeleteResult* gistvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 75 of file gistvacuum.c.

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

Referenced by gisthandler().

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 }
bool analyze_only
Definition: genam.h:47
void * palloc0(Size size)
Definition: mcxt.c:981
double num_heap_tuples
Definition: genam.h:51
double num_index_tuples
Definition: genam.h:77
bool estimated_count
Definition: genam.h:49
static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:125

◆ gistvalidate()

bool gistvalidate ( Oid  opclassoid)

Definition at line 34 of file gistvalidate.c.

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

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&