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

Generated by: LCOV version 1.13