PostgreSQL Source Code  git master
gist.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * gist.c
4  * interface routines for the postgres GiST index access method.
5  *
6  *
7  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/access/gist/gist.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/gist_private.h"
18 #include "access/gistscan.h"
19 #include "catalog/pg_collation.h"
20 #include "miscadmin.h"
21 #include "storage/lmgr.h"
22 #include "storage/predicate.h"
23 #include "nodes/execnodes.h"
24 #include "utils/builtins.h"
25 #include "utils/index_selfuncs.h"
26 #include "utils/memutils.h"
27 #include "utils/rel.h"
28 
29 
30 /* non-export function prototypes */
31 static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate);
33  GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum);
35  GISTSTATE *giststate,
36  IndexTuple *tuples, int ntup, OffsetNumber oldoffnum,
38  bool unlockbuf, bool unlockleftchild);
40  GISTSTATE *giststate, List *splitinfo, bool releasebuf);
41 static void gistvacuumpage(Relation rel, Page page, Buffer buffer);
42 
43 
44 #define ROTATEDIST(d) do { \
45  SplitedPageLayout *tmp=(SplitedPageLayout*)palloc(sizeof(SplitedPageLayout)); \
46  memset(tmp,0,sizeof(SplitedPageLayout)); \
47  tmp->block.blkno = InvalidBlockNumber; \
48  tmp->buffer = InvalidBuffer; \
49  tmp->next = (d); \
50  (d)=tmp; \
51 } while(0)
52 
53 
54 /*
55  * GiST handler function: return IndexAmRoutine with access method parameters
56  * and callbacks.
57  */
58 Datum
60 {
62 
63  amroutine->amstrategies = 0;
64  amroutine->amsupport = GISTNProcs;
65  amroutine->amcanorder = false;
66  amroutine->amcanorderbyop = true;
67  amroutine->amcanbackward = false;
68  amroutine->amcanunique = false;
69  amroutine->amcanmulticol = true;
70  amroutine->amoptionalkey = true;
71  amroutine->amsearcharray = false;
72  amroutine->amsearchnulls = true;
73  amroutine->amstorage = true;
74  amroutine->amclusterable = true;
75  amroutine->ampredlocks = true;
76  amroutine->amcanparallel = false;
77  amroutine->amcaninclude = false;
78  amroutine->amkeytype = InvalidOid;
79 
80  amroutine->ambuild = gistbuild;
81  amroutine->ambuildempty = gistbuildempty;
82  amroutine->aminsert = gistinsert;
83  amroutine->ambulkdelete = gistbulkdelete;
85  amroutine->amcanreturn = gistcanreturn;
86  amroutine->amcostestimate = gistcostestimate;
87  amroutine->amoptions = gistoptions;
88  amroutine->amproperty = gistproperty;
89  amroutine->amvalidate = gistvalidate;
90  amroutine->ambeginscan = gistbeginscan;
91  amroutine->amrescan = gistrescan;
92  amroutine->amgettuple = gistgettuple;
93  amroutine->amgetbitmap = gistgetbitmap;
94  amroutine->amendscan = gistendscan;
95  amroutine->ammarkpos = NULL;
96  amroutine->amrestrpos = NULL;
97  amroutine->amestimateparallelscan = NULL;
98  amroutine->aminitparallelscan = NULL;
99  amroutine->amparallelrescan = NULL;
100 
101  PG_RETURN_POINTER(amroutine);
102 }
103 
104 /*
105  * Create and return a temporary memory context for use by GiST. We
106  * _always_ invoke user-provided methods in a temporary memory
107  * context, so that memory leaks in those functions cannot cause
108  * problems. Also, we use some additional temporary contexts in the
109  * GiST code itself, to avoid the need to do some awkward manual
110  * memory management.
111  */
114 {
116  "GiST temporary context",
118 }
119 
120 /*
121  * gistbuildempty() -- build an empty gist index in the initialization fork
122  */
123 void
125 {
126  Buffer buffer;
127 
128  /* Initialize the root page */
129  buffer = ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
131 
132  /* Initialize and xlog buffer */
134  GISTInitBuffer(buffer, F_LEAF);
135  MarkBufferDirty(buffer);
136  log_newpage_buffer(buffer, true);
138 
139  /* Unlock and release the buffer */
140  UnlockReleaseBuffer(buffer);
141 }
142 
143 /*
144  * gistinsert -- wrapper for GiST tuple insertion.
145  *
146  * This is the public interface routine for tuple insertion in GiSTs.
147  * It doesn't do any work; just locks the relation and passes the buck.
148  */
149 bool
150 gistinsert(Relation r, Datum *values, bool *isnull,
151  ItemPointer ht_ctid, Relation heapRel,
152  IndexUniqueCheck checkUnique,
153  IndexInfo *indexInfo)
154 {
155  GISTSTATE *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
156  IndexTuple itup;
157  MemoryContext oldCxt;
158 
159  /* Initialize GISTSTATE cache if first call in this statement */
160  if (giststate == NULL)
161  {
162  oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
163  giststate = initGISTstate(r);
164  giststate->tempCxt = createTempGistContext();
165  indexInfo->ii_AmCache = (void *) giststate;
166  MemoryContextSwitchTo(oldCxt);
167  }
168 
169  oldCxt = MemoryContextSwitchTo(giststate->tempCxt);
170 
171  itup = gistFormTuple(giststate, r,
172  values, isnull, true /* size is currently bogus */ );
173  itup->t_tid = *ht_ctid;
174 
175  gistdoinsert(r, itup, 0, giststate);
176 
177  /* cleanup */
178  MemoryContextSwitchTo(oldCxt);
179  MemoryContextReset(giststate->tempCxt);
180 
181  return false;
182 }
183 
184 
185 /*
186  * Place tuples from 'itup' to 'buffer'. If 'oldoffnum' is valid, the tuple
187  * at that offset is atomically removed along with inserting the new tuples.
188  * This is used to replace a tuple with a new one.
189  *
190  * If 'leftchildbuf' is valid, we're inserting the downlink for the page
191  * to the right of 'leftchildbuf', or updating the downlink for 'leftchildbuf'.
192  * F_FOLLOW_RIGHT flag on 'leftchildbuf' is cleared and NSN is set.
193  *
194  * If 'markfollowright' is true and the page is split, the left child is
195  * marked with F_FOLLOW_RIGHT flag. That is the normal case. During buffered
196  * index build, however, there is no concurrent access and the page splitting
197  * is done in a slightly simpler fashion, and false is passed.
198  *
199  * If there is not enough room on the page, it is split. All the split
200  * pages are kept pinned and locked and returned in *splitinfo, the caller
201  * is responsible for inserting the downlinks for them. However, if
202  * 'buffer' is the root page and it needs to be split, gistplacetopage()
203  * performs the split as one atomic operation, and *splitinfo is set to NIL.
204  * In that case, we continue to hold the root page locked, and the child
205  * pages are released; note that new tuple(s) are *not* on the root page
206  * but in one of the new child pages.
207  *
208  * If 'newblkno' is not NULL, returns the block number of page the first
209  * new/updated tuple was inserted to. Usually it's the given page, but could
210  * be its right sibling if the page was split.
211  *
212  * Returns 'true' if the page was split, 'false' otherwise.
213  */
214 bool
215 gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
216  Buffer buffer,
217  IndexTuple *itup, int ntup, OffsetNumber oldoffnum,
218  BlockNumber *newblkno,
219  Buffer leftchildbuf,
220  List **splitinfo,
221  bool markfollowright)
222 {
224  Page page = BufferGetPage(buffer);
225  bool is_leaf = (GistPageIsLeaf(page)) ? true : false;
226  XLogRecPtr recptr;
227  int i;
228  bool is_split;
229 
230  /*
231  * Refuse to modify a page that's incompletely split. This should not
232  * happen because we finish any incomplete splits while we walk down the
233  * tree. However, it's remotely possible that another concurrent inserter
234  * splits a parent page, and errors out before completing the split. We
235  * will just throw an error in that case, and leave any split we had in
236  * progress unfinished too. The next insert that comes along will clean up
237  * the mess.
238  */
239  if (GistFollowRight(page))
240  elog(ERROR, "concurrent GiST page split was incomplete");
241 
242  *splitinfo = NIL;
243 
244  /*
245  * if isupdate, remove old key: This node's key has been modified, either
246  * because a child split occurred or because we needed to adjust our key
247  * for an insert in a child node. Therefore, remove the old version of
248  * this node's key.
249  *
250  * for WAL replay, in the non-split case we handle this by setting up a
251  * one-element todelete array; in the split case, it's handled implicitly
252  * because the tuple vector passed to gistSplit won't include this tuple.
253  */
254  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
255 
256  /*
257  * If leaf page is full, try at first to delete dead tuples. And then
258  * check again.
259  */
260  if (is_split && GistPageIsLeaf(page) && GistPageHasGarbage(page))
261  {
262  gistvacuumpage(rel, page, buffer);
263  is_split = gistnospace(page, itup, ntup, oldoffnum, freespace);
264  }
265 
266  if (is_split)
267  {
268  /* no space for insertion */
269  IndexTuple *itvec;
270  int tlen;
271  SplitedPageLayout *dist = NULL,
272  *ptr;
273  BlockNumber oldrlink = InvalidBlockNumber;
274  GistNSN oldnsn = 0;
275  SplitedPageLayout rootpg;
276  bool is_rootsplit;
277  int npage;
278 
279  is_rootsplit = (blkno == GIST_ROOT_BLKNO);
280 
281  /*
282  * Form index tuples vector to split. If we're replacing an old tuple,
283  * remove the old version from the vector.
284  */
285  itvec = gistextractpage(page, &tlen);
286  if (OffsetNumberIsValid(oldoffnum))
287  {
288  /* on inner page we should remove old tuple */
289  int pos = oldoffnum - FirstOffsetNumber;
290 
291  tlen--;
292  if (pos != tlen)
293  memmove(itvec + pos, itvec + pos + 1, sizeof(IndexTuple) * (tlen - pos));
294  }
295  itvec = gistjoinvector(itvec, &tlen, itup, ntup);
296  dist = gistSplit(rel, page, itvec, tlen, giststate);
297 
298  /*
299  * Check that split didn't produce too many pages.
300  */
301  npage = 0;
302  for (ptr = dist; ptr; ptr = ptr->next)
303  npage++;
304  /* in a root split, we'll add one more page to the list below */
305  if (is_rootsplit)
306  npage++;
307  if (npage > GIST_MAX_SPLIT_PAGES)
308  elog(ERROR, "GiST page split into too many halves (%d, maximum %d)",
309  npage, GIST_MAX_SPLIT_PAGES);
310 
311  /*
312  * Set up pages to work with. Allocate new buffers for all but the
313  * leftmost page. The original page becomes the new leftmost page, and
314  * is just replaced with the new contents.
315  *
316  * For a root-split, allocate new buffers for all child pages, the
317  * original page is overwritten with new root page containing
318  * downlinks to the new child pages.
319  */
320  ptr = dist;
321  if (!is_rootsplit)
322  {
323  /* save old rightlink and NSN */
324  oldrlink = GistPageGetOpaque(page)->rightlink;
325  oldnsn = GistPageGetNSN(page);
326 
327  dist->buffer = buffer;
328  dist->block.blkno = BufferGetBlockNumber(buffer);
330 
331  /* clean all flags except F_LEAF */
332  GistPageGetOpaque(dist->page)->flags = (is_leaf) ? F_LEAF : 0;
333 
334  ptr = ptr->next;
335  }
336  for (; ptr; ptr = ptr->next)
337  {
338  /* Allocate new page */
339  ptr->buffer = gistNewBuffer(rel);
340  GISTInitBuffer(ptr->buffer, (is_leaf) ? F_LEAF : 0);
341  ptr->page = BufferGetPage(ptr->buffer);
342  ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
344  BufferGetBlockNumber(buffer),
345  BufferGetBlockNumber(ptr->buffer));
346  }
347 
348  /*
349  * Now that we know which blocks the new pages go to, set up downlink
350  * tuples to point to them.
351  */
352  for (ptr = dist; ptr; ptr = ptr->next)
353  {
354  ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
355  GistTupleSetValid(ptr->itup);
356  }
357 
358  /*
359  * If this is a root split, we construct the new root page with the
360  * downlinks here directly, instead of requiring the caller to insert
361  * them. Add the new root page to the list along with the child pages.
362  */
363  if (is_rootsplit)
364  {
365  IndexTuple *downlinks;
366  int ndownlinks = 0;
367  int i;
368 
369  rootpg.buffer = buffer;
371  GistPageGetOpaque(rootpg.page)->flags = 0;
372 
373  /* Prepare a vector of all the downlinks */
374  for (ptr = dist; ptr; ptr = ptr->next)
375  ndownlinks++;
376  downlinks = palloc(sizeof(IndexTuple) * ndownlinks);
377  for (i = 0, ptr = dist; ptr; ptr = ptr->next)
378  downlinks[i++] = ptr->itup;
379 
380  rootpg.block.blkno = GIST_ROOT_BLKNO;
381  rootpg.block.num = ndownlinks;
382  rootpg.list = gistfillitupvec(downlinks, ndownlinks,
383  &(rootpg.lenlist));
384  rootpg.itup = NULL;
385 
386  rootpg.next = dist;
387  dist = &rootpg;
388  }
389  else
390  {
391  /* Prepare split-info to be returned to caller */
392  for (ptr = dist; ptr; ptr = ptr->next)
393  {
395 
396  si->buf = ptr->buffer;
397  si->downlink = ptr->itup;
398  *splitinfo = lappend(*splitinfo, si);
399  }
400  }
401 
402  /*
403  * Fill all pages. All the pages are new, ie. freshly allocated empty
404  * pages, or a temporary copy of the old page.
405  */
406  for (ptr = dist; ptr; ptr = ptr->next)
407  {
408  char *data = (char *) (ptr->list);
409 
410  for (i = 0; i < ptr->block.num; i++)
411  {
412  IndexTuple thistup = (IndexTuple) data;
413 
414  if (PageAddItem(ptr->page, (Item) data, IndexTupleSize(thistup), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
415  elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(rel));
416 
417  /*
418  * If this is the first inserted/updated tuple, let the caller
419  * know which page it landed on.
420  */
421  if (newblkno && ItemPointerEquals(&thistup->t_tid, &(*itup)->t_tid))
422  *newblkno = ptr->block.blkno;
423 
424  data += IndexTupleSize(thistup);
425  }
426 
427  /* Set up rightlinks */
428  if (ptr->next && ptr->block.blkno != GIST_ROOT_BLKNO)
429  GistPageGetOpaque(ptr->page)->rightlink =
430  ptr->next->block.blkno;
431  else
432  GistPageGetOpaque(ptr->page)->rightlink = oldrlink;
433 
434  /*
435  * Mark the all but the right-most page with the follow-right
436  * flag. It will be cleared as soon as the downlink is inserted
437  * into the parent, but this ensures that if we error out before
438  * that, the index is still consistent. (in buffering build mode,
439  * any error will abort the index build anyway, so this is not
440  * needed.)
441  */
442  if (ptr->next && !is_rootsplit && markfollowright)
443  GistMarkFollowRight(ptr->page);
444  else
445  GistClearFollowRight(ptr->page);
446 
447  /*
448  * Copy the NSN of the original page to all pages. The
449  * F_FOLLOW_RIGHT flags ensure that scans will follow the
450  * rightlinks until the downlinks are inserted.
451  */
452  GistPageSetNSN(ptr->page, oldnsn);
453  }
454 
455  /*
456  * gistXLogSplit() needs to WAL log a lot of pages, prepare WAL
457  * insertion for that. NB: The number of pages and data segments
458  * specified here must match the calculations in gistXLogSplit()!
459  */
460  if (RelationNeedsWAL(rel))
461  XLogEnsureRecordSpace(npage, 1 + npage * 2);
462 
464 
465  /*
466  * Must mark buffers dirty before XLogInsert, even though we'll still
467  * be changing their opaque fields below.
468  */
469  for (ptr = dist; ptr; ptr = ptr->next)
470  MarkBufferDirty(ptr->buffer);
471  if (BufferIsValid(leftchildbuf))
472  MarkBufferDirty(leftchildbuf);
473 
474  /*
475  * The first page in the chain was a temporary working copy meant to
476  * replace the old page. Copy it over the old page.
477  */
479  dist->page = BufferGetPage(dist->buffer);
480 
481  /* Write the WAL record */
482  if (RelationNeedsWAL(rel))
483  recptr = gistXLogSplit(is_leaf,
484  dist, oldrlink, oldnsn, leftchildbuf,
485  markfollowright);
486  else
487  recptr = gistGetFakeLSN(rel);
488 
489  for (ptr = dist; ptr; ptr = ptr->next)
490  {
491  PageSetLSN(ptr->page, recptr);
492  }
493 
494  /*
495  * Return the new child buffers to the caller.
496  *
497  * If this was a root split, we've already inserted the downlink
498  * pointers, in the form of a new root page. Therefore we can release
499  * all the new buffers, and keep just the root page locked.
500  */
501  if (is_rootsplit)
502  {
503  for (ptr = dist->next; ptr; ptr = ptr->next)
504  UnlockReleaseBuffer(ptr->buffer);
505  }
506  }
507  else
508  {
509  /*
510  * Enough space. We always get here if ntup==0.
511  */
513 
514  /*
515  * Delete old tuple if any, then insert new tuple(s) if any. If
516  * possible, use the fast path of PageIndexTupleOverwrite.
517  */
518  if (OffsetNumberIsValid(oldoffnum))
519  {
520  if (ntup == 1)
521  {
522  /* One-for-one replacement, so use PageIndexTupleOverwrite */
523  if (!PageIndexTupleOverwrite(page, oldoffnum, (Item) *itup,
524  IndexTupleSize(*itup)))
525  elog(ERROR, "failed to add item to index page in \"%s\"",
527  }
528  else
529  {
530  /* Delete old, then append new tuple(s) to page */
531  PageIndexTupleDelete(page, oldoffnum);
532  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
533  }
534  }
535  else
536  {
537  /* Just append new tuples at the end of the page */
538  gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
539  }
540 
541  MarkBufferDirty(buffer);
542 
543  if (BufferIsValid(leftchildbuf))
544  MarkBufferDirty(leftchildbuf);
545 
546  if (RelationNeedsWAL(rel))
547  {
548  OffsetNumber ndeloffs = 0,
549  deloffs[1];
550 
551  if (OffsetNumberIsValid(oldoffnum))
552  {
553  deloffs[0] = oldoffnum;
554  ndeloffs = 1;
555  }
556 
557  recptr = gistXLogUpdate(buffer,
558  deloffs, ndeloffs, itup, ntup,
559  leftchildbuf);
560 
561  PageSetLSN(page, recptr);
562  }
563  else
564  {
565  recptr = gistGetFakeLSN(rel);
566  PageSetLSN(page, recptr);
567  }
568 
569  if (newblkno)
570  *newblkno = blkno;
571  }
572 
573  /*
574  * If we inserted the downlink for a child page, set NSN and clear
575  * F_FOLLOW_RIGHT flag on the left child, so that concurrent scans know to
576  * follow the rightlink if and only if they looked at the parent page
577  * before we inserted the downlink.
578  *
579  * Note that we do this *after* writing the WAL record. That means that
580  * the possible full page image in the WAL record does not include these
581  * changes, and they must be replayed even if the page is restored from
582  * the full page image. There's a chicken-and-egg problem: if we updated
583  * the child pages first, we wouldn't know the recptr of the WAL record
584  * we're about to write.
585  */
586  if (BufferIsValid(leftchildbuf))
587  {
588  Page leftpg = BufferGetPage(leftchildbuf);
589 
590  GistPageSetNSN(leftpg, recptr);
591  GistClearFollowRight(leftpg);
592 
593  PageSetLSN(leftpg, recptr);
594  }
595 
597 
598  return is_split;
599 }
600 
601 /*
602  * Workhouse routine for doing insertion into a GiST index. Note that
603  * this routine assumes it is invoked in a short-lived memory context,
604  * so it does not bother releasing palloc'd allocations.
605  */
606 void
607 gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
608 {
609  ItemId iid;
610  IndexTuple idxtuple;
611  GISTInsertStack firststack;
612  GISTInsertStack *stack;
614  bool xlocked = false;
615 
616  memset(&state, 0, sizeof(GISTInsertState));
617  state.freespace = freespace;
618  state.r = r;
619 
620  /* Start from the root */
621  firststack.blkno = GIST_ROOT_BLKNO;
622  firststack.lsn = 0;
623  firststack.parent = NULL;
624  firststack.downlinkoffnum = InvalidOffsetNumber;
625  state.stack = stack = &firststack;
626 
627  /*
628  * Walk down along the path of smallest penalty, updating the parent
629  * pointers with the key we're inserting as we go. If we crash in the
630  * middle, the tree is consistent, although the possible parent updates
631  * were a waste.
632  */
633  for (;;)
634  {
635  if (XLogRecPtrIsInvalid(stack->lsn))
636  stack->buffer = ReadBuffer(state.r, stack->blkno);
637 
638  /*
639  * Be optimistic and grab shared lock first. Swap it for an exclusive
640  * lock later if we need to update the page.
641  */
642  if (!xlocked)
643  {
644  LockBuffer(stack->buffer, GIST_SHARE);
645  gistcheckpage(state.r, stack->buffer);
646  }
647 
648  stack->page = (Page) BufferGetPage(stack->buffer);
649  stack->lsn = xlocked ?
650  PageGetLSN(stack->page) : BufferGetLSNAtomic(stack->buffer);
651  Assert(!RelationNeedsWAL(state.r) || !XLogRecPtrIsInvalid(stack->lsn));
652 
653  /*
654  * If this page was split but the downlink was never inserted to the
655  * parent because the inserting backend crashed before doing that, fix
656  * that now.
657  */
658  if (GistFollowRight(stack->page))
659  {
660  if (!xlocked)
661  {
662  LockBuffer(stack->buffer, GIST_UNLOCK);
664  xlocked = true;
665  /* someone might've completed the split when we unlocked */
666  if (!GistFollowRight(stack->page))
667  continue;
668  }
669  gistfixsplit(&state, giststate);
670 
671  UnlockReleaseBuffer(stack->buffer);
672  xlocked = false;
673  state.stack = stack = stack->parent;
674  continue;
675  }
676 
677  if (stack->blkno != GIST_ROOT_BLKNO &&
678  stack->parent->lsn < GistPageGetNSN(stack->page))
679  {
680  /*
681  * Concurrent split detected. There's no guarantee that the
682  * downlink for this page is consistent with the tuple we're
683  * inserting anymore, so go back to parent and rechoose the best
684  * child.
685  */
686  UnlockReleaseBuffer(stack->buffer);
687  xlocked = false;
688  state.stack = stack = stack->parent;
689  continue;
690  }
691 
692  if (!GistPageIsLeaf(stack->page))
693  {
694  /*
695  * This is an internal page so continue to walk down the tree.
696  * Find the child node that has the minimum insertion penalty.
697  */
698  BlockNumber childblkno;
699  IndexTuple newtup;
700  GISTInsertStack *item;
701  OffsetNumber downlinkoffnum;
702 
703  downlinkoffnum = gistchoose(state.r, stack->page, itup, giststate);
704  iid = PageGetItemId(stack->page, downlinkoffnum);
705  idxtuple = (IndexTuple) PageGetItem(stack->page, iid);
706  childblkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
707 
708  /*
709  * Check that it's not a leftover invalid tuple from pre-9.1
710  */
711  if (GistTupleIsInvalid(idxtuple))
712  ereport(ERROR,
713  (errmsg("index \"%s\" contains an inner tuple marked as invalid",
715  errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
716  errhint("Please REINDEX it.")));
717 
718  /*
719  * Check that the key representing the target child node is
720  * consistent with the key we're inserting. Update it if it's not.
721  */
722  newtup = gistgetadjusted(state.r, idxtuple, itup, giststate);
723  if (newtup)
724  {
725  /*
726  * Swap shared lock for an exclusive one. Beware, the page may
727  * change while we unlock/lock the page...
728  */
729  if (!xlocked)
730  {
731  LockBuffer(stack->buffer, GIST_UNLOCK);
733  xlocked = true;
734  stack->page = (Page) BufferGetPage(stack->buffer);
735 
736  if (PageGetLSN(stack->page) != stack->lsn)
737  {
738  /* the page was changed while we unlocked it, retry */
739  continue;
740  }
741  }
742 
743  /*
744  * Update the tuple.
745  *
746  * We still hold the lock after gistinserttuple(), but it
747  * might have to split the page to make the updated tuple fit.
748  * In that case the updated tuple might migrate to the other
749  * half of the split, so we have to go back to the parent and
750  * descend back to the half that's a better fit for the new
751  * tuple.
752  */
753  if (gistinserttuple(&state, stack, giststate, newtup,
754  downlinkoffnum))
755  {
756  /*
757  * If this was a root split, the root page continues to be
758  * the parent and the updated tuple went to one of the
759  * child pages, so we just need to retry from the root
760  * page.
761  */
762  if (stack->blkno != GIST_ROOT_BLKNO)
763  {
764  UnlockReleaseBuffer(stack->buffer);
765  xlocked = false;
766  state.stack = stack = stack->parent;
767  }
768  continue;
769  }
770  }
771  LockBuffer(stack->buffer, GIST_UNLOCK);
772  xlocked = false;
773 
774  /* descend to the chosen child */
775  item = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
776  item->blkno = childblkno;
777  item->parent = stack;
778  item->downlinkoffnum = downlinkoffnum;
779  state.stack = stack = item;
780  }
781  else
782  {
783  /*
784  * Leaf page. Insert the new key. We've already updated all the
785  * parents on the way down, but we might have to split the page if
786  * it doesn't fit. gistinserthere() will take care of that.
787  */
788 
789  /*
790  * Swap shared lock for an exclusive one. Be careful, the page may
791  * change while we unlock/lock the page...
792  */
793  if (!xlocked)
794  {
795  LockBuffer(stack->buffer, GIST_UNLOCK);
797  xlocked = true;
798  stack->page = (Page) BufferGetPage(stack->buffer);
799  stack->lsn = PageGetLSN(stack->page);
800 
801  if (stack->blkno == GIST_ROOT_BLKNO)
802  {
803  /*
804  * the only page that can become inner instead of leaf is
805  * the root page, so for root we should recheck it
806  */
807  if (!GistPageIsLeaf(stack->page))
808  {
809  /*
810  * very rare situation: during unlock/lock index with
811  * number of pages = 1 was increased
812  */
813  LockBuffer(stack->buffer, GIST_UNLOCK);
814  xlocked = false;
815  continue;
816  }
817 
818  /*
819  * we don't need to check root split, because checking
820  * leaf/inner is enough to recognize split for root
821  */
822  }
823  else if (GistFollowRight(stack->page) ||
824  stack->parent->lsn < GistPageGetNSN(stack->page))
825  {
826  /*
827  * The page was split while we momentarily unlocked the
828  * page. Go back to parent.
829  */
830  UnlockReleaseBuffer(stack->buffer);
831  xlocked = false;
832  state.stack = stack = stack->parent;
833  continue;
834  }
835  }
836 
837  /* now state.stack->(page, buffer and blkno) points to leaf page */
838 
839  gistinserttuple(&state, stack, giststate, itup,
841  LockBuffer(stack->buffer, GIST_UNLOCK);
842 
843  /* Release any pins we might still hold before exiting */
844  for (; stack; stack = stack->parent)
845  ReleaseBuffer(stack->buffer);
846  break;
847  }
848  }
849 }
850 
851 /*
852  * Traverse the tree to find path from root page to specified "child" block.
853  *
854  * returns a new insertion stack, starting from the parent of "child", up
855  * to the root. *downlinkoffnum is set to the offset of the downlink in the
856  * direct parent of child.
857  *
858  * To prevent deadlocks, this should lock only one page at a time.
859  */
860 static GISTInsertStack *
862 {
863  Page page;
864  Buffer buffer;
865  OffsetNumber i,
866  maxoff;
867  ItemId iid;
868  IndexTuple idxtuple;
869  List *fifo;
870  GISTInsertStack *top,
871  *ptr;
873 
874  top = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
875  top->blkno = GIST_ROOT_BLKNO;
877 
878  fifo = list_make1(top);
879  while (fifo != NIL)
880  {
881  /* Get next page to visit */
882  top = linitial(fifo);
883  fifo = list_delete_first(fifo);
884 
885  buffer = ReadBuffer(r, top->blkno);
886  LockBuffer(buffer, GIST_SHARE);
887  gistcheckpage(r, buffer);
888  page = (Page) BufferGetPage(buffer);
889 
890  if (GistPageIsLeaf(page))
891  {
892  /*
893  * Because we scan the index top-down, all the rest of the pages
894  * in the queue must be leaf pages as well.
895  */
896  UnlockReleaseBuffer(buffer);
897  break;
898  }
899 
900  top->lsn = BufferGetLSNAtomic(buffer);
901 
902  /*
903  * If F_FOLLOW_RIGHT is set, the page to the right doesn't have a
904  * downlink. This should not normally happen..
905  */
906  if (GistFollowRight(page))
907  elog(ERROR, "concurrent GiST page split was incomplete");
908 
909  if (top->parent && top->parent->lsn < GistPageGetNSN(page) &&
910  GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ )
911  {
912  /*
913  * Page was split while we looked elsewhere. We didn't see the
914  * downlink to the right page when we scanned the parent, so add
915  * it to the queue now.
916  *
917  * Put the right page ahead of the queue, so that we visit it
918  * next. That's important, because if this is the lowest internal
919  * level, just above leaves, we might already have queued up some
920  * leaf pages, and we assume that there can't be any non-leaf
921  * pages behind leaf pages.
922  */
923  ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
924  ptr->blkno = GistPageGetOpaque(page)->rightlink;
926  ptr->parent = top->parent;
927 
928  fifo = lcons(ptr, fifo);
929  }
930 
931  maxoff = PageGetMaxOffsetNumber(page);
932 
933  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
934  {
935  iid = PageGetItemId(page, i);
936  idxtuple = (IndexTuple) PageGetItem(page, iid);
937  blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
938  if (blkno == child)
939  {
940  /* Found it! */
941  UnlockReleaseBuffer(buffer);
942  *downlinkoffnum = i;
943  return top;
944  }
945  else
946  {
947  /* Append this child to the list of pages to visit later */
948  ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
949  ptr->blkno = blkno;
950  ptr->downlinkoffnum = i;
951  ptr->parent = top;
952 
953  fifo = lappend(fifo, ptr);
954  }
955  }
956 
957  UnlockReleaseBuffer(buffer);
958  }
959 
960  elog(ERROR, "failed to re-find parent of a page in index \"%s\", block %u",
961  RelationGetRelationName(r), child);
962  return NULL; /* keep compiler quiet */
963 }
964 
965 /*
966  * Updates the stack so that child->parent is the correct parent of the
967  * child. child->parent must be exclusively locked on entry, and will
968  * remain so at exit, but it might not be the same page anymore.
969  */
970 static void
972 {
973  GISTInsertStack *parent = child->parent;
974 
975  gistcheckpage(r, parent->buffer);
976  parent->page = (Page) BufferGetPage(parent->buffer);
977 
978  /* here we don't need to distinguish between split and page update */
979  if (child->downlinkoffnum == InvalidOffsetNumber ||
980  parent->lsn != PageGetLSN(parent->page))
981  {
982  /* parent is changed, look child in right links until found */
983  OffsetNumber i,
984  maxoff;
985  ItemId iid;
986  IndexTuple idxtuple;
987  GISTInsertStack *ptr;
988 
989  while (true)
990  {
991  maxoff = PageGetMaxOffsetNumber(parent->page);
992  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
993  {
994  iid = PageGetItemId(parent->page, i);
995  idxtuple = (IndexTuple) PageGetItem(parent->page, iid);
996  if (ItemPointerGetBlockNumber(&(idxtuple->t_tid)) == child->blkno)
997  {
998  /* yes!!, found */
999  child->downlinkoffnum = i;
1000  return;
1001  }
1002  }
1003 
1004  parent->blkno = GistPageGetOpaque(parent->page)->rightlink;
1005  UnlockReleaseBuffer(parent->buffer);
1006  if (parent->blkno == InvalidBlockNumber)
1007  {
1008  /*
1009  * End of chain and still didn't find parent. It's a very-very
1010  * rare situation when root splited.
1011  */
1012  break;
1013  }
1014  parent->buffer = ReadBuffer(r, parent->blkno);
1015  LockBuffer(parent->buffer, GIST_EXCLUSIVE);
1016  gistcheckpage(r, parent->buffer);
1017  parent->page = (Page) BufferGetPage(parent->buffer);
1018  }
1019 
1020  /*
1021  * awful!!, we need search tree to find parent ... , but before we
1022  * should release all old parent
1023  */
1024 
1025  ptr = child->parent->parent; /* child->parent already released
1026  * above */
1027  while (ptr)
1028  {
1029  ReleaseBuffer(ptr->buffer);
1030  ptr = ptr->parent;
1031  }
1032 
1033  /* ok, find new path */
1034  ptr = parent = gistFindPath(r, child->blkno, &child->downlinkoffnum);
1035 
1036  /* read all buffers as expected by caller */
1037  /* note we don't lock them or gistcheckpage them here! */
1038  while (ptr)
1039  {
1040  ptr->buffer = ReadBuffer(r, ptr->blkno);
1041  ptr->page = (Page) BufferGetPage(ptr->buffer);
1042  ptr = ptr->parent;
1043  }
1044 
1045  /* install new chain of parents to stack */
1046  child->parent = parent;
1047 
1048  /* make recursive call to normal processing */
1050  gistFindCorrectParent(r, child);
1051  }
1052 
1053  return;
1054 }
1055 
1056 /*
1057  * Form a downlink pointer for the page in 'buf'.
1058  */
1059 static IndexTuple
1061  GISTInsertStack *stack)
1062 {
1063  Page page = BufferGetPage(buf);
1064  OffsetNumber maxoff;
1065  OffsetNumber offset;
1066  IndexTuple downlink = NULL;
1067 
1068  maxoff = PageGetMaxOffsetNumber(page);
1069  for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
1070  {
1071  IndexTuple ituple = (IndexTuple)
1072  PageGetItem(page, PageGetItemId(page, offset));
1073 
1074  if (downlink == NULL)
1075  downlink = CopyIndexTuple(ituple);
1076  else
1077  {
1078  IndexTuple newdownlink;
1079 
1080  newdownlink = gistgetadjusted(rel, downlink, ituple,
1081  giststate);
1082  if (newdownlink)
1083  downlink = newdownlink;
1084  }
1085  }
1086 
1087  /*
1088  * If the page is completely empty, we can't form a meaningful downlink
1089  * for it. But we have to insert a downlink for the page. Any key will do,
1090  * as long as its consistent with the downlink of parent page, so that we
1091  * can legally insert it to the parent. A minimal one that matches as few
1092  * scans as possible would be best, to keep scans from doing useless work,
1093  * but we don't know how to construct that. So we just use the downlink of
1094  * the original page that was split - that's as far from optimal as it can
1095  * get but will do..
1096  */
1097  if (!downlink)
1098  {
1099  ItemId iid;
1100 
1102  gistFindCorrectParent(rel, stack);
1103  iid = PageGetItemId(stack->parent->page, stack->downlinkoffnum);
1104  downlink = (IndexTuple) PageGetItem(stack->parent->page, iid);
1105  downlink = CopyIndexTuple(downlink);
1106  LockBuffer(stack->parent->buffer, GIST_UNLOCK);
1107  }
1108 
1110  GistTupleSetValid(downlink);
1111 
1112  return downlink;
1113 }
1114 
1115 
1116 /*
1117  * Complete the incomplete split of state->stack->page.
1118  */
1119 static void
1121 {
1122  GISTInsertStack *stack = state->stack;
1123  Buffer buf;
1124  Page page;
1125  List *splitinfo = NIL;
1126 
1127  elog(LOG, "fixing incomplete split in index \"%s\", block %u",
1128  RelationGetRelationName(state->r), stack->blkno);
1129 
1130  Assert(GistFollowRight(stack->page));
1132 
1133  buf = stack->buffer;
1134 
1135  /*
1136  * Read the chain of split pages, following the rightlinks. Construct a
1137  * downlink tuple for each page.
1138  */
1139  for (;;)
1140  {
1142  IndexTuple downlink;
1143 
1144  page = BufferGetPage(buf);
1145 
1146  /* Form the new downlink tuples to insert to parent */
1147  downlink = gistformdownlink(state->r, buf, giststate, stack);
1148 
1149  si->buf = buf;
1150  si->downlink = downlink;
1151 
1152  splitinfo = lappend(splitinfo, si);
1153 
1154  if (GistFollowRight(page))
1155  {
1156  /* lock next page */
1157  buf = ReadBuffer(state->r, GistPageGetOpaque(page)->rightlink);
1158  LockBuffer(buf, GIST_EXCLUSIVE);
1159  }
1160  else
1161  break;
1162  }
1163 
1164  /* Insert the downlinks */
1165  gistfinishsplit(state, stack, giststate, splitinfo, false);
1166 }
1167 
1168 /*
1169  * Insert or replace a tuple in stack->buffer. If 'oldoffnum' is valid, the
1170  * tuple at 'oldoffnum' is replaced, otherwise the tuple is inserted as new.
1171  * 'stack' represents the path from the root to the page being updated.
1172  *
1173  * The caller must hold an exclusive lock on stack->buffer. The lock is still
1174  * held on return, but the page might not contain the inserted tuple if the
1175  * page was split. The function returns true if the page was split, false
1176  * otherwise.
1177  */
1178 static bool
1180  GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
1181 {
1182  return gistinserttuples(state, stack, giststate, &tuple, 1, oldoffnum,
1183  InvalidBuffer, InvalidBuffer, false, false);
1184 }
1185 
1186 /* ----------------
1187  * An extended workhorse version of gistinserttuple(). This version allows
1188  * inserting multiple tuples, or replacing a single tuple with multiple tuples.
1189  * This is used to recursively update the downlinks in the parent when a page
1190  * is split.
1191  *
1192  * If leftchild and rightchild are valid, we're inserting/replacing the
1193  * downlink for rightchild, and leftchild is its left sibling. We clear the
1194  * F_FOLLOW_RIGHT flag and update NSN on leftchild, atomically with the
1195  * insertion of the downlink.
1196  *
1197  * To avoid holding locks for longer than necessary, when recursing up the
1198  * tree to update the parents, the locking is a bit peculiar here. On entry,
1199  * the caller must hold an exclusive lock on stack->buffer, as well as
1200  * leftchild and rightchild if given. On return:
1201  *
1202  * - Lock on stack->buffer is released, if 'unlockbuf' is true. The page is
1203  * always kept pinned, however.
1204  * - Lock on 'leftchild' is released, if 'unlockleftchild' is true. The page
1205  * is kept pinned.
1206  * - Lock and pin on 'rightchild' are always released.
1207  *
1208  * Returns 'true' if the page had to be split. Note that if the page was
1209  * split, the inserted/updated tuples might've been inserted to a right
1210  * sibling of stack->buffer instead of stack->buffer itself.
1211  */
1212 static bool
1214  GISTSTATE *giststate,
1215  IndexTuple *tuples, int ntup, OffsetNumber oldoffnum,
1217  bool unlockbuf, bool unlockleftchild)
1218 {
1219  List *splitinfo;
1220  bool is_split;
1221 
1222  /*
1223  * Check for any rw conflicts (in serialisation isolation level)
1224  * just before we intend to modify the page
1225  */
1226  CheckForSerializableConflictIn(state->r, NULL, stack->buffer);
1227 
1228  /* Insert the tuple(s) to the page, splitting the page if necessary */
1229  is_split = gistplacetopage(state->r, state->freespace, giststate,
1230  stack->buffer,
1231  tuples, ntup,
1232  oldoffnum, NULL,
1233  leftchild,
1234  &splitinfo,
1235  true);
1236 
1237  /*
1238  * Before recursing up in case the page was split, release locks on the
1239  * child pages. We don't need to keep them locked when updating the
1240  * parent.
1241  */
1242  if (BufferIsValid(rightchild))
1243  UnlockReleaseBuffer(rightchild);
1244  if (BufferIsValid(leftchild) && unlockleftchild)
1245  LockBuffer(leftchild, GIST_UNLOCK);
1246 
1247  /*
1248  * If we had to split, insert/update the downlinks in the parent. If the
1249  * caller requested us to release the lock on stack->buffer, tell
1250  * gistfinishsplit() to do that as soon as it's safe to do so. If we
1251  * didn't have to split, release it ourselves.
1252  */
1253  if (splitinfo)
1254  gistfinishsplit(state, stack, giststate, splitinfo, unlockbuf);
1255  else if (unlockbuf)
1256  LockBuffer(stack->buffer, GIST_UNLOCK);
1257 
1258  return is_split;
1259 }
1260 
1261 /*
1262  * Finish an incomplete split by inserting/updating the downlinks in parent
1263  * page. 'splitinfo' contains all the child pages involved in the split,
1264  * from left-to-right.
1265  *
1266  * On entry, the caller must hold a lock on stack->buffer and all the child
1267  * pages in 'splitinfo'. If 'unlockbuf' is true, the lock on stack->buffer is
1268  * released on return. The child pages are always unlocked and unpinned.
1269  */
1270 static void
1272  GISTSTATE *giststate, List *splitinfo, bool unlockbuf)
1273 {
1274  ListCell *lc;
1275  List *reversed;
1276  GISTPageSplitInfo *right;
1277  GISTPageSplitInfo *left;
1278  IndexTuple tuples[2];
1279 
1280  /* A split always contains at least two halves */
1281  Assert(list_length(splitinfo) >= 2);
1282 
1283  /*
1284  * We need to insert downlinks for each new page, and update the downlink
1285  * for the original (leftmost) page in the split. Begin at the rightmost
1286  * page, inserting one downlink at a time until there's only two pages
1287  * left. Finally insert the downlink for the last new page and update the
1288  * downlink for the original page as one operation.
1289  */
1290 
1291  /* for convenience, create a copy of the list in reverse order */
1292  reversed = NIL;
1293  foreach(lc, splitinfo)
1294  {
1295  reversed = lcons(lfirst(lc), reversed);
1296  }
1297 
1299  gistFindCorrectParent(state->r, stack);
1300 
1301  /*
1302  * insert downlinks for the siblings from right to left, until there are
1303  * only two siblings left.
1304  */
1305  while (list_length(reversed) > 2)
1306  {
1307  right = (GISTPageSplitInfo *) linitial(reversed);
1308  left = (GISTPageSplitInfo *) lsecond(reversed);
1309 
1310  if (gistinserttuples(state, stack->parent, giststate,
1311  &right->downlink, 1,
1313  left->buf, right->buf, false, false))
1314  {
1315  /*
1316  * If the parent page was split, need to relocate the original
1317  * parent pointer.
1318  */
1319  gistFindCorrectParent(state->r, stack);
1320  }
1321  /* gistinserttuples() released the lock on right->buf. */
1322  reversed = list_delete_first(reversed);
1323  }
1324 
1325  right = (GISTPageSplitInfo *) linitial(reversed);
1326  left = (GISTPageSplitInfo *) lsecond(reversed);
1327 
1328  /*
1329  * Finally insert downlink for the remaining right page and update the
1330  * downlink for the original page to not contain the tuples that were
1331  * moved to the new pages.
1332  */
1333  tuples[0] = left->downlink;
1334  tuples[1] = right->downlink;
1335  gistinserttuples(state, stack->parent, giststate,
1336  tuples, 2,
1337  stack->downlinkoffnum,
1338  left->buf, right->buf,
1339  true, /* Unlock parent */
1340  unlockbuf /* Unlock stack->buffer if caller wants that */
1341  );
1342  Assert(left->buf == stack->buffer);
1343 }
1344 
1345 /*
1346  * gistSplit -- split a page in the tree and fill struct
1347  * used for XLOG and real writes buffers. Function is recursive, ie
1348  * it will split page until keys will fit in every page.
1349  */
1352  Page page,
1353  IndexTuple *itup, /* contains compressed entry */
1354  int len,
1355  GISTSTATE *giststate)
1356 {
1357  IndexTuple *lvectup,
1358  *rvectup;
1359  GistSplitVector v;
1360  int i;
1361  SplitedPageLayout *res = NULL;
1362 
1363  /* this should never recurse very deeply, but better safe than sorry */
1365 
1366  /* there's no point in splitting an empty page */
1367  Assert(len > 0);
1368 
1369  /*
1370  * If a single tuple doesn't fit on a page, no amount of splitting will
1371  * help.
1372  */
1373  if (len == 1)
1374  ereport(ERROR,
1375  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1376  errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
1377  IndexTupleSize(itup[0]), GiSTPageSize,
1379 
1380  memset(v.spl_lisnull, true, sizeof(bool) * giststate->tupdesc->natts);
1381  memset(v.spl_risnull, true, sizeof(bool) * giststate->tupdesc->natts);
1382  gistSplitByKey(r, page, itup, len, giststate, &v, 0);
1383 
1384  /* form left and right vector */
1385  lvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1386  rvectup = (IndexTuple *) palloc(sizeof(IndexTuple) * (len + 1));
1387 
1388  for (i = 0; i < v.splitVector.spl_nleft; i++)
1389  lvectup[i] = itup[v.splitVector.spl_left[i] - 1];
1390 
1391  for (i = 0; i < v.splitVector.spl_nright; i++)
1392  rvectup[i] = itup[v.splitVector.spl_right[i] - 1];
1393 
1394  /* finalize splitting (may need another split) */
1395  if (!gistfitpage(rvectup, v.splitVector.spl_nright))
1396  {
1397  res = gistSplit(r, page, rvectup, v.splitVector.spl_nright, giststate);
1398  }
1399  else
1400  {
1401  ROTATEDIST(res);
1402  res->block.num = v.splitVector.spl_nright;
1403  res->list = gistfillitupvec(rvectup, v.splitVector.spl_nright, &(res->lenlist));
1404  res->itup = gistFormTuple(giststate, r, v.spl_rattr, v.spl_risnull, false);
1405  }
1406 
1407  if (!gistfitpage(lvectup, v.splitVector.spl_nleft))
1408  {
1409  SplitedPageLayout *resptr,
1410  *subres;
1411 
1412  resptr = subres = gistSplit(r, page, lvectup, v.splitVector.spl_nleft, giststate);
1413 
1414  /* install on list's tail */
1415  while (resptr->next)
1416  resptr = resptr->next;
1417 
1418  resptr->next = res;
1419  res = subres;
1420  }
1421  else
1422  {
1423  ROTATEDIST(res);
1424  res->block.num = v.splitVector.spl_nleft;
1425  res->list = gistfillitupvec(lvectup, v.splitVector.spl_nleft, &(res->lenlist));
1426  res->itup = gistFormTuple(giststate, r, v.spl_lattr, v.spl_lisnull, false);
1427  }
1428 
1429  return res;
1430 }
1431 
1432 /*
1433  * Create a GISTSTATE and fill it with information about the index
1434  */
1435 GISTSTATE *
1437 {
1438  GISTSTATE *giststate;
1439  MemoryContext scanCxt;
1440  MemoryContext oldCxt;
1441  int i;
1442 
1443  /* safety check to protect fixed-size arrays in GISTSTATE */
1444  if (index->rd_att->natts > INDEX_MAX_KEYS)
1445  elog(ERROR, "numberOfAttributes %d > %d",
1446  index->rd_att->natts, INDEX_MAX_KEYS);
1447 
1448  /* Create the memory context that will hold the GISTSTATE */
1450  "GiST scan context",
1452  oldCxt = MemoryContextSwitchTo(scanCxt);
1453 
1454  /* Create and fill in the GISTSTATE */
1455  giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
1456 
1457  giststate->scanCxt = scanCxt;
1458  giststate->tempCxt = scanCxt; /* caller must change this if needed */
1459  giststate->tupdesc = index->rd_att;
1460 
1461  for (i = 0; i < index->rd_att->natts; i++)
1462  {
1463  fmgr_info_copy(&(giststate->consistentFn[i]),
1464  index_getprocinfo(index, i + 1, GIST_CONSISTENT_PROC),
1465  scanCxt);
1466  fmgr_info_copy(&(giststate->unionFn[i]),
1467  index_getprocinfo(index, i + 1, GIST_UNION_PROC),
1468  scanCxt);
1469 
1470  /* opclasses are not required to provide a Compress method */
1471  if (OidIsValid(index_getprocid(index, i + 1, GIST_COMPRESS_PROC)))
1472  fmgr_info_copy(&(giststate->compressFn[i]),
1473  index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
1474  scanCxt);
1475  else
1476  giststate->compressFn[i].fn_oid = InvalidOid;
1477 
1478  /* opclasses are not required to provide a Decompress method */
1479  if (OidIsValid(index_getprocid(index, i + 1, GIST_DECOMPRESS_PROC)))
1480  fmgr_info_copy(&(giststate->decompressFn[i]),
1481  index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
1482  scanCxt);
1483  else
1484  giststate->decompressFn[i].fn_oid = InvalidOid;
1485 
1486  fmgr_info_copy(&(giststate->penaltyFn[i]),
1487  index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
1488  scanCxt);
1489  fmgr_info_copy(&(giststate->picksplitFn[i]),
1490  index_getprocinfo(index, i + 1, GIST_PICKSPLIT_PROC),
1491  scanCxt);
1492  fmgr_info_copy(&(giststate->equalFn[i]),
1493  index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
1494  scanCxt);
1495 
1496  /* opclasses are not required to provide a Distance method */
1497  if (OidIsValid(index_getprocid(index, i + 1, GIST_DISTANCE_PROC)))
1498  fmgr_info_copy(&(giststate->distanceFn[i]),
1499  index_getprocinfo(index, i + 1, GIST_DISTANCE_PROC),
1500  scanCxt);
1501  else
1502  giststate->distanceFn[i].fn_oid = InvalidOid;
1503 
1504  /* opclasses are not required to provide a Fetch method */
1505  if (OidIsValid(index_getprocid(index, i + 1, GIST_FETCH_PROC)))
1506  fmgr_info_copy(&(giststate->fetchFn[i]),
1507  index_getprocinfo(index, i + 1, GIST_FETCH_PROC),
1508  scanCxt);
1509  else
1510  giststate->fetchFn[i].fn_oid = InvalidOid;
1511 
1512  /*
1513  * If the index column has a specified collation, we should honor that
1514  * while doing comparisons. However, we may have a collatable storage
1515  * type for a noncollatable indexed data type. If there's no index
1516  * collation then specify default collation in case the support
1517  * functions need collation. This is harmless if the support
1518  * functions don't care about collation, so we just do it
1519  * unconditionally. (We could alternatively call get_typcollation,
1520  * but that seems like expensive overkill --- there aren't going to be
1521  * any cases where a GiST storage type has a nondefault collation.)
1522  */
1523  if (OidIsValid(index->rd_indcollation[i]))
1524  giststate->supportCollation[i] = index->rd_indcollation[i];
1525  else
1526  giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
1527  }
1528 
1529  MemoryContextSwitchTo(oldCxt);
1530 
1531  return giststate;
1532 }
1533 
1534 void
1536 {
1537  /* It's sufficient to delete the scanCxt */
1538  MemoryContextDelete(giststate->scanCxt);
1539 }
1540 
1541 /*
1542  * gistvacuumpage() -- try to remove LP_DEAD items from the given page.
1543  * Function assumes that buffer is exclusively locked.
1544  */
1545 static void
1547 {
1549  int ndeletable = 0;
1550  OffsetNumber offnum,
1551  maxoff;
1552 
1553  Assert(GistPageIsLeaf(page));
1554 
1555  /*
1556  * Scan over all items to see which ones need to be deleted according to
1557  * LP_DEAD flags.
1558  */
1559  maxoff = PageGetMaxOffsetNumber(page);
1560  for (offnum = FirstOffsetNumber;
1561  offnum <= maxoff;
1562  offnum = OffsetNumberNext(offnum))
1563  {
1564  ItemId itemId = PageGetItemId(page, offnum);
1565 
1566  if (ItemIdIsDead(itemId))
1567  deletable[ndeletable++] = offnum;
1568  }
1569 
1570  if (ndeletable > 0)
1571  {
1573 
1574  PageIndexMultiDelete(page, deletable, ndeletable);
1575 
1576  /*
1577  * Mark the page as not containing any LP_DEAD items. This is not
1578  * certainly true (there might be some that have recently been marked,
1579  * but weren't included in our target-item list), but it will almost
1580  * always be true and it doesn't seem worth an additional page scan to
1581  * check it. Remember that F_HAS_GARBAGE is only a hint anyway.
1582  */
1584 
1585  MarkBufferDirty(buffer);
1586 
1587  /* XLOG stuff */
1588  if (RelationNeedsWAL(rel))
1589  {
1590  XLogRecPtr recptr;
1591 
1592  recptr = gistXLogUpdate(buffer,
1593  deletable, ndeletable,
1594  NULL, 0, InvalidBuffer);
1595 
1596  PageSetLSN(page, recptr);
1597  }
1598  else
1599  PageSetLSN(page, gistGetFakeLSN(rel));
1600 
1601  END_CRIT_SECTION();
1602  }
1603 
1604  /*
1605  * Note: if we didn't find any LP_DEAD items, then the page's
1606  * F_HAS_GARBAGE hint bit is falsely set. We do not bother expending a
1607  * separate write to clear it, however. We will clear it when we split
1608  * the page.
1609  */
1610 }
#define GistFollowRight(page)
Definition: gist.h:147
ambeginscan_function ambeginscan
Definition: amapi.h:217
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:326
#define NIL
Definition: pg_list.h:69
TupleDesc tupdesc
Definition: gist_private.h:81
BlockNumber blkno
Definition: gist_private.h:206
#define GistPageGetNSN(page)
Definition: gist.h:151
MemoryContext ii_Context
Definition: execnodes.h:171
ambulkdelete_function ambulkdelete
Definition: amapi.h:210
bool amcanmulticol
Definition: amapi.h:180
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple tuple, OffsetNumber oldoffnum)
Definition: gist.c:1179
uint16 amsupport
Definition: amapi.h:170
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
int errhint(const char *fmt,...)
Definition: elog.c:987
#define GIST_FETCH_PROC
Definition: gist.h:36
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1009
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:855
Datum spl_lattr[INDEX_MAX_KEYS]
Definition: gist_private.h:228
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:407
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
Definition: gist.c:1120
amgettuple_function amgettuple
Definition: amapi.h:219
BlockNumber blkno
Definition: gist_private.h:182
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:93
IndexBulkDeleteResult * gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: gistvacuum.c:29
bool amcanorderbyop
Definition: amapi.h:174
#define GistClearPageHasGarbage(page)
Definition: gist.h:145
amproperty_function amproperty
Definition: amapi.h:215
#define GIST_EQUAL_PROC
Definition: gist.h:34
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:723
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
MemoryContext createTempGistContext(void)
Definition: gist.c:113
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:96
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: gistvacuum.c:138
Datum gisthandler(PG_FUNCTION_ARGS)
Definition: gist.c:59
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:91
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
ItemPointerData t_tid
Definition: itup.h:37
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:33
amparallelrescan_function amparallelrescan
Definition: amapi.h:228
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
bool amstorage
Definition: amapi.h:188
bool gistgettuple(IndexScanDesc scan, ScanDirection dir)
Definition: gistget.c:630
OffsetNumber * spl_left
Definition: gist.h:105
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Pointer Item
Definition: item.h:17
#define GistPageSetNSN(page, val)
Definition: gist.h:152
#define InvalidBuffer
Definition: buf.h:25
GIST_SPLITVEC splitVector
Definition: gist_private.h:226
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
int errcode(int sqlerrcode)
Definition: elog.c:575
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
bool ampredlocks
Definition: amapi.h:192
#define GistTupleIsInvalid(itup)
Definition: gist_private.h:275
#define GIST_UNLOCK
Definition: gist_private.h:45
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
#define GistPageHasGarbage(page)
Definition: gist.h:143
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define P_NEW
Definition: bufmgr.h:82
aminsert_function aminsert
Definition: amapi.h:209
void gistdoinsert(Relation r, IndexTuple itup, Size freespace, GISTSTATE *giststate)
Definition: gist.c:607
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:89
int spl_nleft
Definition: gist.h:106
#define LOG
Definition: elog.h:26
bytea * gistoptions(Datum reloptions, bool validate)
Definition: gistutil.c:836
IndexTupleData * list
Definition: gist_private.h:190
#define ItemIdIsDead(itemId)
Definition: itemid.h:112
Oid amkeytype
Definition: amapi.h:198
#define OidIsValid(objectId)
Definition: c.h:605
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
static void gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, List *splitinfo, bool releasebuf)
Definition: gist.c:1271
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:314
void CheckForSerializableConflictIn(Relation relation, HeapTuple tuple, Buffer buffer)
Definition: predicate.c:4280
bool amoptionalkey
Definition: amapi.h:182
void gistSplitByKey(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate, GistSplitVector *v, int attno)
Definition: gistsplit.c:623
amvalidate_function amvalidate
Definition: amapi.h:216
Datum spl_rattr[INDEX_MAX_KEYS]
Definition: gist_private.h:232
int natts
Definition: tupdesc.h:82
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:980
#define lsecond(l)
Definition: pg_list.h:116
bool gistinsert(Relation r, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, IndexInfo *indexInfo)
Definition: gist.c:150
uint16 OffsetNumber
Definition: off.h:24
Definition: type.h:89
Page PageGetTempPageCopySpecial(Page page)
Definition: bufpage.c:385
gistxlogPage block
Definition: gist_private.h:189
#define list_make1(x1)
Definition: pg_list.h:139
IndexBuildResult * gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: gistbuild.c:114
IndexUniqueCheck
Definition: genam.h:111
XLogRecPtr gistXLogSplit(bool page_is_leaf, SplitedPageLayout *dist, BlockNumber origrlink, GistNSN orignsn, Buffer leftchildbuf, bool markfollowright)
Definition: gistxlog.c:393
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:94
amgetbitmap_function amgetbitmap
Definition: amapi.h:220
FmgrInfo consistentFn[INDEX_MAX_KEYS]
Definition: gist_private.h:85
#define linitial(l)
Definition: pg_list.h:111
#define GIST_PICKSPLIT_PROC
Definition: gist.h:33
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
Oid * rd_indcollation
Definition: rel.h:165
#define ERROR
Definition: elog.h:43
static IndexTuple gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate, GISTInsertStack *stack)
Definition: gist.c:1060
ambuild_function ambuild
Definition: amapi.h:207
#define GIST_COMPRESS_PROC
Definition: gist.h:30
#define GIST_MAX_SPLIT_PAGES
Definition: gist_private.h:40
amoptions_function amoptions
Definition: amapi.h:214
int spl_nright
Definition: gist.h:111
bool amcaninclude
Definition: amapi.h:196
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt)
Definition: fmgr.c:609
MemoryContext tempCxt
Definition: gist_private.h:79
BlockNumber blkno
Definition: ginvacuum.c:119
amcostestimate_function amcostestimate
Definition: amapi.h:213
IndexTuple downlink
Definition: gist_private.h:398
bool amcanunique
Definition: amapi.h:178
void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys, ScanKey orderbys, int norderbys)
Definition: gistscan.c:111
bool gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, Buffer buffer, IndexTuple *itup, int ntup, OffsetNumber oldoffnum, BlockNumber *newblkno, Buffer leftchildbuf, List **splitinfo, bool markfollowright)
Definition: gist.c:215
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1062
void gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7283
GISTInsertStack * stack
Definition: gist_private.h:245
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:441
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:2832
static char * buf
Definition: pg_test_fsync.c:67
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:211
amendscan_function amendscan
Definition: amapi.h:221
#define memmove(d, s, c)
Definition: c.h:1100
bool amcanbackward
Definition: amapi.h:176
void check_stack_depth(void)
Definition: postgres.c:3155
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
int errdetail(const char *fmt,...)
Definition: elog.c:873
FmgrInfo picksplitFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:58
#define RelationGetRelationName(relation)
Definition: rel.h:441
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
struct SplitedPageLayout * next
Definition: gist_private.h:196
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
void freeGISTstate(GISTSTATE *giststate)
Definition: gist.c:1535
#define ereport(elevel, rest)
Definition: elog.h:122
amrescan_function amrescan
Definition: amapi.h:218
bool amcanparallel
Definition: amapi.h:194
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
static void gistvacuumpage(Relation rel, Page page, Buffer buffer)
Definition: gist.c:1546
int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: gistget.c:761
List * lappend(List *list, void *datum)
Definition: list.c:128
void * ii_AmCache
Definition: execnodes.h:170
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:372
void gistbuildempty(Relation index)
Definition: gist.c:124
#define GistPageIsLeaf(page)
Definition: gist.h:132
#define GistTupleSetValid(itup)
Definition: gist_private.h:276
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
OffsetNumber downlinkoffnum
Definition: gist_private.h:217
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
static void gistFindCorrectParent(Relation r, GISTInsertStack *child)
Definition: gist.c:971
bool amsearchnulls
Definition: amapi.h:186
#define GiSTPageSize
Definition: gist_private.h:432
#define GistClearFollowRight(page)
Definition: gist.h:149
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:113
static bool gistinserttuples(GISTInsertState *state, GISTInsertStack *stack, GISTSTATE *giststate, IndexTuple *tuples, int ntup, OffsetNumber oldoffnum, Buffer leftchild, Buffer rightchild, bool unlockbuf, bool unlockleftchild)
Definition: gist.c:1213
void * palloc0(Size size)
Definition: mcxt.c:955
uintptr_t Datum
Definition: postgres.h:365
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define ROTATEDIST(d)
Definition: gist.c:44
bool amclusterable
Definition: amapi.h:190
TupleDesc rd_att
Definition: rel.h:85
GISTSTATE * initGISTstate(Relation index)
Definition: gist.c:1436
struct DataPageDeleteStack * child
Definition: ginvacuum.c:116
bool amsearcharray
Definition: amapi.h:184
SplitedPageLayout * gistSplit(Relation r, Page page, IndexTuple *itup, int len, GISTSTATE *giststate)
Definition: gist.c:1351
#define rightchild(x)
Definition: fsmpage.c:30
#define InvalidOffsetNumber
Definition: off.h:26
static GISTInsertStack * gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
Definition: gist.c:861
#define InvalidOid
Definition: postgres_ext.h:36
XLogRecPtr gistXLogUpdate(Buffer buffer, OffsetNumber *todelete, int ntodelete, IndexTuple *itup, int ituplen, Buffer leftchildbuf)
Definition: gistxlog.c:457
Oid fn_oid
Definition: fmgr.h:59
#define GIST_CONSISTENT_PROC
Definition: gist.h:28
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:78
#define GIST_UNION_PROC
Definition: gist.h:29
bool gistcanreturn(Relation index, int attno)
Definition: gistget.c:810
#define GistPageGetOpaque(page)
Definition: gist.h:130
List * lcons(void *datum, List *list)
Definition: list.c:259
#define makeNode(_type_)
Definition: nodes.h:565
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Definition: regguts.h:298
struct DataPageDeleteStack * parent
Definition: ginvacuum.c:117
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:832
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:743
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define INDEX_MAX_KEYS
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
size_t Size
Definition: c.h:433
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:126
#define GIST_PENALTY_PROC
Definition: gist.h:32
#define InvalidBlockNumber
Definition: block.h:33
static int list_length(const List *l)
Definition: pg_list.h:89
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:146
#define leftchild(x)
Definition: fsmpage.c:29
#define GIST_DISTANCE_PROC
Definition: gist.h:35
OffsetNumber * spl_right
Definition: gist.h:110
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
#define GIST_SHARE
Definition: gist_private.h:43
ammarkpos_function ammarkpos
Definition: amapi.h:222
bool amcanorder
Definition: amapi.h:172
FmgrInfo distanceFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
#define ItemPointerSetBlockNumber(pointer, blockNumber)
Definition: itemptr.h:116
#define GistMarkFollowRight(page)
Definition: gist.h:148
#define RelationNeedsWAL(relation)
Definition: rel.h:510
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:226
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
XLogRecPtr GistNSN
Definition: gist.h:50
#define PageGetLSN(page)
Definition: bufpage.h:362
#define GISTNProcs
Definition: gist.h:37
static Datum values[MAXATTR]
Definition: bootstrap.c:164
uint16 amstrategies
Definition: amapi.h:168
bool spl_risnull[INDEX_MAX_KEYS]
Definition: gist_private.h:234
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
bool gistvalidate(Oid opclassoid)
Definition: gistvalidate.c:34
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define F_LEAF
Definition: gist.h:42
ambuildempty_function ambuildempty
Definition: amapi.h:208
int i
#define GIST_ROOT_BLKNO
Definition: gist_private.h:249
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:40
MemoryContext scanCxt
Definition: gist_private.h:78
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:721
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:572
#define PG_FUNCTION_ARGS
Definition: fmgr.h:163
#define GIST_DECOMPRESS_PROC
Definition: gist.h:31
IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys)
Definition: gistscan.c:58
void gistendscan(IndexScanDesc scan)
Definition: gistscan.c:322
#define elog
Definition: elog.h:219
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:76
bool spl_lisnull[INDEX_MAX_KEYS]
Definition: gist_private.h:230
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:86
#define GIST_EXCLUSIVE
Definition: gist_private.h:44
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
Definition: pg_list.h:45
bool gistproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: gistutil.c:871
int Buffer
Definition: buf.h:23
struct GISTInsertStack * parent
Definition: gist_private.h:220
amcanreturn_function amcanreturn
Definition: amapi.h:212
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:782
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3065
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
#define IndexTupleSize(itup)
Definition: itup.h:71
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:227
List * list_delete_first(List *list)
Definition: list.c:666
amrestrpos_function amrestrpos
Definition: amapi.h:223
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:821