LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginentrypage.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.5 % 275 249
Test Date: 2026-02-17 17:20:33 Functions: 94.1 % 17 16
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-2026, 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      1161935 : 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      1161935 :     if (ginstate->oneCol)
      56              :     {
      57       561000 :         datums[0] = key;
      58       561000 :         isnull[0] = (category != GIN_CAT_NORM_KEY);
      59              :     }
      60              :     else
      61              :     {
      62       600935 :         datums[0] = UInt16GetDatum(attnum);
      63       600935 :         isnull[0] = false;
      64       600935 :         datums[1] = key;
      65       600935 :         isnull[1] = (category != GIN_CAT_NORM_KEY);
      66              :     }
      67              : 
      68      1161935 :     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      1161935 :     newsize = IndexTupleSize(itup);
      81              : 
      82      1161935 :     if (IndexTupleHasNulls(itup))
      83              :     {
      84              :         uint32      minsize;
      85              : 
      86              :         Assert(category != GIN_CAT_NORM_KEY);
      87          119 :         minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
      88          119 :         newsize = Max(newsize, minsize);
      89              :     }
      90              : 
      91      1161935 :     newsize = SHORTALIGN(newsize);
      92              : 
      93      1161935 :     GinSetPostingOffset(itup, newsize);
      94      1161935 :     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      1161935 :     newsize += dataSize;
     101              : 
     102      1161935 :     newsize = MAXALIGN(newsize);
     103              : 
     104      1161935 :     if (newsize > GinMaxItemSize)
     105              :     {
     106            5 :         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            5 :         pfree(itup);
     113            5 :         return NULL;
     114              :     }
     115              : 
     116              :     /*
     117              :      * Resize tuple if needed
     118              :      */
     119      1161930 :     if (newsize != IndexTupleSize(itup))
     120              :     {
     121       461177 :         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       461177 :         memset((char *) itup + IndexTupleSize(itup),
     128       461177 :                0, newsize - IndexTupleSize(itup));
     129              :         /* set new size in tuple header */
     130       461177 :         itup->t_info &= ~INDEX_SIZE_MASK;
     131       461177 :         itup->t_info |= newsize;
     132              :     }
     133              : 
     134              :     /*
     135              :      * Copy in the posting list, if provided
     136              :      */
     137      1161930 :     if (data)
     138              :     {
     139       461174 :         char       *ptr = GinGetPosting(itup);
     140              : 
     141       461174 :         memcpy(ptr, data, dataSize);
     142              :     }
     143              : 
     144              :     /*
     145              :      * Insert category byte, if needed
     146              :      */
     147      1161930 :     if (category != GIN_CAT_NORM_KEY)
     148              :     {
     149              :         Assert(IndexTupleHasNulls(itup));
     150          119 :         GinSetNullCategory(itup, ginstate, category);
     151              :     }
     152      1161930 :     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       320661 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
     163              :              int *nitems)
     164              : {
     165       320661 :     Pointer     ptr = GinGetPosting(itup);
     166       320661 :     int         nipd = GinGetNPosting(itup);
     167              :     ItemPointer ipd;
     168              :     int         ndecoded;
     169              : 
     170       320661 :     if (GinItupIsCompressed(itup))
     171              :     {
     172       320661 :         if (nipd > 0)
     173              :         {
     174       260664 :             ipd = ginPostingListDecode(ptr, &ndecoded);
     175       260664 :             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        59997 :             ipd = palloc(0);
     182              :         }
     183              :     }
     184              :     else
     185              :     {
     186            0 :         ipd = palloc_array(ItemPointerData, nipd);
     187            0 :         memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
     188              :     }
     189       320661 :     *nitems = nipd;
     190       320661 :     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         2575 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
     202              : {
     203              :     IndexTuple  nitup;
     204              : 
     205         2575 :     if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
     206         2572 :     {
     207              :         /* Tuple contains a posting list, just copy stuff before that */
     208         2572 :         uint32      origsize = GinGetPostingOffset(itup);
     209              : 
     210         2572 :         origsize = MAXALIGN(origsize);
     211         2572 :         nitup = (IndexTuple) palloc(origsize);
     212         2572 :         memcpy(nitup, itup, origsize);
     213              :         /* ... be sure to fix the size header field ... */
     214         2572 :         nitup->t_info &= ~INDEX_SIZE_MASK;
     215         2572 :         nitup->t_info |= origsize;
     216              :     }
     217              :     else
     218              :     {
     219              :         /* Copy the tuple as-is */
     220            3 :         nitup = (IndexTuple) palloc(IndexTupleSize(itup));
     221            3 :         memcpy(nitup, itup, IndexTupleSize(itup));
     222              :     }
     223              : 
     224              :     /* Now insert the correct downlink */
     225         2575 :     GinSetDownlink(nitup, childblk);
     226              : 
     227         2575 :     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       110627 : getRightMostTuple(Page page)
     236              : {
     237       110627 :     OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
     238              : 
     239       110627 :     return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
     240              : }
     241              : 
     242              : static bool
     243       483395 : entryIsMoveRight(GinBtree btree, Page page)
     244              : {
     245              :     IndexTuple  itup;
     246              :     OffsetNumber attnum;
     247              :     Datum       key;
     248              :     GinNullCategory category;
     249              : 
     250       483395 :     if (GinPageRightMost(page))
     251       375343 :         return false;
     252              : 
     253       108052 :     itup = getRightMostTuple(page);
     254       108052 :     attnum = gintuple_get_attrnum(btree->ginstate, itup);
     255       108052 :     key = gintuple_get_key(btree->ginstate, itup, &category);
     256              : 
     257       108052 :     if (ginCompareAttEntries(btree->ginstate,
     258       108052 :                              btree->entryAttnum, btree->entryKey, btree->entryCategory,
     259              :                              attnum, key, category) > 0)
     260          546 :         return true;
     261              : 
     262       107506 :     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       482849 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
     271              : {
     272              :     OffsetNumber low,
     273              :                 high,
     274              :                 maxoff;
     275       482849 :     IndexTuple  itup = NULL;
     276              :     int         result;
     277       482849 :     Page        page = BufferGetPage(stack->buffer);
     278              : 
     279              :     Assert(!GinPageIsLeaf(page));
     280              :     Assert(!GinPageIsData(page));
     281              : 
     282       482849 :     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       482849 :     low = FirstOffsetNumber;
     290       482849 :     maxoff = high = PageGetMaxOffsetNumber(page);
     291              :     Assert(high >= low);
     292              : 
     293       482849 :     high++;
     294              : 
     295      3119761 :     while (high > low)
     296              :     {
     297      2641239 :         OffsetNumber mid = low + ((high - low) / 2);
     298              : 
     299      2641239 :         if (mid == maxoff && GinPageRightMost(page))
     300              :         {
     301              :             /* Right infinity */
     302       378859 :             result = -1;
     303              :         }
     304              :         else
     305              :         {
     306              :             OffsetNumber attnum;
     307              :             Datum       key;
     308              :             GinNullCategory category;
     309              : 
     310      2262380 :             itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     311      2262380 :             attnum = gintuple_get_attrnum(btree->ginstate, itup);
     312      2262380 :             key = gintuple_get_key(btree->ginstate, itup, &category);
     313      2262380 :             result = ginCompareAttEntries(btree->ginstate,
     314      2262380 :                                           btree->entryAttnum,
     315              :                                           btree->entryKey,
     316      2262380 :                                           btree->entryCategory,
     317              :                                           attnum, key, category);
     318              :         }
     319              : 
     320      2641239 :         if (result == 0)
     321              :         {
     322         4327 :             stack->off = mid;
     323              :             Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     324         4327 :             return GinGetDownlink(itup);
     325              :         }
     326      2636912 :         else if (result > 0)
     327      1920114 :             low = mid + 1;
     328              :         else
     329       716798 :             high = mid;
     330              :     }
     331              : 
     332              :     Assert(high >= FirstOffsetNumber && high <= maxoff);
     333              : 
     334       478522 :     stack->off = high;
     335       478522 :     itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
     336              :     Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     337       478522 :     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       489461 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
     347              : {
     348       489461 :     Page        page = BufferGetPage(stack->buffer);
     349              :     OffsetNumber low,
     350              :                 high;
     351              : 
     352              :     Assert(GinPageIsLeaf(page));
     353              :     Assert(!GinPageIsData(page));
     354              : 
     355       489461 :     if (btree->fullScan)
     356              :     {
     357            0 :         stack->off = FirstOffsetNumber;
     358            0 :         return true;
     359              :     }
     360              : 
     361       489461 :     low = FirstOffsetNumber;
     362       489461 :     high = PageGetMaxOffsetNumber(page);
     363              : 
     364       489461 :     if (high < low)
     365              :     {
     366          302 :         stack->off = FirstOffsetNumber;
     367          302 :         return false;
     368              :     }
     369              : 
     370       489159 :     high++;
     371              : 
     372      3404352 :     while (high > low)
     373              :     {
     374      3056761 :         OffsetNumber mid = low + ((high - low) / 2);
     375              :         IndexTuple  itup;
     376              :         OffsetNumber attnum;
     377              :         Datum       key;
     378              :         GinNullCategory category;
     379              :         int         result;
     380              : 
     381      3056761 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     382      3056761 :         attnum = gintuple_get_attrnum(btree->ginstate, itup);
     383      3056761 :         key = gintuple_get_key(btree->ginstate, itup, &category);
     384      3056761 :         result = ginCompareAttEntries(btree->ginstate,
     385      3056761 :                                       btree->entryAttnum,
     386              :                                       btree->entryKey,
     387      3056761 :                                       btree->entryCategory,
     388              :                                       attnum, key, category);
     389      3056761 :         if (result == 0)
     390              :         {
     391       141568 :             stack->off = mid;
     392       141568 :             return true;
     393              :         }
     394      2915193 :         else if (result > 0)
     395      2635352 :             low = mid + 1;
     396              :         else
     397       279841 :             high = mid;
     398              :     }
     399              : 
     400       347591 :     stack->off = high;
     401       347591 :     return false;
     402              : }
     403              : 
     404              : static OffsetNumber
     405         2437 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
     406              : {
     407              :     OffsetNumber i,
     408         2437 :                 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         2437 :     if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
     416              :     {
     417         2437 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
     418         2437 :         if (GinGetDownlink(itup) == blkno)
     419         2437 :             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       463658 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
     460              :                    GinBtreeEntryInsertData *insertData)
     461              : {
     462       463658 :     Size        releasedsz = 0;
     463              :     Size        addedsz;
     464       463658 :     Page        page = BufferGetPage(buf);
     465              : 
     466              :     Assert(insertData->entry);
     467              :     Assert(!GinPageIsData(page));
     468              : 
     469       463658 :     if (insertData->isDelete)
     470              :     {
     471       114433 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     472              : 
     473       114433 :         releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     474              :     }
     475              : 
     476       463658 :     addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
     477              : 
     478       463658 :     if (PageGetFreeSpace(page) + releasedsz >= addedsz)
     479       461152 :         return true;
     480              : 
     481         2506 :     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       463658 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
     491              :                  GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
     492              : {
     493              :     Assert(insertData->entry);
     494              :     Assert(!GinPageIsData(page));
     495              : 
     496       463658 :     if (insertData->isDelete)
     497              :     {
     498              :         Assert(GinPageIsLeaf(page));
     499       114433 :         PageIndexTupleDelete(page, off);
     500              :     }
     501              : 
     502       463658 :     if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
     503              :     {
     504         2437 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     505              : 
     506         2437 :         GinSetDownlink(itup, updateblkno);
     507              :     }
     508       463658 : }
     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       463658 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     528              :                       void *insertPayload, BlockNumber updateblkno,
     529              :                       void **ptp_workspace,
     530              :                       Page *newlpage, Page *newrpage)
     531              : {
     532       463658 :     GinBtreeEntryInsertData *insertData = insertPayload;
     533       463658 :     OffsetNumber off = stack->off;
     534              : 
     535              :     /* If it doesn't fit, deal with split case */
     536       463658 :     if (!entryIsEnoughSpace(btree, buf, off, insertData))
     537              :     {
     538         2506 :         entrySplitPage(btree, buf, stack, insertData, updateblkno,
     539              :                        newlpage, newrpage);
     540         2506 :         return GPTP_SPLIT;
     541              :     }
     542              : 
     543              :     /* Else, we're ready to proceed with insertion */
     544       461152 :     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       461152 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     555              :                      void *insertPayload, BlockNumber updateblkno,
     556              :                      void *ptp_workspace)
     557              : {
     558       461152 :     GinBtreeEntryInsertData *insertData = insertPayload;
     559       461152 :     Page        page = BufferGetPage(buf);
     560       461152 :     OffsetNumber off = stack->off;
     561              :     OffsetNumber placed;
     562              : 
     563       461152 :     entryPreparePage(btree, page, off, insertData, updateblkno);
     564              : 
     565       461152 :     placed = PageAddItem(page,
     566              :                          insertData->entry,
     567              :                          IndexTupleSize(insertData->entry),
     568              :                          off, false, false);
     569       461152 :     if (placed != off)
     570            0 :         elog(ERROR, "failed to add item to index page in \"%s\"",
     571              :              RelationGetRelationName(btree->index));
     572              : 
     573       461152 :     MarkBufferDirty(buf);
     574              : 
     575       461152 :     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       264845 :         data.isDelete = insertData->isDelete;
     585       264845 :         data.offset = off;
     586              : 
     587       264845 :         XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
     588       264845 :         XLogRegisterBufData(0, &data,
     589              :                             offsetof(ginxlogInsertEntry, tuple));
     590       264845 :         XLogRegisterBufData(0, insertData->entry,
     591       264845 :                             IndexTupleSize(insertData->entry));
     592              :     }
     593       461152 : }
     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         2506 : entrySplitPage(GinBtree btree, Buffer origbuf,
     603              :                GinBtreeStack *stack,
     604              :                GinBtreeEntryInsertData *insertData,
     605              :                BlockNumber updateblkno,
     606              :                Page *newlpage, Page *newrpage)
     607              : {
     608         2506 :     OffsetNumber off = stack->off;
     609              :     OffsetNumber i,
     610              :                 maxoff,
     611         2506 :                 separator = InvalidOffsetNumber;
     612         2506 :     Size        totalsize = 0;
     613         2506 :     Size        lsize = 0,
     614              :                 size;
     615              :     char       *ptr;
     616              :     IndexTuple  itup;
     617              :     Page        page;
     618         2506 :     Page        lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     619         2506 :     Page        rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     620         2506 :     Size        pageSize = PageGetPageSize(lpage);
     621              :     PGAlignedBlock tupstore[2]; /* could need 2 pages' worth of tuples */
     622              : 
     623         2506 :     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         2506 :     maxoff = PageGetMaxOffsetNumber(lpage);
     630         2506 :     ptr = tupstore[0].data;
     631       666623 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     632              :     {
     633       664117 :         if (i == off)
     634              :         {
     635           27 :             size = MAXALIGN(IndexTupleSize(insertData->entry));
     636           27 :             memcpy(ptr, insertData->entry, size);
     637           27 :             ptr += size;
     638           27 :             totalsize += size + sizeof(ItemIdData);
     639              :         }
     640              : 
     641       664117 :         itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
     642       664117 :         size = MAXALIGN(IndexTupleSize(itup));
     643       664117 :         memcpy(ptr, itup, size);
     644       664117 :         ptr += size;
     645       664117 :         totalsize += size + sizeof(ItemIdData);
     646              :     }
     647              : 
     648         2506 :     if (off == maxoff + 1)
     649              :     {
     650         2479 :         size = MAXALIGN(IndexTupleSize(insertData->entry));
     651         2479 :         memcpy(ptr, insertData->entry, size);
     652         2479 :         ptr += size;
     653         2479 :         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         2506 :     GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
     661         2506 :     GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
     662              : 
     663         2506 :     ptr = tupstore[0].data;
     664         2506 :     maxoff++;
     665         2506 :     lsize = 0;
     666              : 
     667         2506 :     page = lpage;
     668       669129 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     669              :     {
     670       666623 :         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       666623 :         if (lsize > totalsize / 2)
     677              :         {
     678       331303 :             if (separator == InvalidOffsetNumber)
     679         2506 :                 separator = i - 1;
     680       331303 :             page = rpage;
     681              :         }
     682              :         else
     683              :         {
     684       335320 :             lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     685              :         }
     686              : 
     687       666623 :         if (PageAddItem(page, 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       666623 :         ptr += MAXALIGN(IndexTupleSize(itup));
     691              :     }
     692              : 
     693              :     /* return temp pages to caller */
     694         2506 :     *newlpage = lpage;
     695         2506 :     *newrpage = rpage;
     696         2506 : }
     697              : 
     698              : /*
     699              :  * Construct insertion payload for inserting the downlink for given buffer.
     700              :  */
     701              : static void *
     702         2437 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
     703              : {
     704              :     GinBtreeEntryInsertData *insertData;
     705         2437 :     Page        lpage = BufferGetPage(lbuf);
     706         2437 :     BlockNumber lblkno = BufferGetBlockNumber(lbuf);
     707              :     IndexTuple  itup;
     708              : 
     709         2437 :     itup = getRightMostTuple(lpage);
     710              : 
     711         2437 :     insertData = palloc_object(GinBtreeEntryInsertData);
     712         2437 :     insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
     713         2437 :     insertData->isDelete = false;
     714              : 
     715         2437 :     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           69 : ginEntryFillRoot(GinBtree btree, Page root,
     724              :                  BlockNumber lblkno, Page lpage,
     725              :                  BlockNumber rblkno, Page rpage)
     726              : {
     727              :     IndexTuple  itup;
     728              : 
     729           69 :     itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
     730           69 :     if (PageAddItem(root, itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     731            0 :         elog(ERROR, "failed to add item to index root page");
     732           69 :     pfree(itup);
     733              : 
     734           69 :     itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
     735           69 :     if (PageAddItem(root, itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     736            0 :         elog(ERROR, "failed to add item to index root page");
     737           69 :     pfree(itup);
     738           69 : }
     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       489461 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
     748              :                     Datum key, GinNullCategory category,
     749              :                     GinState *ginstate)
     750              : {
     751       489461 :     memset(btree, 0, sizeof(GinBtreeData));
     752              : 
     753       489461 :     btree->index = ginstate->index;
     754       489461 :     btree->rootBlkno = GIN_ROOT_BLKNO;
     755       489461 :     btree->ginstate = ginstate;
     756              : 
     757       489461 :     btree->findChildPage = entryLocateEntry;
     758       489461 :     btree->getLeftMostChild = entryGetLeftMostPage;
     759       489461 :     btree->isMoveRight = entryIsMoveRight;
     760       489461 :     btree->findItem = entryLocateLeafEntry;
     761       489461 :     btree->findChildPtr = entryFindChildPtr;
     762       489461 :     btree->beginPlaceToPage = entryBeginPlaceToPage;
     763       489461 :     btree->execPlaceToPage = entryExecPlaceToPage;
     764       489461 :     btree->fillRoot = ginEntryFillRoot;
     765       489461 :     btree->prepareDownlink = entryPrepareDownlink;
     766              : 
     767       489461 :     btree->isData = false;
     768       489461 :     btree->fullScan = false;
     769       489461 :     btree->isBuild = false;
     770              : 
     771       489461 :     btree->entryAttnum = attnum;
     772       489461 :     btree->entryKey = key;
     773       489461 :     btree->entryCategory = category;
     774       489461 : }
        

Generated by: LCOV version 2.0-1