PostgreSQL Source Code  git master
ginbtree.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/ginxlog.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
#include "storage/predicate.h"
#include "utils/injection_point.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for ginbtree.c:

Go to the source code of this file.

Functions

static void ginFindParents (GinBtree btree, GinBtreeStack *stack)
 
static bool ginPlaceToPage (GinBtree btree, GinBtreeStack *stack, void *insertdata, BlockNumber updateblkno, Buffer childbuf, GinStatsData *buildStats)
 
static void ginFinishSplit (GinBtree btree, GinBtreeStack *stack, bool freestack, GinStatsData *buildStats)
 
static void ginFinishOldSplit (GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats, int access)
 
int ginTraverseLock (Buffer buffer, bool searchMode)
 
GinBtreeStackginFindLeafPage (GinBtree btree, bool searchMode, bool rootConflictCheck)
 
Buffer ginStepRight (Buffer buffer, Relation index, int lockmode)
 
void freeGinBtreeStack (GinBtreeStack *stack)
 
void ginInsertValue (GinBtree btree, GinBtreeStack *stack, void *insertdata, GinStatsData *buildStats)
 

Function Documentation

◆ freeGinBtreeStack()

void freeGinBtreeStack ( GinBtreeStack stack)

Definition at line 198 of file ginbtree.c.

199 {
200  while (stack)
201  {
202  GinBtreeStack *tmp = stack->parent;
203 
204  if (stack->buffer != InvalidBuffer)
205  ReleaseBuffer(stack->buffer);
206 
207  pfree(stack);
208  stack = tmp;
209  }
210 }
#define InvalidBuffer
Definition: buf.h:25
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4850
void pfree(void *pointer)
Definition: mcxt.c:1520
struct GinBtreeStack * parent
Definition: gin_private.h:137

References GinBtreeStack::buffer, InvalidBuffer, GinBtreeStack::parent, pfree(), and ReleaseBuffer().

Referenced by entryLoadMoreItems(), ginEntryInsert(), ginFinishSplit(), ginInsertValue(), scanPostingTree(), and startScanEntry().

◆ ginFindLeafPage()

GinBtreeStack* ginFindLeafPage ( GinBtree  btree,
bool  searchMode,
bool  rootConflictCheck 
)

Definition at line 83 of file ginbtree.c.

85 {
86  GinBtreeStack *stack;
87 
88  stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
89  stack->blkno = btree->rootBlkno;
90  stack->buffer = ReadBuffer(btree->index, btree->rootBlkno);
91  stack->parent = NULL;
92  stack->predictNumber = 1;
93 
94  if (rootConflictCheck)
95  CheckForSerializableConflictIn(btree->index, NULL, btree->rootBlkno);
96 
97  for (;;)
98  {
99  Page page;
100  BlockNumber child;
101  int access;
102 
103  stack->off = InvalidOffsetNumber;
104 
105  page = BufferGetPage(stack->buffer);
106 
107  access = ginTraverseLock(stack->buffer, searchMode);
108 
109  /*
110  * If we're going to modify the tree, finish any incomplete splits we
111  * encounter on the way.
112  */
113  if (!searchMode && GinPageIsIncompleteSplit(page))
114  ginFinishOldSplit(btree, stack, NULL, access);
115 
116  /*
117  * ok, page is correctly locked, we should check to move right ..,
118  * root never has a right link, so small optimization
119  */
120  while (btree->fullScan == false && stack->blkno != btree->rootBlkno &&
121  btree->isMoveRight(btree, page))
122  {
123  BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;
124 
125  if (rightlink == InvalidBlockNumber)
126  /* rightmost page */
127  break;
128 
129  stack->buffer = ginStepRight(stack->buffer, btree->index, access);
130  stack->blkno = rightlink;
131  page = BufferGetPage(stack->buffer);
132 
133  if (!searchMode && GinPageIsIncompleteSplit(page))
134  ginFinishOldSplit(btree, stack, NULL, access);
135  }
136 
137  if (GinPageIsLeaf(page)) /* we found, return locked page */
138  return stack;
139 
140  /* now we have correct buffer, try to find child */
141  child = btree->findChildPage(btree, stack);
142 
143  LockBuffer(stack->buffer, GIN_UNLOCK);
144  Assert(child != InvalidBlockNumber);
145  Assert(stack->blkno != child);
146 
147  if (searchMode)
148  {
149  /* in search mode we may forget path to leaf */
150  stack->blkno = child;
151  stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
152  }
153  else
154  {
155  GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
156 
157  ptr->parent = stack;
158  stack = ptr;
159  stack->blkno = child;
160  stack->buffer = ReadBuffer(btree->index, stack->blkno);
161  stack->predictNumber = 1;
162  }
163  }
164 }
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition: bufmgr.c:2537
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5085
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:745
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:408
Pointer Page
Definition: bufpage.h:78
#define Assert(condition)
Definition: c.h:858
#define GIN_UNLOCK
Definition: gin_private.h:49
#define GinPageGetOpaque(page)
Definition: ginblock.h:110
#define GinPageIsLeaf(page)
Definition: ginblock.h:112
#define GinPageIsIncompleteSplit(page)
Definition: ginblock.h:127
int ginTraverseLock(Buffer buffer, bool searchMode)
Definition: ginbtree.c:39
static void ginFinishOldSplit(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats, int access)
Definition: ginbtree.c:779
Buffer ginStepRight(Buffer buffer, Relation index, int lockmode)
Definition: ginbtree.c:177
void * palloc(Size size)
Definition: mcxt.c:1316
#define InvalidOffsetNumber
Definition: off.h:26
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4321
short access
Definition: preproc-type.c:36
BlockNumber(* findChildPage)(GinBtree, GinBtreeStack *)
Definition: gin_private.h:153
bool(* isMoveRight)(GinBtree, Page)
Definition: gin_private.h:155
Relation index
Definition: gin_private.h:167
BlockNumber rootBlkno
Definition: gin_private.h:168
OffsetNumber off
Definition: gin_private.h:133
uint32 predictNumber
Definition: gin_private.h:136
BlockNumber blkno
Definition: gin_private.h:131

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetPage(), CheckForSerializableConflictIn(), GinBtreeData::findChildPage, GinBtreeData::fullScan, GIN_UNLOCK, ginFinishOldSplit(), GinPageGetOpaque, GinPageIsIncompleteSplit, GinPageIsLeaf, ginStepRight(), ginTraverseLock(), GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, GinBtreeData::isMoveRight, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, GinBtreeStack::predictNumber, ReadBuffer(), ReleaseAndReadBuffer(), and GinBtreeData::rootBlkno.

Referenced by entryLoadMoreItems(), ginEntryInsert(), ginInsertItemPointers(), ginScanBeginPostingTree(), and startScanEntry().

◆ ginFindParents()

static void ginFindParents ( GinBtree  btree,
GinBtreeStack stack 
)
static

Definition at line 218 of file ginbtree.c.

219 {
220  Page page;
221  Buffer buffer;
222  BlockNumber blkno,
223  leftmostBlkno;
224  OffsetNumber offset;
226  GinBtreeStack *ptr;
227 
228  /*
229  * Unwind the stack all the way up to the root, leaving only the root
230  * item.
231  *
232  * Be careful not to release the pin on the root page! The pin on root
233  * page is required to lock out concurrent vacuums on the tree.
234  */
235  root = stack->parent;
236  while (root->parent)
237  {
238  ReleaseBuffer(root->buffer);
239  root = root->parent;
240  }
241 
242  Assert(root->blkno == btree->rootBlkno);
243  Assert(BufferGetBlockNumber(root->buffer) == btree->rootBlkno);
244  root->off = InvalidOffsetNumber;
245 
246  blkno = root->blkno;
247  buffer = root->buffer;
248 
249  ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
250 
251  for (;;)
252  {
253  LockBuffer(buffer, GIN_EXCLUSIVE);
254  page = BufferGetPage(buffer);
255  if (GinPageIsLeaf(page))
256  elog(ERROR, "Lost path");
257 
258  if (GinPageIsIncompleteSplit(page))
259  {
260  Assert(blkno != btree->rootBlkno);
261  ptr->blkno = blkno;
262  ptr->buffer = buffer;
263 
264  /*
265  * parent may be wrong, but if so, the ginFinishSplit call will
266  * recurse to call ginFindParents again to fix it.
267  */
268  ptr->parent = root;
269  ptr->off = InvalidOffsetNumber;
270 
271  ginFinishOldSplit(btree, ptr, NULL, GIN_EXCLUSIVE);
272  }
273 
274  leftmostBlkno = btree->getLeftMostChild(btree, page);
275 
276  while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber)
277  {
278  blkno = GinPageGetOpaque(page)->rightlink;
279  if (blkno == InvalidBlockNumber)
280  {
281  /* Link not present in this level */
282  LockBuffer(buffer, GIN_UNLOCK);
283  /* Do not release pin on the root buffer */
284  if (buffer != root->buffer)
285  ReleaseBuffer(buffer);
286  break;
287  }
288  buffer = ginStepRight(buffer, btree->index, GIN_EXCLUSIVE);
289  page = BufferGetPage(buffer);
290 
291  /* finish any incomplete splits, as above */
292  if (GinPageIsIncompleteSplit(page))
293  {
294  Assert(blkno != btree->rootBlkno);
295  ptr->blkno = blkno;
296  ptr->buffer = buffer;
297  ptr->parent = root;
298  ptr->off = InvalidOffsetNumber;
299 
300  ginFinishOldSplit(btree, ptr, NULL, GIN_EXCLUSIVE);
301  }
302  }
303 
304  if (blkno != InvalidBlockNumber)
305  {
306  ptr->blkno = blkno;
307  ptr->buffer = buffer;
308  ptr->parent = root; /* it may be wrong, but in next call we will
309  * correct */
310  ptr->off = offset;
311  stack->parent = ptr;
312  return;
313  }
314 
315  /* Descend down to next level */
316  blkno = leftmostBlkno;
317  buffer = ReadBuffer(btree->index, blkno);
318  }
319 }
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3667
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define GIN_EXCLUSIVE
Definition: gin_private.h:51
uint16 OffsetNumber
Definition: off.h:24
tree ctl root
Definition: radixtree.h:1884
BlockNumber(* getLeftMostChild)(GinBtree, Page)
Definition: gin_private.h:154
OffsetNumber(* findChildPtr)(GinBtree, Page, BlockNumber, OffsetNumber)
Definition: gin_private.h:159

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), elog, ERROR, GinBtreeData::findChildPtr, GinBtreeData::getLeftMostChild, GIN_EXCLUSIVE, GIN_UNLOCK, ginFinishOldSplit(), GinPageGetOpaque, GinPageIsIncompleteSplit, GinPageIsLeaf, ginStepRight(), GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, ReadBuffer(), ReleaseBuffer(), root, and GinBtreeData::rootBlkno.

Referenced by ginFinishSplit().

◆ ginFinishOldSplit()

static void ginFinishOldSplit ( GinBtree  btree,
GinBtreeStack stack,
GinStatsData buildStats,
int  access 
)
static

Definition at line 779 of file ginbtree.c.

780 {
781  INJECTION_POINT("gin-finish-incomplete-split");
782  elog(DEBUG1, "finishing incomplete split of block %u in gin index \"%s\"",
783  stack->blkno, RelationGetRelationName(btree->index));
784 
785  if (access == GIN_SHARE)
786  {
787  LockBuffer(stack->buffer, GIN_UNLOCK);
789 
791  {
792  /*
793  * Someone else already completed the split while we were not
794  * holding the lock.
795  */
796  return;
797  }
798  }
799 
800  ginFinishSplit(btree, stack, false, buildStats);
801 }
#define DEBUG1
Definition: elog.h:30
#define GIN_SHARE
Definition: gin_private.h:50
static void ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack, GinStatsData *buildStats)
Definition: ginbtree.c:672
#define INJECTION_POINT(name)
#define RelationGetRelationName(relation)
Definition: rel.h:539

References GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetPage(), DEBUG1, elog, GIN_EXCLUSIVE, GIN_SHARE, GIN_UNLOCK, ginFinishSplit(), GinPageIsIncompleteSplit, GinBtreeData::index, INJECTION_POINT, LockBuffer(), and RelationGetRelationName.

Referenced by ginFindLeafPage(), ginFindParents(), ginFinishSplit(), and ginInsertValue().

◆ ginFinishSplit()

static void ginFinishSplit ( GinBtree  btree,
GinBtreeStack stack,
bool  freestack,
GinStatsData buildStats 
)
static

Definition at line 672 of file ginbtree.c.

674 {
675  Page page;
676  bool done;
677  bool first = true;
678 
679  /* this loop crawls up the stack until the insertion is complete */
680  do
681  {
682  GinBtreeStack *parent = stack->parent;
683  void *insertdata;
684  BlockNumber updateblkno;
685 
686 #ifdef USE_INJECTION_POINTS
687  if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
688  INJECTION_POINT("gin-leave-leaf-split-incomplete");
689  else
690  INJECTION_POINT("gin-leave-internal-split-incomplete");
691 #endif
692 
693  /* search parent to lock */
694  LockBuffer(parent->buffer, GIN_EXCLUSIVE);
695 
696  /*
697  * If the parent page was incompletely split, finish that split first,
698  * then continue with the current one.
699  *
700  * Note: we have to finish *all* incomplete splits we encounter, even
701  * if we have to move right. Otherwise we might choose as the target a
702  * page that has no downlink in the parent, and splitting it further
703  * would fail.
704  */
706  ginFinishOldSplit(btree, parent, buildStats, GIN_EXCLUSIVE);
707 
708  /* move right if it's needed */
709  page = BufferGetPage(parent->buffer);
710  while ((parent->off = btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber)
711  {
712  if (GinPageRightMost(page))
713  {
714  /*
715  * rightmost page, but we don't find parent, we should use
716  * plain search...
717  */
718  LockBuffer(parent->buffer, GIN_UNLOCK);
719  ginFindParents(btree, stack);
720  parent = stack->parent;
721  Assert(parent != NULL);
722  break;
723  }
724 
725  parent->buffer = ginStepRight(parent->buffer, btree->index, GIN_EXCLUSIVE);
726  parent->blkno = BufferGetBlockNumber(parent->buffer);
727  page = BufferGetPage(parent->buffer);
728 
730  ginFinishOldSplit(btree, parent, buildStats, GIN_EXCLUSIVE);
731  }
732 
733  /* insert the downlink */
734  insertdata = btree->prepareDownlink(btree, stack->buffer);
735  updateblkno = GinPageGetOpaque(BufferGetPage(stack->buffer))->rightlink;
736  done = ginPlaceToPage(btree, parent,
737  insertdata, updateblkno,
738  stack->buffer, buildStats);
739  pfree(insertdata);
740 
741  /*
742  * If the caller requested to free the stack, unlock and release the
743  * child buffer now. Otherwise keep it pinned and locked, but if we
744  * have to recurse up the tree, we can unlock the upper pages, only
745  * keeping the page at the bottom of the stack locked.
746  */
747  if (!first || freestack)
748  LockBuffer(stack->buffer, GIN_UNLOCK);
749  if (freestack)
750  {
751  ReleaseBuffer(stack->buffer);
752  pfree(stack);
753  }
754  stack = parent;
755 
756  first = false;
757  } while (!done);
758 
759  /* unlock the parent */
760  LockBuffer(stack->buffer, GIN_UNLOCK);
761 
762  if (freestack)
763  freeGinBtreeStack(stack);
764 }
#define GinPageRightMost(page)
Definition: ginblock.h:129
void freeGinBtreeStack(GinBtreeStack *stack)
Definition: ginbtree.c:198
static bool ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, void *insertdata, BlockNumber updateblkno, Buffer childbuf, GinStatsData *buildStats)
Definition: ginbtree.c:337
static void ginFindParents(GinBtree btree, GinBtreeStack *stack)
Definition: ginbtree.c:218
void *(* prepareDownlink)(GinBtree, Buffer)
Definition: gin_private.h:162

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), GinBtreeData::findChildPtr, freeGinBtreeStack(), GIN_EXCLUSIVE, GIN_UNLOCK, ginFindParents(), ginFinishOldSplit(), GinPageGetOpaque, GinPageIsIncompleteSplit, GinPageIsLeaf, GinPageRightMost, ginPlaceToPage(), ginStepRight(), GinBtreeData::index, INJECTION_POINT, InvalidOffsetNumber, LockBuffer(), GinBtreeStack::off, GinBtreeStack::parent, pfree(), GinBtreeData::prepareDownlink, and ReleaseBuffer().

Referenced by ginFinishOldSplit(), and ginInsertValue().

◆ ginInsertValue()

void ginInsertValue ( GinBtree  btree,
GinBtreeStack stack,
void *  insertdata,
GinStatsData buildStats 
)

Definition at line 816 of file ginbtree.c.

818 {
819  bool done;
820 
821  /* If the leaf page was incompletely split, finish the split first */
823  ginFinishOldSplit(btree, stack, buildStats, GIN_EXCLUSIVE);
824 
825  done = ginPlaceToPage(btree, stack,
826  insertdata, InvalidBlockNumber,
827  InvalidBuffer, buildStats);
828  if (done)
829  {
830  LockBuffer(stack->buffer, GIN_UNLOCK);
831  freeGinBtreeStack(stack);
832  }
833  else
834  ginFinishSplit(btree, stack, true, buildStats);
835 }

References GinBtreeStack::buffer, BufferGetPage(), freeGinBtreeStack(), GIN_EXCLUSIVE, GIN_UNLOCK, ginFinishOldSplit(), ginFinishSplit(), GinPageIsIncompleteSplit, ginPlaceToPage(), InvalidBlockNumber, InvalidBuffer, and LockBuffer().

Referenced by ginEntryInsert(), and ginInsertItemPointers().

◆ ginPlaceToPage()

static bool ginPlaceToPage ( GinBtree  btree,
GinBtreeStack stack,
void *  insertdata,
BlockNumber  updateblkno,
Buffer  childbuf,
GinStatsData buildStats 
)
static

Definition at line 337 of file ginbtree.c.

340 {
341  Page page = BufferGetPage(stack->buffer);
342  bool result;
343  GinPlaceToPageRC rc;
344  uint16 xlflags = 0;
345  Page childpage = NULL;
346  Page newlpage = NULL,
347  newrpage = NULL;
348  void *ptp_workspace = NULL;
349  MemoryContext tmpCxt;
350  MemoryContext oldCxt;
351 
352  /*
353  * We do all the work of this function and its subfunctions in a temporary
354  * memory context. This avoids leakages and simplifies APIs, since some
355  * subfunctions allocate storage that has to survive until we've finished
356  * the WAL insertion.
357  */
359  "ginPlaceToPage temporary context",
361  oldCxt = MemoryContextSwitchTo(tmpCxt);
362 
363  if (GinPageIsData(page))
364  xlflags |= GIN_INSERT_ISDATA;
365  if (GinPageIsLeaf(page))
366  {
367  xlflags |= GIN_INSERT_ISLEAF;
368  Assert(!BufferIsValid(childbuf));
369  Assert(updateblkno == InvalidBlockNumber);
370  }
371  else
372  {
373  Assert(BufferIsValid(childbuf));
374  Assert(updateblkno != InvalidBlockNumber);
375  childpage = BufferGetPage(childbuf);
376  }
377 
378  /*
379  * See if the incoming tuple will fit on the page. beginPlaceToPage will
380  * decide if the page needs to be split, and will compute the split
381  * contents if so. See comments for beginPlaceToPage and execPlaceToPage
382  * functions for more details of the API here.
383  */
384  rc = btree->beginPlaceToPage(btree, stack->buffer, stack,
385  insertdata, updateblkno,
386  &ptp_workspace,
387  &newlpage, &newrpage);
388 
389  if (rc == GPTP_NO_WORK)
390  {
391  /* Nothing to do */
392  result = true;
393  }
394  else if (rc == GPTP_INSERT)
395  {
396  /* It will fit, perform the insertion */
398 
399  if (RelationNeedsWAL(btree->index) && !btree->isBuild)
400  XLogBeginInsert();
401 
402  /*
403  * Perform the page update, dirty and register stack->buffer, and
404  * register any extra WAL data.
405  */
406  btree->execPlaceToPage(btree, stack->buffer, stack,
407  insertdata, updateblkno, ptp_workspace);
408 
409  /* An insert to an internal page finishes the split of the child. */
410  if (BufferIsValid(childbuf))
411  {
412  GinPageGetOpaque(childpage)->flags &= ~GIN_INCOMPLETE_SPLIT;
413  MarkBufferDirty(childbuf);
414  if (RelationNeedsWAL(btree->index) && !btree->isBuild)
415  XLogRegisterBuffer(1, childbuf, REGBUF_STANDARD);
416  }
417 
418  if (RelationNeedsWAL(btree->index) && !btree->isBuild)
419  {
420  XLogRecPtr recptr;
421  ginxlogInsert xlrec;
422  BlockIdData childblknos[2];
423 
424  xlrec.flags = xlflags;
425 
426  XLogRegisterData((char *) &xlrec, sizeof(ginxlogInsert));
427 
428  /*
429  * Log information about child if this was an insertion of a
430  * downlink.
431  */
432  if (BufferIsValid(childbuf))
433  {
434  BlockIdSet(&childblknos[0], BufferGetBlockNumber(childbuf));
435  BlockIdSet(&childblknos[1], GinPageGetOpaque(childpage)->rightlink);
436  XLogRegisterData((char *) childblknos,
437  sizeof(BlockIdData) * 2);
438  }
439 
440  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT);
441  PageSetLSN(page, recptr);
442  if (BufferIsValid(childbuf))
443  PageSetLSN(childpage, recptr);
444  }
445 
447 
448  /* Insertion is complete. */
449  result = true;
450  }
451  else if (rc == GPTP_SPLIT)
452  {
453  /*
454  * Didn't fit, need to split. The split has been computed in newlpage
455  * and newrpage, which are pointers to palloc'd pages, not associated
456  * with buffers. stack->buffer is not touched yet.
457  */
458  Buffer rbuffer;
459  BlockNumber savedRightLink;
461  Buffer lbuffer = InvalidBuffer;
462  Page newrootpg = NULL;
463 
464  /* Get a new index page to become the right page */
465  rbuffer = GinNewBuffer(btree->index);
466 
467  /* During index build, count the new page */
468  if (buildStats)
469  {
470  if (btree->isData)
471  buildStats->nDataPages++;
472  else
473  buildStats->nEntryPages++;
474  }
475 
476  savedRightLink = GinPageGetOpaque(page)->rightlink;
477 
478  /* Begin setting up WAL record */
479  data.locator = btree->index->rd_locator;
480  data.flags = xlflags;
481  if (BufferIsValid(childbuf))
482  {
483  data.leftChildBlkno = BufferGetBlockNumber(childbuf);
484  data.rightChildBlkno = GinPageGetOpaque(childpage)->rightlink;
485  }
486  else
487  data.leftChildBlkno = data.rightChildBlkno = InvalidBlockNumber;
488 
489  if (stack->parent == NULL)
490  {
491  /*
492  * splitting the root, so we need to allocate new left page and
493  * place pointers to left and right page on root page.
494  */
495  lbuffer = GinNewBuffer(btree->index);
496 
497  /* During index build, count the new left page */
498  if (buildStats)
499  {
500  if (btree->isData)
501  buildStats->nDataPages++;
502  else
503  buildStats->nEntryPages++;
504  }
505 
506  data.rrlink = InvalidBlockNumber;
507  data.flags |= GIN_SPLIT_ROOT;
508 
509  GinPageGetOpaque(newrpage)->rightlink = InvalidBlockNumber;
510  GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
511 
512  /*
513  * Construct a new root page containing downlinks to the new left
514  * and right pages. (Do this in a temporary copy rather than
515  * overwriting the original page directly, since we're not in the
516  * critical section yet.)
517  */
518  newrootpg = PageGetTempPage(newrpage);
519  GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), BLCKSZ);
520 
521  btree->fillRoot(btree, newrootpg,
522  BufferGetBlockNumber(lbuffer), newlpage,
523  BufferGetBlockNumber(rbuffer), newrpage);
524 
525  if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
526  {
527 
530  BufferGetBlockNumber(lbuffer));
531 
534  BufferGetBlockNumber(rbuffer));
535  }
536  }
537  else
538  {
539  /* splitting a non-root page */
540  data.rrlink = savedRightLink;
541 
542  GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
543  GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
544  GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
545 
546  if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
547  {
548 
551  BufferGetBlockNumber(rbuffer));
552  }
553  }
554 
555  /*
556  * OK, we have the new contents of the left page in a temporary copy
557  * now (newlpage), and likewise for the new contents of the
558  * newly-allocated right block. The original page is still unchanged.
559  *
560  * If this is a root split, we also have a temporary page containing
561  * the new contents of the root.
562  */
563 
565 
566  MarkBufferDirty(rbuffer);
567  MarkBufferDirty(stack->buffer);
568 
569  /*
570  * Restore the temporary copies over the real buffers.
571  */
572  if (stack->parent == NULL)
573  {
574  /* Splitting the root, three pages to update */
575  MarkBufferDirty(lbuffer);
576  memcpy(page, newrootpg, BLCKSZ);
577  memcpy(BufferGetPage(lbuffer), newlpage, BLCKSZ);
578  memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
579  }
580  else
581  {
582  /* Normal split, only two pages to update */
583  memcpy(page, newlpage, BLCKSZ);
584  memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
585  }
586 
587  /* We also clear childbuf's INCOMPLETE_SPLIT flag, if passed */
588  if (BufferIsValid(childbuf))
589  {
590  GinPageGetOpaque(childpage)->flags &= ~GIN_INCOMPLETE_SPLIT;
591  MarkBufferDirty(childbuf);
592  }
593 
594  /* write WAL record */
595  if (RelationNeedsWAL(btree->index) && !btree->isBuild)
596  {
597  XLogRecPtr recptr;
598 
599  XLogBeginInsert();
600 
601  /*
602  * We just take full page images of all the split pages. Splits
603  * are uncommon enough that it's not worth complicating the code
604  * to be more efficient.
605  */
606  if (stack->parent == NULL)
607  {
611  }
612  else
613  {
616  }
617  if (BufferIsValid(childbuf))
618  XLogRegisterBuffer(3, childbuf, REGBUF_STANDARD);
619 
620  XLogRegisterData((char *) &data, sizeof(ginxlogSplit));
621 
622  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT);
623 
624  PageSetLSN(page, recptr);
625  PageSetLSN(BufferGetPage(rbuffer), recptr);
626  if (stack->parent == NULL)
627  PageSetLSN(BufferGetPage(lbuffer), recptr);
628  if (BufferIsValid(childbuf))
629  PageSetLSN(childpage, recptr);
630  }
632 
633  /*
634  * We can release the locks/pins on the new pages now, but keep
635  * stack->buffer locked. childbuf doesn't get unlocked either.
636  */
637  UnlockReleaseBuffer(rbuffer);
638  if (stack->parent == NULL)
639  UnlockReleaseBuffer(lbuffer);
640 
641  /*
642  * If we split the root, we're done. Otherwise the split is not
643  * complete until the downlink for the new page has been inserted to
644  * the parent.
645  */
646  result = (stack->parent == NULL);
647  }
648  else
649  {
650  elog(ERROR, "invalid return code from GIN beginPlaceToPage method: %d", rc);
651  result = false; /* keep compiler quiet */
652  }
653 
654  /* Clean up temp context */
655  MemoryContextSwitchTo(oldCxt);
656  MemoryContextDelete(tmpCxt);
657 
658  return result;
659 }
static void BlockIdSet(BlockIdData *blockId, BlockNumber blockNumber)
Definition: block.h:81
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4867
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2474
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:359
Page PageGetTempPage(Page page)
Definition: bufpage.c:365
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
unsigned short uint16
Definition: c.h:505
GinPlaceToPageRC
Definition: gin_private.h:144
@ GPTP_INSERT
Definition: gin_private.h:146
@ GPTP_SPLIT
Definition: gin_private.h:147
@ GPTP_NO_WORK
Definition: gin_private.h:145
#define GIN_COMPRESSED
Definition: ginblock.h:48
#define GIN_LEAF
Definition: ginblock.h:42
#define GinPageIsData(page)
Definition: ginblock.h:115
#define GIN_INCOMPLETE_SPLIT
Definition: ginblock.h:47
void GinInitPage(Page page, uint32 f, Size pageSize)
Definition: ginutil.c:338
Buffer GinNewBuffer(Relation index)
Definition: ginutil.c:300
#define GIN_INSERT_ISDATA
Definition: ginxlog.h:124
#define GIN_INSERT_ISLEAF
Definition: ginxlog.h:125
#define GIN_SPLIT_ROOT
Definition: ginxlog.h:126
#define XLOG_GIN_INSERT
Definition: ginxlog.h:35
#define XLOG_GIN_SPLIT
Definition: ginxlog.h:109
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
const void * data
void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3129
MemoryContextSwitchTo(old_ctx)
#define RelationNeedsWAL(relation)
Definition: rel.h:628
void(* execPlaceToPage)(GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void *)
Definition: gin_private.h:161
GinPlaceToPageRC(* beginPlaceToPage)(GinBtree, Buffer, GinBtreeStack *, void *, BlockNumber, void **, Page *, Page *)
Definition: gin_private.h:160
void(* fillRoot)(GinBtree, Page, BlockNumber, Page, BlockNumber, Page)
Definition: gin_private.h:163
BlockNumber nDataPages
Definition: gin.h:47
BlockNumber nEntryPages
Definition: gin.h:46
RelFileLocator rd_locator
Definition: rel.h:57
uint16 flags
Definition: ginxlog.h:39
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:364
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:242
void XLogBeginInsert(void)
Definition: xloginsert.c:149
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define REGBUF_FORCE_IMAGE
Definition: xloginsert.h:31

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, GinBtreeData::beginPlaceToPage, BlockIdSet(), GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), CurrentMemoryContext, data, elog, END_CRIT_SECTION, ERROR, GinBtreeData::execPlaceToPage, GinBtreeData::fillRoot, ginxlogInsert::flags, GIN_COMPRESSED, GIN_INCOMPLETE_SPLIT, GIN_INSERT_ISDATA, GIN_INSERT_ISLEAF, GIN_LEAF, GIN_SPLIT_ROOT, GinInitPage(), GinNewBuffer(), GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, GPTP_INSERT, GPTP_NO_WORK, GPTP_SPLIT, GinBtreeData::index, InvalidBlockNumber, InvalidBuffer, GinBtreeData::isBuild, GinBtreeData::isData, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), GinStatsData::nDataPages, GinStatsData::nEntryPages, PageGetTempPage(), PageSetLSN(), GinBtreeStack::parent, PredicateLockPageSplit(), RelationData::rd_locator, REGBUF_FORCE_IMAGE, REGBUF_STANDARD, RelationNeedsWAL, START_CRIT_SECTION, UnlockReleaseBuffer(), XLOG_GIN_INSERT, XLOG_GIN_SPLIT, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by ginFinishSplit(), and ginInsertValue().

◆ ginStepRight()

Buffer ginStepRight ( Buffer  buffer,
Relation  index,
int  lockmode 
)

Definition at line 177 of file ginbtree.c.

178 {
179  Buffer nextbuffer;
180  Page page = BufferGetPage(buffer);
181  bool isLeaf = GinPageIsLeaf(page);
182  bool isData = GinPageIsData(page);
183  BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
184 
185  nextbuffer = ReadBuffer(index, blkno);
186  LockBuffer(nextbuffer, lockmode);
187  UnlockReleaseBuffer(buffer);
188 
189  /* Sanity check that the page we stepped to is of similar kind. */
190  page = BufferGetPage(nextbuffer);
191  if (isLeaf != GinPageIsLeaf(page) || isData != GinPageIsData(page))
192  elog(ERROR, "right sibling of GIN page is of different type");
193 
194  return nextbuffer;
195 }
Definition: type.h:95

References BufferGetPage(), elog, ERROR, GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, LockBuffer(), ReadBuffer(), and UnlockReleaseBuffer().

Referenced by entryLoadMoreItems(), ginFindLeafPage(), ginFindParents(), ginFinishSplit(), moveRightIfItNeeded(), and scanPostingTree().

◆ ginTraverseLock()

int ginTraverseLock ( Buffer  buffer,
bool  searchMode 
)

Definition at line 39 of file ginbtree.c.

40 {
41  Page page;
42  int access = GIN_SHARE;
43 
44  LockBuffer(buffer, GIN_SHARE);
45  page = BufferGetPage(buffer);
46  if (GinPageIsLeaf(page))
47  {
48  if (searchMode == false)
49  {
50  /* we should relock our page */
51  LockBuffer(buffer, GIN_UNLOCK);
52  LockBuffer(buffer, GIN_EXCLUSIVE);
53 
54  /* But root can become non-leaf during relock */
55  if (!GinPageIsLeaf(page))
56  {
57  /* restore old lock type (very rare) */
58  LockBuffer(buffer, GIN_UNLOCK);
59  LockBuffer(buffer, GIN_SHARE);
60  }
61  else
63  }
64  }
65 
66  return access;
67 }

References BufferGetPage(), GIN_EXCLUSIVE, GIN_SHARE, GIN_UNLOCK, GinPageIsLeaf, and LockBuffer().

Referenced by ginFindLeafPage().