LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginbtree.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 202 278 72.7 %
Date: 2021-12-09 04:09:06 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ginbtree.c
       4             :  *    page utilities routines for the postgres inverted index access method.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *          src/backend/access/gin/ginbtree.c
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/gin_private.h"
      18             : #include "access/ginxlog.h"
      19             : #include "access/xloginsert.h"
      20             : #include "miscadmin.h"
      21             : #include "storage/predicate.h"
      22             : #include "utils/memutils.h"
      23             : #include "utils/rel.h"
      24             : 
      25             : static void ginFindParents(GinBtree btree, GinBtreeStack *stack);
      26             : static bool ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
      27             :                            void *insertdata, BlockNumber updateblkno,
      28             :                            Buffer childbuf, GinStatsData *buildStats);
      29             : static void ginFinishSplit(GinBtree btree, GinBtreeStack *stack,
      30             :                            bool freestack, GinStatsData *buildStats);
      31             : 
      32             : /*
      33             :  * Lock buffer by needed method for search.
      34             :  */
      35             : int
      36      858666 : ginTraverseLock(Buffer buffer, bool searchMode)
      37             : {
      38             :     Page        page;
      39      858666 :     int         access = GIN_SHARE;
      40             : 
      41      858666 :     LockBuffer(buffer, GIN_SHARE);
      42      858666 :     page = BufferGetPage(buffer);
      43      858666 :     if (GinPageIsLeaf(page))
      44             :     {
      45      474258 :         if (searchMode == false)
      46             :         {
      47             :             /* we should relock our page */
      48      471088 :             LockBuffer(buffer, GIN_UNLOCK);
      49      471088 :             LockBuffer(buffer, GIN_EXCLUSIVE);
      50             : 
      51             :             /* But root can become non-leaf during relock */
      52      471088 :             if (!GinPageIsLeaf(page))
      53             :             {
      54             :                 /* restore old lock type (very rare) */
      55           0 :                 LockBuffer(buffer, GIN_UNLOCK);
      56           0 :                 LockBuffer(buffer, GIN_SHARE);
      57             :             }
      58             :             else
      59      471088 :                 access = GIN_EXCLUSIVE;
      60             :         }
      61             :     }
      62             : 
      63      858666 :     return access;
      64             : }
      65             : 
      66             : /*
      67             :  * Descend the tree to the leaf page that contains or would contain the key
      68             :  * we're searching for. The key should already be filled in 'btree', in
      69             :  * tree-type specific manner. If btree->fullScan is true, descends to the
      70             :  * leftmost leaf page.
      71             :  *
      72             :  * If 'searchmode' is false, on return stack->buffer is exclusively locked,
      73             :  * and the stack represents the full path to the root. Otherwise stack->buffer
      74             :  * is share-locked, and stack->parent is NULL.
      75             :  *
      76             :  * If 'rootConflictCheck' is true, tree root is checked for serialization
      77             :  * conflict.
      78             :  */
      79             : GinBtreeStack *
      80      474262 : ginFindLeafPage(GinBtree btree, bool searchMode,
      81             :                 bool rootConflictCheck, Snapshot snapshot)
      82             : {
      83             :     GinBtreeStack *stack;
      84             : 
      85      474262 :     stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
      86      474262 :     stack->blkno = btree->rootBlkno;
      87      474262 :     stack->buffer = ReadBuffer(btree->index, btree->rootBlkno);
      88      474262 :     stack->parent = NULL;
      89      474262 :     stack->predictNumber = 1;
      90             : 
      91      474262 :     if (rootConflictCheck)
      92       42844 :         CheckForSerializableConflictIn(btree->index, NULL, btree->rootBlkno);
      93             : 
      94             :     for (;;)
      95      384408 :     {
      96             :         Page        page;
      97             :         BlockNumber child;
      98             :         int         access;
      99             : 
     100      858666 :         stack->off = InvalidOffsetNumber;
     101             : 
     102      858666 :         page = BufferGetPage(stack->buffer);
     103      858666 :         TestForOldSnapshot(snapshot, btree->index, page);
     104             : 
     105      858666 :         access = ginTraverseLock(stack->buffer, searchMode);
     106             : 
     107             :         /*
     108             :          * If we're going to modify the tree, finish any incomplete splits we
     109             :          * encounter on the way.
     110             :          */
     111      858666 :         if (!searchMode && GinPageIsIncompleteSplit(page))
     112           0 :             ginFinishSplit(btree, stack, false, NULL);
     113             : 
     114             :         /*
     115             :          * ok, page is correctly locked, we should check to move right ..,
     116             :          * root never has a right link, so small optimization
     117             :          */
     118     1243016 :         while (btree->fullScan == false && stack->blkno != btree->rootBlkno &&
     119      384350 :                btree->isMoveRight(btree, page))
     120             :         {
     121           0 :             BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;
     122             : 
     123           0 :             if (rightlink == InvalidBlockNumber)
     124             :                 /* rightmost page */
     125           0 :                 break;
     126             : 
     127           0 :             stack->buffer = ginStepRight(stack->buffer, btree->index, access);
     128           0 :             stack->blkno = rightlink;
     129           0 :             page = BufferGetPage(stack->buffer);
     130           0 :             TestForOldSnapshot(snapshot, btree->index, page);
     131             : 
     132           0 :             if (!searchMode && GinPageIsIncompleteSplit(page))
     133           0 :                 ginFinishSplit(btree, stack, false, NULL);
     134             :         }
     135             : 
     136      858666 :         if (GinPageIsLeaf(page))    /* we found, return locked page */
     137      474258 :             return stack;
     138             : 
     139             :         /* now we have correct buffer, try to find child */
     140      384408 :         child = btree->findChildPage(btree, stack);
     141             : 
     142      384408 :         LockBuffer(stack->buffer, GIN_UNLOCK);
     143             :         Assert(child != InvalidBlockNumber);
     144             :         Assert(stack->blkno != child);
     145             : 
     146      384408 :         if (searchMode)
     147             :         {
     148             :             /* in search mode we may forget path to leaf */
     149        2118 :             stack->blkno = child;
     150        2118 :             stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
     151             :         }
     152             :         else
     153             :         {
     154      382290 :             GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
     155             : 
     156      382290 :             ptr->parent = stack;
     157      382290 :             stack = ptr;
     158      382290 :             stack->blkno = child;
     159      382290 :             stack->buffer = ReadBuffer(btree->index, stack->blkno);
     160      382290 :             stack->predictNumber = 1;
     161             :         }
     162             :     }
     163             : }
     164             : 
     165             : /*
     166             :  * Step right from current page.
     167             :  *
     168             :  * The next page is locked first, before releasing the current page. This is
     169             :  * crucial to protect from concurrent page deletion (see comment in
     170             :  * ginDeletePage).
     171             :  */
     172             : Buffer
     173        2364 : ginStepRight(Buffer buffer, Relation index, int lockmode)
     174             : {
     175             :     Buffer      nextbuffer;
     176        2364 :     Page        page = BufferGetPage(buffer);
     177        2364 :     bool        isLeaf = GinPageIsLeaf(page);
     178        2364 :     bool        isData = GinPageIsData(page);
     179        2364 :     BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
     180             : 
     181        2364 :     nextbuffer = ReadBuffer(index, blkno);
     182        2364 :     LockBuffer(nextbuffer, lockmode);
     183        2364 :     UnlockReleaseBuffer(buffer);
     184             : 
     185             :     /* Sanity check that the page we stepped to is of similar kind. */
     186        2364 :     page = BufferGetPage(nextbuffer);
     187        2364 :     if (isLeaf != GinPageIsLeaf(page) || isData != GinPageIsData(page))
     188           0 :         elog(ERROR, "right sibling of GIN page is of different type");
     189             : 
     190        2364 :     return nextbuffer;
     191             : }
     192             : 
     193             : void
     194      474246 : freeGinBtreeStack(GinBtreeStack *stack)
     195             : {
     196     1328354 :     while (stack)
     197             :     {
     198      854108 :         GinBtreeStack *tmp = stack->parent;
     199             : 
     200      854108 :         if (stack->buffer != InvalidBuffer)
     201      854108 :             ReleaseBuffer(stack->buffer);
     202             : 
     203      854108 :         pfree(stack);
     204      854108 :         stack = tmp;
     205             :     }
     206      474246 : }
     207             : 
     208             : /*
     209             :  * Try to find parent for current stack position. Returns correct parent and
     210             :  * child's offset in stack->parent. The root page is never released, to
     211             :  * prevent conflict with vacuum process.
     212             :  */
     213             : static void
     214           0 : ginFindParents(GinBtree btree, GinBtreeStack *stack)
     215             : {
     216             :     Page        page;
     217             :     Buffer      buffer;
     218             :     BlockNumber blkno,
     219             :                 leftmostBlkno;
     220             :     OffsetNumber offset;
     221             :     GinBtreeStack *root;
     222             :     GinBtreeStack *ptr;
     223             : 
     224             :     /*
     225             :      * Unwind the stack all the way up to the root, leaving only the root
     226             :      * item.
     227             :      *
     228             :      * Be careful not to release the pin on the root page! The pin on root
     229             :      * page is required to lock out concurrent vacuums on the tree.
     230             :      */
     231           0 :     root = stack->parent;
     232           0 :     while (root->parent)
     233             :     {
     234           0 :         ReleaseBuffer(root->buffer);
     235           0 :         root = root->parent;
     236             :     }
     237             : 
     238             :     Assert(root->blkno == btree->rootBlkno);
     239             :     Assert(BufferGetBlockNumber(root->buffer) == btree->rootBlkno);
     240           0 :     root->off = InvalidOffsetNumber;
     241             : 
     242           0 :     blkno = root->blkno;
     243           0 :     buffer = root->buffer;
     244             : 
     245           0 :     ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
     246             : 
     247             :     for (;;)
     248             :     {
     249           0 :         LockBuffer(buffer, GIN_EXCLUSIVE);
     250           0 :         page = BufferGetPage(buffer);
     251           0 :         if (GinPageIsLeaf(page))
     252           0 :             elog(ERROR, "Lost path");
     253             : 
     254           0 :         if (GinPageIsIncompleteSplit(page))
     255             :         {
     256             :             Assert(blkno != btree->rootBlkno);
     257           0 :             ptr->blkno = blkno;
     258           0 :             ptr->buffer = buffer;
     259             : 
     260             :             /*
     261             :              * parent may be wrong, but if so, the ginFinishSplit call will
     262             :              * recurse to call ginFindParents again to fix it.
     263             :              */
     264           0 :             ptr->parent = root;
     265           0 :             ptr->off = InvalidOffsetNumber;
     266             : 
     267           0 :             ginFinishSplit(btree, ptr, false, NULL);
     268             :         }
     269             : 
     270           0 :         leftmostBlkno = btree->getLeftMostChild(btree, page);
     271             : 
     272           0 :         while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber)
     273             :         {
     274           0 :             blkno = GinPageGetOpaque(page)->rightlink;
     275           0 :             if (blkno == InvalidBlockNumber)
     276             :             {
     277           0 :                 UnlockReleaseBuffer(buffer);
     278           0 :                 break;
     279             :             }
     280           0 :             buffer = ginStepRight(buffer, btree->index, GIN_EXCLUSIVE);
     281           0 :             page = BufferGetPage(buffer);
     282             : 
     283             :             /* finish any incomplete splits, as above */
     284           0 :             if (GinPageIsIncompleteSplit(page))
     285             :             {
     286             :                 Assert(blkno != btree->rootBlkno);
     287           0 :                 ptr->blkno = blkno;
     288           0 :                 ptr->buffer = buffer;
     289           0 :                 ptr->parent = root;
     290           0 :                 ptr->off = InvalidOffsetNumber;
     291             : 
     292           0 :                 ginFinishSplit(btree, ptr, false, NULL);
     293             :             }
     294             :         }
     295             : 
     296           0 :         if (blkno != InvalidBlockNumber)
     297             :         {
     298           0 :             ptr->blkno = blkno;
     299           0 :             ptr->buffer = buffer;
     300           0 :             ptr->parent = root; /* it may be wrong, but in next call we will
     301             :                                  * correct */
     302           0 :             ptr->off = offset;
     303           0 :             stack->parent = ptr;
     304           0 :             return;
     305             :         }
     306             : 
     307             :         /* Descend down to next level */
     308           0 :         blkno = leftmostBlkno;
     309           0 :         buffer = ReadBuffer(btree->index, blkno);
     310             :     }
     311             : }
     312             : 
     313             : /*
     314             :  * Insert a new item to a page.
     315             :  *
     316             :  * Returns true if the insertion was finished. On false, the page was split and
     317             :  * the parent needs to be updated. (A root split returns true as it doesn't
     318             :  * need any further action by the caller to complete.)
     319             :  *
     320             :  * When inserting a downlink to an internal page, 'childbuf' contains the
     321             :  * child page that was split. Its GIN_INCOMPLETE_SPLIT flag will be cleared
     322             :  * atomically with the insert. Also, the existing item at offset stack->off
     323             :  * in the target page is updated to point to updateblkno.
     324             :  *
     325             :  * stack->buffer is locked on entry, and is kept locked.
     326             :  * Likewise for childbuf, if given.
     327             :  */
     328             : static bool
     329      430784 : ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
     330             :                void *insertdata, BlockNumber updateblkno,
     331             :                Buffer childbuf, GinStatsData *buildStats)
     332             : {
     333      430784 :     Page        page = BufferGetPage(stack->buffer);
     334             :     bool        result;
     335             :     GinPlaceToPageRC rc;
     336      430784 :     uint16      xlflags = 0;
     337      430784 :     Page        childpage = NULL;
     338      430784 :     Page        newlpage = NULL,
     339      430784 :                 newrpage = NULL;
     340      430784 :     void       *ptp_workspace = NULL;
     341             :     MemoryContext tmpCxt;
     342             :     MemoryContext oldCxt;
     343             : 
     344             :     /*
     345             :      * We do all the work of this function and its subfunctions in a temporary
     346             :      * memory context.  This avoids leakages and simplifies APIs, since some
     347             :      * subfunctions allocate storage that has to survive until we've finished
     348             :      * the WAL insertion.
     349             :      */
     350      430784 :     tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
     351             :                                    "ginPlaceToPage temporary context",
     352             :                                    ALLOCSET_DEFAULT_SIZES);
     353      430784 :     oldCxt = MemoryContextSwitchTo(tmpCxt);
     354             : 
     355      430784 :     if (GinPageIsData(page))
     356       42876 :         xlflags |= GIN_INSERT_ISDATA;
     357      430784 :     if (GinPageIsLeaf(page))
     358             :     {
     359      428368 :         xlflags |= GIN_INSERT_ISLEAF;
     360             :         Assert(!BufferIsValid(childbuf));
     361             :         Assert(updateblkno == InvalidBlockNumber);
     362             :     }
     363             :     else
     364             :     {
     365             :         Assert(BufferIsValid(childbuf));
     366             :         Assert(updateblkno != InvalidBlockNumber);
     367        2416 :         childpage = BufferGetPage(childbuf);
     368             :     }
     369             : 
     370             :     /*
     371             :      * See if the incoming tuple will fit on the page.  beginPlaceToPage will
     372             :      * decide if the page needs to be split, and will compute the split
     373             :      * contents if so.  See comments for beginPlaceToPage and execPlaceToPage
     374             :      * functions for more details of the API here.
     375             :      */
     376      430784 :     rc = btree->beginPlaceToPage(btree, stack->buffer, stack,
     377             :                                  insertdata, updateblkno,
     378             :                                  &ptp_workspace,
     379             :                                  &newlpage, &newrpage);
     380             : 
     381      430784 :     if (rc == GPTP_NO_WORK)
     382             :     {
     383             :         /* Nothing to do */
     384           0 :         result = true;
     385             :     }
     386      430784 :     else if (rc == GPTP_INSERT)
     387             :     {
     388             :         /* It will fit, perform the insertion */
     389      428170 :         START_CRIT_SECTION();
     390             : 
     391      428170 :         if (RelationNeedsWAL(btree->index) && !btree->isBuild)
     392             :         {
     393      148058 :             XLogBeginInsert();
     394      148058 :             XLogRegisterBuffer(0, stack->buffer, REGBUF_STANDARD);
     395      148058 :             if (BufferIsValid(childbuf))
     396         556 :                 XLogRegisterBuffer(1, childbuf, REGBUF_STANDARD);
     397             :         }
     398             : 
     399             :         /* Perform the page update, and register any extra WAL data */
     400      428170 :         btree->execPlaceToPage(btree, stack->buffer, stack,
     401             :                                insertdata, updateblkno, ptp_workspace);
     402             : 
     403      428170 :         MarkBufferDirty(stack->buffer);
     404             : 
     405             :         /* An insert to an internal page finishes the split of the child. */
     406      428170 :         if (BufferIsValid(childbuf))
     407             :         {
     408        2416 :             GinPageGetOpaque(childpage)->flags &= ~GIN_INCOMPLETE_SPLIT;
     409        2416 :             MarkBufferDirty(childbuf);
     410             :         }
     411             : 
     412      428170 :         if (RelationNeedsWAL(btree->index) && !btree->isBuild)
     413             :         {
     414             :             XLogRecPtr  recptr;
     415             :             ginxlogInsert xlrec;
     416             :             BlockIdData childblknos[2];
     417             : 
     418      148058 :             xlrec.flags = xlflags;
     419             : 
     420      148058 :             XLogRegisterData((char *) &xlrec, sizeof(ginxlogInsert));
     421             : 
     422             :             /*
     423             :              * Log information about child if this was an insertion of a
     424             :              * downlink.
     425             :              */
     426      148058 :             if (BufferIsValid(childbuf))
     427             :             {
     428         556 :                 BlockIdSet(&childblknos[0], BufferGetBlockNumber(childbuf));
     429         556 :                 BlockIdSet(&childblknos[1], GinPageGetOpaque(childpage)->rightlink);
     430         556 :                 XLogRegisterData((char *) childblknos,
     431             :                                  sizeof(BlockIdData) * 2);
     432             :             }
     433             : 
     434      148058 :             recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT);
     435      148058 :             PageSetLSN(page, recptr);
     436      148058 :             if (BufferIsValid(childbuf))
     437         556 :                 PageSetLSN(childpage, recptr);
     438             :         }
     439             : 
     440      428170 :         END_CRIT_SECTION();
     441             : 
     442             :         /* Insertion is complete. */
     443      428170 :         result = true;
     444             :     }
     445        2614 :     else if (rc == GPTP_SPLIT)
     446             :     {
     447             :         /*
     448             :          * Didn't fit, need to split.  The split has been computed in newlpage
     449             :          * and newrpage, which are pointers to palloc'd pages, not associated
     450             :          * with buffers.  stack->buffer is not touched yet.
     451             :          */
     452             :         Buffer      rbuffer;
     453             :         BlockNumber savedRightLink;
     454             :         ginxlogSplit data;
     455        2614 :         Buffer      lbuffer = InvalidBuffer;
     456        2614 :         Page        newrootpg = NULL;
     457             : 
     458             :         /* Get a new index page to become the right page */
     459        2614 :         rbuffer = GinNewBuffer(btree->index);
     460             : 
     461             :         /* During index build, count the new page */
     462        2614 :         if (buildStats)
     463             :         {
     464         882 :             if (btree->isData)
     465          90 :                 buildStats->nDataPages++;
     466             :             else
     467         792 :                 buildStats->nEntryPages++;
     468             :         }
     469             : 
     470        2614 :         savedRightLink = GinPageGetOpaque(page)->rightlink;
     471             : 
     472             :         /* Begin setting up WAL record */
     473        2614 :         data.node = btree->index->rd_node;
     474        2614 :         data.flags = xlflags;
     475        2614 :         if (BufferIsValid(childbuf))
     476             :         {
     477           0 :             data.leftChildBlkno = BufferGetBlockNumber(childbuf);
     478           0 :             data.rightChildBlkno = GinPageGetOpaque(childpage)->rightlink;
     479             :         }
     480             :         else
     481        2614 :             data.leftChildBlkno = data.rightChildBlkno = InvalidBlockNumber;
     482             : 
     483        2614 :         if (stack->parent == NULL)
     484             :         {
     485             :             /*
     486             :              * splitting the root, so we need to allocate new left page and
     487             :              * place pointers to left and right page on root page.
     488             :              */
     489         198 :             lbuffer = GinNewBuffer(btree->index);
     490             : 
     491             :             /* During index build, count the new left page */
     492         198 :             if (buildStats)
     493             :             {
     494         170 :                 if (btree->isData)
     495          74 :                     buildStats->nDataPages++;
     496             :                 else
     497          96 :                     buildStats->nEntryPages++;
     498             :             }
     499             : 
     500         198 :             data.rrlink = InvalidBlockNumber;
     501         198 :             data.flags |= GIN_SPLIT_ROOT;
     502             : 
     503         198 :             GinPageGetOpaque(newrpage)->rightlink = InvalidBlockNumber;
     504         198 :             GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
     505             : 
     506             :             /*
     507             :              * Construct a new root page containing downlinks to the new left
     508             :              * and right pages.  (Do this in a temporary copy rather than
     509             :              * overwriting the original page directly, since we're not in the
     510             :              * critical section yet.)
     511             :              */
     512         198 :             newrootpg = PageGetTempPage(newrpage);
     513         198 :             GinInitPage(newrootpg, GinPageGetOpaque(newlpage)->flags & ~(GIN_LEAF | GIN_COMPRESSED), BLCKSZ);
     514             : 
     515         198 :             btree->fillRoot(btree, newrootpg,
     516             :                             BufferGetBlockNumber(lbuffer), newlpage,
     517             :                             BufferGetBlockNumber(rbuffer), newrpage);
     518             : 
     519         198 :             if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
     520             :             {
     521             : 
     522         198 :                 PredicateLockPageSplit(btree->index,
     523             :                                        BufferGetBlockNumber(stack->buffer),
     524             :                                        BufferGetBlockNumber(lbuffer));
     525             : 
     526         198 :                 PredicateLockPageSplit(btree->index,
     527             :                                        BufferGetBlockNumber(stack->buffer),
     528             :                                        BufferGetBlockNumber(rbuffer));
     529             :             }
     530             : 
     531             :         }
     532             :         else
     533             :         {
     534             :             /* splitting a non-root page */
     535        2416 :             data.rrlink = savedRightLink;
     536             : 
     537        2416 :             GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
     538        2416 :             GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
     539        2416 :             GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
     540             : 
     541        2416 :             if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
     542             :             {
     543             : 
     544        2416 :                 PredicateLockPageSplit(btree->index,
     545             :                                        BufferGetBlockNumber(stack->buffer),
     546             :                                        BufferGetBlockNumber(rbuffer));
     547             :             }
     548             :         }
     549             : 
     550             :         /*
     551             :          * OK, we have the new contents of the left page in a temporary copy
     552             :          * now (newlpage), and likewise for the new contents of the
     553             :          * newly-allocated right block. The original page is still unchanged.
     554             :          *
     555             :          * If this is a root split, we also have a temporary page containing
     556             :          * the new contents of the root.
     557             :          */
     558             : 
     559        2614 :         START_CRIT_SECTION();
     560             : 
     561        2614 :         MarkBufferDirty(rbuffer);
     562        2614 :         MarkBufferDirty(stack->buffer);
     563             : 
     564             :         /*
     565             :          * Restore the temporary copies over the real buffers.
     566             :          */
     567        2614 :         if (stack->parent == NULL)
     568             :         {
     569             :             /* Splitting the root, three pages to update */
     570         198 :             MarkBufferDirty(lbuffer);
     571         198 :             memcpy(page, newrootpg, BLCKSZ);
     572         198 :             memcpy(BufferGetPage(lbuffer), newlpage, BLCKSZ);
     573         198 :             memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
     574             :         }
     575             :         else
     576             :         {
     577             :             /* Normal split, only two pages to update */
     578        2416 :             memcpy(page, newlpage, BLCKSZ);
     579        2416 :             memcpy(BufferGetPage(rbuffer), newrpage, BLCKSZ);
     580             :         }
     581             : 
     582             :         /* We also clear childbuf's INCOMPLETE_SPLIT flag, if passed */
     583        2614 :         if (BufferIsValid(childbuf))
     584             :         {
     585           0 :             GinPageGetOpaque(childpage)->flags &= ~GIN_INCOMPLETE_SPLIT;
     586           0 :             MarkBufferDirty(childbuf);
     587             :         }
     588             : 
     589             :         /* write WAL record */
     590        2614 :         if (RelationNeedsWAL(btree->index) && !btree->isBuild)
     591             :         {
     592             :             XLogRecPtr  recptr;
     593             : 
     594         572 :             XLogBeginInsert();
     595             : 
     596             :             /*
     597             :              * We just take full page images of all the split pages. Splits
     598             :              * are uncommon enough that it's not worth complicating the code
     599             :              * to be more efficient.
     600             :              */
     601         572 :             if (stack->parent == NULL)
     602             :             {
     603          16 :                 XLogRegisterBuffer(0, lbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
     604          16 :                 XLogRegisterBuffer(1, rbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
     605          16 :                 XLogRegisterBuffer(2, stack->buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
     606             :             }
     607             :             else
     608             :             {
     609         556 :                 XLogRegisterBuffer(0, stack->buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
     610         556 :                 XLogRegisterBuffer(1, rbuffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
     611             :             }
     612         572 :             if (BufferIsValid(childbuf))
     613           0 :                 XLogRegisterBuffer(3, childbuf, REGBUF_STANDARD);
     614             : 
     615         572 :             XLogRegisterData((char *) &data, sizeof(ginxlogSplit));
     616             : 
     617         572 :             recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT);
     618             : 
     619         572 :             PageSetLSN(page, recptr);
     620         572 :             PageSetLSN(BufferGetPage(rbuffer), recptr);
     621         572 :             if (stack->parent == NULL)
     622          16 :                 PageSetLSN(BufferGetPage(lbuffer), recptr);
     623         572 :             if (BufferIsValid(childbuf))
     624           0 :                 PageSetLSN(childpage, recptr);
     625             :         }
     626        2614 :         END_CRIT_SECTION();
     627             : 
     628             :         /*
     629             :          * We can release the locks/pins on the new pages now, but keep
     630             :          * stack->buffer locked.  childbuf doesn't get unlocked either.
     631             :          */
     632        2614 :         UnlockReleaseBuffer(rbuffer);
     633        2614 :         if (stack->parent == NULL)
     634         198 :             UnlockReleaseBuffer(lbuffer);
     635             : 
     636             :         /*
     637             :          * If we split the root, we're done. Otherwise the split is not
     638             :          * complete until the downlink for the new page has been inserted to
     639             :          * the parent.
     640             :          */
     641        2614 :         result = (stack->parent == NULL);
     642             :     }
     643             :     else
     644             :     {
     645           0 :         elog(ERROR, "invalid return code from GIN beginPlaceToPage method: %d", rc);
     646             :         result = false;         /* keep compiler quiet */
     647             :     }
     648             : 
     649             :     /* Clean up temp context */
     650      430784 :     MemoryContextSwitchTo(oldCxt);
     651      430784 :     MemoryContextDelete(tmpCxt);
     652             : 
     653      430784 :     return result;
     654             : }
     655             : 
     656             : /*
     657             :  * Finish a split by inserting the downlink for the new page to parent.
     658             :  *
     659             :  * On entry, stack->buffer is exclusively locked.
     660             :  *
     661             :  * If freestack is true, all the buffers are released and unlocked as we
     662             :  * crawl up the tree, and 'stack' is freed. Otherwise stack->buffer is kept
     663             :  * locked, and stack is unmodified, except for possibly moving right to find
     664             :  * the correct parent of page.
     665             :  */
     666             : static void
     667        2416 : ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
     668             :                GinStatsData *buildStats)
     669             : {
     670             :     Page        page;
     671             :     bool        done;
     672        2416 :     bool        first = true;
     673             : 
     674             :     /*
     675             :      * freestack == false when we encounter an incompletely split page during
     676             :      * a scan, while freestack == true is used in the normal scenario that a
     677             :      * split is finished right after the initial insert.
     678             :      */
     679        2416 :     if (!freestack)
     680           0 :         elog(DEBUG1, "finishing incomplete split of block %u in gin index \"%s\"",
     681             :              stack->blkno, RelationGetRelationName(btree->index));
     682             : 
     683             :     /* this loop crawls up the stack until the insertion is complete */
     684             :     do
     685             :     {
     686        2416 :         GinBtreeStack *parent = stack->parent;
     687             :         void       *insertdata;
     688             :         BlockNumber updateblkno;
     689             : 
     690             :         /* search parent to lock */
     691        2416 :         LockBuffer(parent->buffer, GIN_EXCLUSIVE);
     692             : 
     693             :         /*
     694             :          * If the parent page was incompletely split, finish that split first,
     695             :          * then continue with the current one.
     696             :          *
     697             :          * Note: we have to finish *all* incomplete splits we encounter, even
     698             :          * if we have to move right. Otherwise we might choose as the target a
     699             :          * page that has no downlink in the parent, and splitting it further
     700             :          * would fail.
     701             :          */
     702        2416 :         if (GinPageIsIncompleteSplit(BufferGetPage(parent->buffer)))
     703           0 :             ginFinishSplit(btree, parent, false, buildStats);
     704             : 
     705             :         /* move right if it's needed */
     706        2416 :         page = BufferGetPage(parent->buffer);
     707        2416 :         while ((parent->off = btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber)
     708             :         {
     709           0 :             if (GinPageRightMost(page))
     710             :             {
     711             :                 /*
     712             :                  * rightmost page, but we don't find parent, we should use
     713             :                  * plain search...
     714             :                  */
     715           0 :                 LockBuffer(parent->buffer, GIN_UNLOCK);
     716           0 :                 ginFindParents(btree, stack);
     717           0 :                 parent = stack->parent;
     718             :                 Assert(parent != NULL);
     719           0 :                 break;
     720             :             }
     721             : 
     722           0 :             parent->buffer = ginStepRight(parent->buffer, btree->index, GIN_EXCLUSIVE);
     723           0 :             parent->blkno = BufferGetBlockNumber(parent->buffer);
     724           0 :             page = BufferGetPage(parent->buffer);
     725             : 
     726           0 :             if (GinPageIsIncompleteSplit(BufferGetPage(parent->buffer)))
     727           0 :                 ginFinishSplit(btree, parent, false, buildStats);
     728             :         }
     729             : 
     730             :         /* insert the downlink */
     731        2416 :         insertdata = btree->prepareDownlink(btree, stack->buffer);
     732        2416 :         updateblkno = GinPageGetOpaque(BufferGetPage(stack->buffer))->rightlink;
     733        2416 :         done = ginPlaceToPage(btree, parent,
     734             :                               insertdata, updateblkno,
     735             :                               stack->buffer, buildStats);
     736        2416 :         pfree(insertdata);
     737             : 
     738             :         /*
     739             :          * If the caller requested to free the stack, unlock and release the
     740             :          * child buffer now. Otherwise keep it pinned and locked, but if we
     741             :          * have to recurse up the tree, we can unlock the upper pages, only
     742             :          * keeping the page at the bottom of the stack locked.
     743             :          */
     744        2416 :         if (!first || freestack)
     745        2416 :             LockBuffer(stack->buffer, GIN_UNLOCK);
     746        2416 :         if (freestack)
     747             :         {
     748        2416 :             ReleaseBuffer(stack->buffer);
     749        2416 :             pfree(stack);
     750             :         }
     751        2416 :         stack = parent;
     752             : 
     753        2416 :         first = false;
     754        2416 :     } while (!done);
     755             : 
     756             :     /* unlock the parent */
     757        2416 :     LockBuffer(stack->buffer, GIN_UNLOCK);
     758             : 
     759        2416 :     if (freestack)
     760        2416 :         freeGinBtreeStack(stack);
     761        2416 : }
     762             : 
     763             : /*
     764             :  * Insert a value to tree described by stack.
     765             :  *
     766             :  * The value to be inserted is given in 'insertdata'. Its format depends
     767             :  * on whether this is an entry or data tree, ginInsertValue just passes it
     768             :  * through to the tree-specific callback function.
     769             :  *
     770             :  * During an index build, buildStats is non-null and the counters it contains
     771             :  * are incremented as needed.
     772             :  *
     773             :  * NB: the passed-in stack is freed, as though by freeGinBtreeStack.
     774             :  */
     775             : void
     776      428368 : ginInsertValue(GinBtree btree, GinBtreeStack *stack, void *insertdata,
     777             :                GinStatsData *buildStats)
     778             : {
     779             :     bool        done;
     780             : 
     781             :     /* If the leaf page was incompletely split, finish the split first */
     782      428368 :     if (GinPageIsIncompleteSplit(BufferGetPage(stack->buffer)))
     783           0 :         ginFinishSplit(btree, stack, false, buildStats);
     784             : 
     785      428368 :     done = ginPlaceToPage(btree, stack,
     786             :                           insertdata, InvalidBlockNumber,
     787             :                           InvalidBuffer, buildStats);
     788      428368 :     if (done)
     789             :     {
     790      425952 :         LockBuffer(stack->buffer, GIN_UNLOCK);
     791      425952 :         freeGinBtreeStack(stack);
     792             :     }
     793             :     else
     794        2416 :         ginFinishSplit(btree, stack, true, buildStats);
     795      428368 : }

Generated by: LCOV version 1.14