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

Generated by: LCOV version 1.13