LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginentrypage.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 249 275 90.5 %
Date: 2024-11-21 08:14:44 Functions: 16 17 94.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ginentrypage.c
       4             :  *    routines for handling GIN entry tree pages.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *          src/backend/access/gin/ginentrypage.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 "utils/rel.h"
      21             : 
      22             : static void entrySplitPage(GinBtree btree, Buffer origbuf,
      23             :                            GinBtreeStack *stack,
      24             :                            GinBtreeEntryInsertData *insertData,
      25             :                            BlockNumber updateblkno,
      26             :                            Page *newlpage, Page *newrpage);
      27             : 
      28             : /*
      29             :  * Form a tuple for entry tree.
      30             :  *
      31             :  * If the tuple would be too big to be stored, function throws a suitable
      32             :  * error if errorTooBig is true, or returns NULL if errorTooBig is false.
      33             :  *
      34             :  * See src/backend/access/gin/README for a description of the index tuple
      35             :  * format that is being built here.  We build on the assumption that we
      36             :  * are making a leaf-level key entry containing a posting list of nipd items.
      37             :  * If the caller is actually trying to make a posting-tree entry, non-leaf
      38             :  * entry, or pending-list entry, it should pass dataSize = 0 and then overwrite
      39             :  * the t_tid fields as necessary.  In any case, 'data' can be NULL to skip
      40             :  * filling in the posting list; the caller is responsible for filling it
      41             :  * afterwards if data = NULL and nipd > 0.
      42             :  */
      43             : IndexTuple
      44     2118342 : GinFormTuple(GinState *ginstate,
      45             :              OffsetNumber attnum, Datum key, GinNullCategory category,
      46             :              Pointer data, Size dataSize, int nipd,
      47             :              bool errorTooBig)
      48             : {
      49             :     Datum       datums[2];
      50             :     bool        isnull[2];
      51             :     IndexTuple  itup;
      52             :     uint32      newsize;
      53             : 
      54             :     /* Build the basic tuple: optional column number, plus key datum */
      55     2118342 :     if (ginstate->oneCol)
      56             :     {
      57      916476 :         datums[0] = key;
      58      916476 :         isnull[0] = (category != GIN_CAT_NORM_KEY);
      59             :     }
      60             :     else
      61             :     {
      62     1201866 :         datums[0] = UInt16GetDatum(attnum);
      63     1201866 :         isnull[0] = false;
      64     1201866 :         datums[1] = key;
      65     1201866 :         isnull[1] = (category != GIN_CAT_NORM_KEY);
      66             :     }
      67             : 
      68     2118342 :     itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
      69             : 
      70             :     /*
      71             :      * Determine and store offset to the posting list, making sure there is
      72             :      * room for the category byte if needed.
      73             :      *
      74             :      * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
      75             :      * be some wasted pad space.  Is it worth recomputing the data length to
      76             :      * prevent that?  That would also allow us to Assert that the real data
      77             :      * doesn't overlap the GinNullCategory byte, which this code currently
      78             :      * takes on faith.
      79             :      */
      80     2118342 :     newsize = IndexTupleSize(itup);
      81             : 
      82     2118342 :     if (IndexTupleHasNulls(itup))
      83             :     {
      84             :         uint32      minsize;
      85             : 
      86             :         Assert(category != GIN_CAT_NORM_KEY);
      87         236 :         minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
      88         236 :         newsize = Max(newsize, minsize);
      89             :     }
      90             : 
      91     2118342 :     newsize = SHORTALIGN(newsize);
      92             : 
      93     2118342 :     GinSetPostingOffset(itup, newsize);
      94     2118342 :     GinSetNPosting(itup, nipd);
      95             : 
      96             :     /*
      97             :      * Add space needed for posting list, if any.  Then check that the tuple
      98             :      * won't be too big to store.
      99             :      */
     100     2118342 :     newsize += dataSize;
     101             : 
     102     2118342 :     newsize = MAXALIGN(newsize);
     103             : 
     104     2118342 :     if (newsize > GinMaxItemSize)
     105             :     {
     106         116 :         if (errorTooBig)
     107           0 :             ereport(ERROR,
     108             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     109             :                      errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
     110             :                             (Size) newsize, (Size) GinMaxItemSize,
     111             :                             RelationGetRelationName(ginstate->index))));
     112         116 :         pfree(itup);
     113         116 :         return NULL;
     114             :     }
     115             : 
     116             :     /*
     117             :      * Resize tuple if needed
     118             :      */
     119     2118226 :     if (newsize != IndexTupleSize(itup))
     120             :     {
     121      722016 :         itup = repalloc(itup, newsize);
     122             : 
     123             :         /*
     124             :          * PostgreSQL 9.3 and earlier did not clear this new space, so we
     125             :          * might find uninitialized padding when reading tuples from disk.
     126             :          */
     127      722016 :         memset((char *) itup + IndexTupleSize(itup),
     128      722016 :                0, newsize - IndexTupleSize(itup));
     129             :         /* set new size in tuple header */
     130      722016 :         itup->t_info &= ~INDEX_SIZE_MASK;
     131      722016 :         itup->t_info |= newsize;
     132             :     }
     133             : 
     134             :     /*
     135             :      * Copy in the posting list, if provided
     136             :      */
     137     2118226 :     if (data)
     138             :     {
     139      722010 :         char       *ptr = GinGetPosting(itup);
     140             : 
     141      722010 :         memcpy(ptr, data, dataSize);
     142             :     }
     143             : 
     144             :     /*
     145             :      * Insert category byte, if needed
     146             :      */
     147     2118226 :     if (category != GIN_CAT_NORM_KEY)
     148             :     {
     149             :         Assert(IndexTupleHasNulls(itup));
     150         236 :         GinSetNullCategory(itup, ginstate, category);
     151             :     }
     152     2118226 :     return itup;
     153             : }
     154             : 
     155             : /*
     156             :  * Read item pointers from leaf entry tuple.
     157             :  *
     158             :  * Returns a palloc'd array of ItemPointers. The number of items is returned
     159             :  * in *nitems.
     160             :  */
     161             : ItemPointer
     162      444054 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
     163             :              int *nitems)
     164             : {
     165      444054 :     Pointer     ptr = GinGetPosting(itup);
     166      444054 :     int         nipd = GinGetNPosting(itup);
     167             :     ItemPointer ipd;
     168             :     int         ndecoded;
     169             : 
     170      444054 :     if (GinItupIsCompressed(itup))
     171             :     {
     172      444054 :         if (nipd > 0)
     173             :         {
     174      324060 :             ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
     175      324060 :             if (nipd != ndecoded)
     176           0 :                 elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
     177             :                      nipd, ndecoded);
     178             :         }
     179             :         else
     180             :         {
     181      119994 :             ipd = palloc(0);
     182             :         }
     183             :     }
     184             :     else
     185             :     {
     186           0 :         ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
     187           0 :         memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
     188             :     }
     189      444054 :     *nitems = nipd;
     190      444054 :     return ipd;
     191             : }
     192             : 
     193             : /*
     194             :  * Form a non-leaf entry tuple by copying the key data from the given tuple,
     195             :  * which can be either a leaf or non-leaf entry tuple.
     196             :  *
     197             :  * Any posting list in the source tuple is not copied.  The specified child
     198             :  * block number is inserted into t_tid.
     199             :  */
     200             : static IndexTuple
     201        4900 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
     202             : {
     203             :     IndexTuple  nitup;
     204             : 
     205        4900 :     if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
     206        4894 :     {
     207             :         /* Tuple contains a posting list, just copy stuff before that */
     208        4894 :         uint32      origsize = GinGetPostingOffset(itup);
     209             : 
     210        4894 :         origsize = MAXALIGN(origsize);
     211        4894 :         nitup = (IndexTuple) palloc(origsize);
     212        4894 :         memcpy(nitup, itup, origsize);
     213             :         /* ... be sure to fix the size header field ... */
     214        4894 :         nitup->t_info &= ~INDEX_SIZE_MASK;
     215        4894 :         nitup->t_info |= origsize;
     216             :     }
     217             :     else
     218             :     {
     219             :         /* Copy the tuple as-is */
     220           6 :         nitup = (IndexTuple) palloc(IndexTupleSize(itup));
     221           6 :         memcpy(nitup, itup, IndexTupleSize(itup));
     222             :     }
     223             : 
     224             :     /* Now insert the correct downlink */
     225        4900 :     GinSetDownlink(nitup, childblk);
     226             : 
     227        4900 :     return nitup;
     228             : }
     229             : 
     230             : /*
     231             :  * Entry tree is a "static", ie tuple never deletes from it,
     232             :  * so we don't use right bound, we use rightmost key instead.
     233             :  */
     234             : static IndexTuple
     235       50418 : getRightMostTuple(Page page)
     236             : {
     237       50418 :     OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
     238             : 
     239       50418 :     return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
     240             : }
     241             : 
     242             : static bool
     243      769314 : entryIsMoveRight(GinBtree btree, Page page)
     244             : {
     245             :     IndexTuple  itup;
     246             :     OffsetNumber attnum;
     247             :     Datum       key;
     248             :     GinNullCategory category;
     249             : 
     250      769314 :     if (GinPageRightMost(page))
     251      723796 :         return false;
     252             : 
     253       45518 :     itup = getRightMostTuple(page);
     254       45518 :     attnum = gintuple_get_attrnum(btree->ginstate, itup);
     255       45518 :     key = gintuple_get_key(btree->ginstate, itup, &category);
     256             : 
     257       45518 :     if (ginCompareAttEntries(btree->ginstate,
     258       45518 :                              btree->entryAttnum, btree->entryKey, btree->entryCategory,
     259             :                              attnum, key, category) > 0)
     260        1092 :         return true;
     261             : 
     262       44426 :     return false;
     263             : }
     264             : 
     265             : /*
     266             :  * Find correct tuple in non-leaf page. It supposed that
     267             :  * page correctly chosen and searching value SHOULD be on page
     268             :  */
     269             : static BlockNumber
     270      768222 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
     271             : {
     272             :     OffsetNumber low,
     273             :                 high,
     274             :                 maxoff;
     275      768222 :     IndexTuple  itup = NULL;
     276             :     int         result;
     277      768222 :     Page        page = BufferGetPage(stack->buffer);
     278             : 
     279             :     Assert(!GinPageIsLeaf(page));
     280             :     Assert(!GinPageIsData(page));
     281             : 
     282      768222 :     if (btree->fullScan)
     283             :     {
     284           0 :         stack->off = FirstOffsetNumber;
     285           0 :         stack->predictNumber *= PageGetMaxOffsetNumber(page);
     286           0 :         return btree->getLeftMostChild(btree, page);
     287             :     }
     288             : 
     289      768222 :     low = FirstOffsetNumber;
     290      768222 :     maxoff = high = PageGetMaxOffsetNumber(page);
     291             :     Assert(high >= low);
     292             : 
     293      768222 :     high++;
     294             : 
     295     5344872 :     while (high > low)
     296             :     {
     297     4576840 :         OffsetNumber mid = low + ((high - low) / 2);
     298             : 
     299     4576840 :         if (mid == maxoff && GinPageRightMost(page))
     300             :         {
     301             :             /* Right infinity */
     302      724486 :             result = -1;
     303             :         }
     304             :         else
     305             :         {
     306             :             OffsetNumber attnum;
     307             :             Datum       key;
     308             :             GinNullCategory category;
     309             : 
     310     3852354 :             itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     311     3852354 :             attnum = gintuple_get_attrnum(btree->ginstate, itup);
     312     3852354 :             key = gintuple_get_key(btree->ginstate, itup, &category);
     313     3852354 :             result = ginCompareAttEntries(btree->ginstate,
     314     3852354 :                                           btree->entryAttnum,
     315             :                                           btree->entryKey,
     316     3852354 :                                           btree->entryCategory,
     317             :                                           attnum, key, category);
     318             :         }
     319             : 
     320     4576840 :         if (result == 0)
     321             :         {
     322         190 :             stack->off = mid;
     323             :             Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     324         190 :             return GinGetDownlink(itup);
     325             :         }
     326     4576650 :         else if (result > 0)
     327     3551940 :             low = mid + 1;
     328             :         else
     329     1024710 :             high = mid;
     330             :     }
     331             : 
     332             :     Assert(high >= FirstOffsetNumber && high <= maxoff);
     333             : 
     334      768032 :     stack->off = high;
     335      768032 :     itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
     336             :     Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     337      768032 :     return GinGetDownlink(itup);
     338             : }
     339             : 
     340             : /*
     341             :  * Searches correct position for value on leaf page.
     342             :  * Page should be correctly chosen.
     343             :  * Returns true if value found on page.
     344             :  */
     345             : static bool
     346      778186 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
     347             : {
     348      778186 :     Page        page = BufferGetPage(stack->buffer);
     349             :     OffsetNumber low,
     350             :                 high;
     351             : 
     352             :     Assert(GinPageIsLeaf(page));
     353             :     Assert(!GinPageIsData(page));
     354             : 
     355      778186 :     if (btree->fullScan)
     356             :     {
     357           0 :         stack->off = FirstOffsetNumber;
     358           0 :         return true;
     359             :     }
     360             : 
     361      778186 :     low = FirstOffsetNumber;
     362      778186 :     high = PageGetMaxOffsetNumber(page);
     363             : 
     364      778186 :     if (high < low)
     365             :     {
     366         510 :         stack->off = FirstOffsetNumber;
     367         510 :         return false;
     368             :     }
     369             : 
     370      777676 :     high++;
     371             : 
     372     6007072 :     while (high > low)
     373             :     {
     374     5316054 :         OffsetNumber mid = low + ((high - low) / 2);
     375             :         IndexTuple  itup;
     376             :         OffsetNumber attnum;
     377             :         Datum       key;
     378             :         GinNullCategory category;
     379             :         int         result;
     380             : 
     381     5316054 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     382     5316054 :         attnum = gintuple_get_attrnum(btree->ginstate, itup);
     383     5316054 :         key = gintuple_get_key(btree->ginstate, itup, &category);
     384     5316054 :         result = ginCompareAttEntries(btree->ginstate,
     385     5316054 :                                       btree->entryAttnum,
     386             :                                       btree->entryKey,
     387     5316054 :                                       btree->entryCategory,
     388             :                                       attnum, key, category);
     389     5316054 :         if (result == 0)
     390             :         {
     391       86658 :             stack->off = mid;
     392       86658 :             return true;
     393             :         }
     394     5229396 :         else if (result > 0)
     395     4981034 :             low = mid + 1;
     396             :         else
     397      248362 :             high = mid;
     398             :     }
     399             : 
     400      691018 :     stack->off = high;
     401      691018 :     return false;
     402             : }
     403             : 
     404             : static OffsetNumber
     405        4652 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
     406             : {
     407             :     OffsetNumber i,
     408        4652 :                 maxoff = PageGetMaxOffsetNumber(page);
     409             :     IndexTuple  itup;
     410             : 
     411             :     Assert(!GinPageIsLeaf(page));
     412             :     Assert(!GinPageIsData(page));
     413             : 
     414             :     /* if page isn't changed, we returns storedOff */
     415        4652 :     if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
     416             :     {
     417        4652 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
     418        4652 :         if (GinGetDownlink(itup) == blkno)
     419        4652 :             return storedOff;
     420             : 
     421             :         /*
     422             :          * we hope, that needed pointer goes to right. It's true if there
     423             :          * wasn't a deletion
     424             :          */
     425           0 :         for (i = storedOff + 1; i <= maxoff; i++)
     426             :         {
     427           0 :             itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
     428           0 :             if (GinGetDownlink(itup) == blkno)
     429           0 :                 return i;
     430             :         }
     431           0 :         maxoff = storedOff - 1;
     432             :     }
     433             : 
     434             :     /* last chance */
     435           0 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     436             :     {
     437           0 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
     438           0 :         if (GinGetDownlink(itup) == blkno)
     439           0 :             return i;
     440             :     }
     441             : 
     442           0 :     return InvalidOffsetNumber;
     443             : }
     444             : 
     445             : static BlockNumber
     446           0 : entryGetLeftMostPage(GinBtree btree, Page page)
     447             : {
     448             :     IndexTuple  itup;
     449             : 
     450             :     Assert(!GinPageIsLeaf(page));
     451             :     Assert(!GinPageIsData(page));
     452             :     Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
     453             : 
     454           0 :     itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
     455           0 :     return GinGetDownlink(itup);
     456             : }
     457             : 
     458             : static bool
     459      726742 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
     460             :                    GinBtreeEntryInsertData *insertData)
     461             : {
     462      726742 :     Size        releasedsz = 0;
     463             :     Size        addedsz;
     464      726742 :     Page        page = BufferGetPage(buf);
     465             : 
     466             :     Assert(insertData->entry);
     467             :     Assert(!GinPageIsData(page));
     468             : 
     469      726742 :     if (insertData->isDelete)
     470             :     {
     471       32674 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     472             : 
     473       32674 :         releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     474             :     }
     475             : 
     476      726742 :     addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
     477             : 
     478      726742 :     if (PageGetFreeSpace(page) + releasedsz >= addedsz)
     479      721966 :         return true;
     480             : 
     481        4776 :     return false;
     482             : }
     483             : 
     484             : /*
     485             :  * Delete tuple on leaf page if tuples existed and we
     486             :  * should update it, update old child blkno to new right page
     487             :  * if child split occurred
     488             :  */
     489             : static void
     490      726742 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
     491             :                  GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
     492             : {
     493             :     Assert(insertData->entry);
     494             :     Assert(!GinPageIsData(page));
     495             : 
     496      726742 :     if (insertData->isDelete)
     497             :     {
     498             :         Assert(GinPageIsLeaf(page));
     499       32674 :         PageIndexTupleDelete(page, off);
     500             :     }
     501             : 
     502      726742 :     if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
     503             :     {
     504        4652 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     505             : 
     506        4652 :         GinSetDownlink(itup, updateblkno);
     507             :     }
     508      726742 : }
     509             : 
     510             : /*
     511             :  * Prepare to insert data on an entry page.
     512             :  *
     513             :  * If it will fit, return GPTP_INSERT after doing whatever setup is needed
     514             :  * before we enter the insertion critical section.  *ptp_workspace can be
     515             :  * set to pass information along to the execPlaceToPage function.
     516             :  *
     517             :  * If it won't fit, perform a page split and return two temporary page
     518             :  * images into *newlpage and *newrpage, with result GPTP_SPLIT.
     519             :  *
     520             :  * In neither case should the given page buffer be modified here.
     521             :  *
     522             :  * Note: on insertion to an internal node, in addition to inserting the given
     523             :  * item, the downlink of the existing item at stack->off will be updated to
     524             :  * point to updateblkno.
     525             :  */
     526             : static GinPlaceToPageRC
     527      726742 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     528             :                       void *insertPayload, BlockNumber updateblkno,
     529             :                       void **ptp_workspace,
     530             :                       Page *newlpage, Page *newrpage)
     531             : {
     532      726742 :     GinBtreeEntryInsertData *insertData = insertPayload;
     533      726742 :     OffsetNumber off = stack->off;
     534             : 
     535             :     /* If it doesn't fit, deal with split case */
     536      726742 :     if (!entryIsEnoughSpace(btree, buf, off, insertData))
     537             :     {
     538        4776 :         entrySplitPage(btree, buf, stack, insertData, updateblkno,
     539             :                        newlpage, newrpage);
     540        4776 :         return GPTP_SPLIT;
     541             :     }
     542             : 
     543             :     /* Else, we're ready to proceed with insertion */
     544      721966 :     return GPTP_INSERT;
     545             : }
     546             : 
     547             : /*
     548             :  * Perform data insertion after beginPlaceToPage has decided it will fit.
     549             :  *
     550             :  * This is invoked within a critical section, and XLOG record creation (if
     551             :  * needed) is already started.  The target buffer is registered in slot 0.
     552             :  */
     553             : static void
     554      721966 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     555             :                      void *insertPayload, BlockNumber updateblkno,
     556             :                      void *ptp_workspace)
     557             : {
     558      721966 :     GinBtreeEntryInsertData *insertData = insertPayload;
     559      721966 :     Page        page = BufferGetPage(buf);
     560      721966 :     OffsetNumber off = stack->off;
     561             :     OffsetNumber placed;
     562             : 
     563      721966 :     entryPreparePage(btree, page, off, insertData, updateblkno);
     564             : 
     565      721966 :     placed = PageAddItem(page,
     566             :                          (Item) insertData->entry,
     567             :                          IndexTupleSize(insertData->entry),
     568             :                          off, false, false);
     569      721966 :     if (placed != off)
     570           0 :         elog(ERROR, "failed to add item to index page in \"%s\"",
     571             :              RelationGetRelationName(btree->index));
     572             : 
     573      721966 :     MarkBufferDirty(buf);
     574             : 
     575      721966 :     if (RelationNeedsWAL(btree->index) && !btree->isBuild)
     576             :     {
     577             :         /*
     578             :          * This must be static, because it has to survive until XLogInsert,
     579             :          * and we can't palloc here.  Ugly, but the XLogInsert infrastructure
     580             :          * isn't reentrant anyway.
     581             :          */
     582             :         static ginxlogInsertEntry data;
     583             : 
     584      332898 :         data.isDelete = insertData->isDelete;
     585      332898 :         data.offset = off;
     586             : 
     587      332898 :         XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
     588      332898 :         XLogRegisterBufData(0, (char *) &data,
     589             :                             offsetof(ginxlogInsertEntry, tuple));
     590      332898 :         XLogRegisterBufData(0, (char *) insertData->entry,
     591      332898 :                             IndexTupleSize(insertData->entry));
     592             :     }
     593      721966 : }
     594             : 
     595             : /*
     596             :  * Split entry page and insert new data.
     597             :  *
     598             :  * Returns new temp pages to *newlpage and *newrpage.
     599             :  * The original buffer is left untouched.
     600             :  */
     601             : static void
     602        4776 : entrySplitPage(GinBtree btree, Buffer origbuf,
     603             :                GinBtreeStack *stack,
     604             :                GinBtreeEntryInsertData *insertData,
     605             :                BlockNumber updateblkno,
     606             :                Page *newlpage, Page *newrpage)
     607             : {
     608        4776 :     OffsetNumber off = stack->off;
     609             :     OffsetNumber i,
     610             :                 maxoff,
     611        4776 :                 separator = InvalidOffsetNumber;
     612        4776 :     Size        totalsize = 0;
     613        4776 :     Size        lsize = 0,
     614             :                 size;
     615             :     char       *ptr;
     616             :     IndexTuple  itup;
     617             :     Page        page;
     618        4776 :     Page        lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     619        4776 :     Page        rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     620        4776 :     Size        pageSize = PageGetPageSize(lpage);
     621             :     PGAlignedBlock tupstore[2]; /* could need 2 pages' worth of tuples */
     622             : 
     623        4776 :     entryPreparePage(btree, lpage, off, insertData, updateblkno);
     624             : 
     625             :     /*
     626             :      * First, append all the existing tuples and the new tuple we're inserting
     627             :      * one after another in a temporary workspace.
     628             :      */
     629        4776 :     maxoff = PageGetMaxOffsetNumber(lpage);
     630        4776 :     ptr = tupstore[0].data;
     631     1326484 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     632             :     {
     633     1321708 :         if (i == off)
     634             :         {
     635           4 :             size = MAXALIGN(IndexTupleSize(insertData->entry));
     636           4 :             memcpy(ptr, insertData->entry, size);
     637           4 :             ptr += size;
     638           4 :             totalsize += size + sizeof(ItemIdData);
     639             :         }
     640             : 
     641     1321708 :         itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
     642     1321708 :         size = MAXALIGN(IndexTupleSize(itup));
     643     1321708 :         memcpy(ptr, itup, size);
     644     1321708 :         ptr += size;
     645     1321708 :         totalsize += size + sizeof(ItemIdData);
     646             :     }
     647             : 
     648        4776 :     if (off == maxoff + 1)
     649             :     {
     650        4772 :         size = MAXALIGN(IndexTupleSize(insertData->entry));
     651        4772 :         memcpy(ptr, insertData->entry, size);
     652        4772 :         ptr += size;
     653        4772 :         totalsize += size + sizeof(ItemIdData);
     654             :     }
     655             : 
     656             :     /*
     657             :      * Initialize the left and right pages, and copy all the tuples back to
     658             :      * them.
     659             :      */
     660        4776 :     GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
     661        4776 :     GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
     662             : 
     663        4776 :     ptr = tupstore[0].data;
     664        4776 :     maxoff++;
     665        4776 :     lsize = 0;
     666             : 
     667        4776 :     page = lpage;
     668     1331260 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     669             :     {
     670     1326484 :         itup = (IndexTuple) ptr;
     671             : 
     672             :         /*
     673             :          * Decide where to split.  We try to equalize the pages' total data
     674             :          * size, not number of tuples.
     675             :          */
     676     1326484 :         if (lsize > totalsize / 2)
     677             :         {
     678      659340 :             if (separator == InvalidOffsetNumber)
     679        4776 :                 separator = i - 1;
     680      659340 :             page = rpage;
     681             :         }
     682             :         else
     683             :         {
     684      667144 :             lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     685             :         }
     686             : 
     687     1326484 :         if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     688           0 :             elog(ERROR, "failed to add item to index page in \"%s\"",
     689             :                  RelationGetRelationName(btree->index));
     690     1326484 :         ptr += MAXALIGN(IndexTupleSize(itup));
     691             :     }
     692             : 
     693             :     /* return temp pages to caller */
     694        4776 :     *newlpage = lpage;
     695        4776 :     *newrpage = rpage;
     696        4776 : }
     697             : 
     698             : /*
     699             :  * Construct insertion payload for inserting the downlink for given buffer.
     700             :  */
     701             : static void *
     702        4652 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
     703             : {
     704             :     GinBtreeEntryInsertData *insertData;
     705        4652 :     Page        lpage = BufferGetPage(lbuf);
     706        4652 :     BlockNumber lblkno = BufferGetBlockNumber(lbuf);
     707             :     IndexTuple  itup;
     708             : 
     709        4652 :     itup = getRightMostTuple(lpage);
     710             : 
     711        4652 :     insertData = palloc(sizeof(GinBtreeEntryInsertData));
     712        4652 :     insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
     713        4652 :     insertData->isDelete = false;
     714             : 
     715        4652 :     return insertData;
     716             : }
     717             : 
     718             : /*
     719             :  * Fills new root by rightest values from child.
     720             :  * Also called from ginxlog, should not use btree
     721             :  */
     722             : void
     723         124 : ginEntryFillRoot(GinBtree btree, Page root,
     724             :                  BlockNumber lblkno, Page lpage,
     725             :                  BlockNumber rblkno, Page rpage)
     726             : {
     727             :     IndexTuple  itup;
     728             : 
     729         124 :     itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
     730         124 :     if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     731           0 :         elog(ERROR, "failed to add item to index root page");
     732         124 :     pfree(itup);
     733             : 
     734         124 :     itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
     735         124 :     if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     736           0 :         elog(ERROR, "failed to add item to index root page");
     737         124 :     pfree(itup);
     738         124 : }
     739             : 
     740             : /*
     741             :  * Set up GinBtree for entry page access
     742             :  *
     743             :  * Note: during WAL recovery, there may be no valid data in ginstate
     744             :  * other than a faked-up Relation pointer; the key datum is bogus too.
     745             :  */
     746             : void
     747      778186 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
     748             :                     Datum key, GinNullCategory category,
     749             :                     GinState *ginstate)
     750             : {
     751      778186 :     memset(btree, 0, sizeof(GinBtreeData));
     752             : 
     753      778186 :     btree->index = ginstate->index;
     754      778186 :     btree->rootBlkno = GIN_ROOT_BLKNO;
     755      778186 :     btree->ginstate = ginstate;
     756             : 
     757      778186 :     btree->findChildPage = entryLocateEntry;
     758      778186 :     btree->getLeftMostChild = entryGetLeftMostPage;
     759      778186 :     btree->isMoveRight = entryIsMoveRight;
     760      778186 :     btree->findItem = entryLocateLeafEntry;
     761      778186 :     btree->findChildPtr = entryFindChildPtr;
     762      778186 :     btree->beginPlaceToPage = entryBeginPlaceToPage;
     763      778186 :     btree->execPlaceToPage = entryExecPlaceToPage;
     764      778186 :     btree->fillRoot = ginEntryFillRoot;
     765      778186 :     btree->prepareDownlink = entryPrepareDownlink;
     766             : 
     767      778186 :     btree->isData = false;
     768      778186 :     btree->fullScan = false;
     769      778186 :     btree->isBuild = false;
     770             : 
     771      778186 :     btree->entryAttnum = attnum;
     772      778186 :     btree->entryKey = key;
     773      778186 :     btree->entryCategory = category;
     774      778186 : }

Generated by: LCOV version 1.14