LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginentrypage.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 256 293 87.4 %
Date: 2017-09-29 15:12:54 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-2017, 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      124156 : 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      124156 :     if (ginstate->oneCol)
      57             :     {
      58      123861 :         datums[0] = key;
      59      123861 :         isnull[0] = (category != GIN_CAT_NORM_KEY);
      60             :     }
      61             :     else
      62             :     {
      63         295 :         datums[0] = UInt16GetDatum(attnum);
      64         295 :         isnull[0] = false;
      65         295 :         datums[1] = key;
      66         295 :         isnull[1] = (category != GIN_CAT_NORM_KEY);
      67             :     }
      68             : 
      69      124156 :     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      124156 :     newsize = IndexTupleSize(itup);
      82             : 
      83      124156 :     if (IndexTupleHasNulls(itup))
      84             :     {
      85             :         uint32      minsize;
      86             : 
      87          20 :         Assert(category != GIN_CAT_NORM_KEY);
      88          20 :         minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
      89          20 :         newsize = Max(newsize, minsize);
      90             :     }
      91             : 
      92      124156 :     newsize = SHORTALIGN(newsize);
      93             : 
      94      124156 :     GinSetPostingOffset(itup, newsize);
      95      124156 :     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      124156 :     newsize += dataSize;
     102             : 
     103      124156 :     newsize = MAXALIGN(newsize);
     104             : 
     105      124156 :     if (newsize > GinMaxItemSize)
     106             :     {
     107           4 :         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           4 :         pfree(itup);
     114           4 :         return NULL;
     115             :     }
     116             : 
     117             :     /*
     118             :      * Resize tuple if needed
     119             :      */
     120      124152 :     if (newsize != IndexTupleSize(itup))
     121             :     {
     122       39144 :         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       39144 :         memset((char *) itup + IndexTupleSize(itup),
     129       39144 :                0, newsize - IndexTupleSize(itup));
     130             :         /* set new size in tuple header */
     131       39144 :         itup->t_info &= ~INDEX_SIZE_MASK;
     132       39144 :         itup->t_info |= newsize;
     133             :     }
     134             : 
     135             :     /*
     136             :      * Copy in the posting list, if provided
     137             :      */
     138      124152 :     if (data)
     139             :     {
     140       39143 :         char       *ptr = GinGetPosting(itup);
     141             : 
     142       39143 :         memcpy(ptr, data, dataSize);
     143             :     }
     144             : 
     145             :     /*
     146             :      * Insert category byte, if needed
     147             :      */
     148      124152 :     if (category != GIN_CAT_NORM_KEY)
     149             :     {
     150          20 :         Assert(IndexTupleHasNulls(itup));
     151          20 :         GinSetNullCategory(itup, ginstate, category);
     152             :     }
     153      124152 :     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        7899 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
     164             :              int *nitems)
     165             : {
     166        7899 :     Pointer     ptr = GinGetPosting(itup);
     167        7899 :     int         nipd = GinGetNPosting(itup);
     168             :     ItemPointer ipd;
     169             :     int         ndecoded;
     170             : 
     171        7899 :     if (GinItupIsCompressed(itup))
     172             :     {
     173        7899 :         if (nipd > 0)
     174             :         {
     175        7899 :             ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
     176        7899 :             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        7899 :     *nitems = nipd;
     191        7899 :     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         216 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
     203             : {
     204             :     IndexTuple  nitup;
     205             : 
     206         216 :     if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
     207         216 :     {
     208             :         /* Tuple contains a posting list, just copy stuff before that */
     209         216 :         uint32      origsize = GinGetPostingOffset(itup);
     210             : 
     211         216 :         origsize = MAXALIGN(origsize);
     212         216 :         nitup = (IndexTuple) palloc(origsize);
     213         216 :         memcpy(nitup, itup, origsize);
     214             :         /* ... be sure to fix the size header field ... */
     215         216 :         nitup->t_info &= ~INDEX_SIZE_MASK;
     216         216 :         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         216 :     GinSetDownlink(nitup, childblk);
     227             : 
     228         216 :     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        7274 : getRightMostTuple(Page page)
     237             : {
     238        7274 :     OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
     239             : 
     240        7274 :     return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
     241             : }
     242             : 
     243             : static bool
     244       40038 : entryIsMoveRight(GinBtree btree, Page page)
     245             : {
     246             :     IndexTuple  itup;
     247             :     OffsetNumber attnum;
     248             :     Datum       key;
     249             :     GinNullCategory category;
     250             : 
     251       40038 :     if (GinPageRightMost(page))
     252       32980 :         return FALSE;
     253             : 
     254        7058 :     itup = getRightMostTuple(page);
     255        7058 :     attnum = gintuple_get_attrnum(btree->ginstate, itup);
     256        7058 :     key = gintuple_get_key(btree->ginstate, itup, &category);
     257             : 
     258       21174 :     if (ginCompareAttEntries(btree->ginstate,
     259       14116 :                              btree->entryAttnum, btree->entryKey, btree->entryCategory,
     260             :                              attnum, key, category) > 0)
     261           0 :         return TRUE;
     262             : 
     263        7058 :     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       40038 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
     272             : {
     273             :     OffsetNumber low,
     274             :                 high,
     275             :                 maxoff;
     276       40038 :     IndexTuple  itup = NULL;
     277             :     int         result;
     278       40038 :     Page        page = BufferGetPage(stack->buffer);
     279             : 
     280       40038 :     Assert(!GinPageIsLeaf(page));
     281       40038 :     Assert(!GinPageIsData(page));
     282             : 
     283       40038 :     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       40038 :     low = FirstOffsetNumber;
     291       40038 :     maxoff = high = PageGetMaxOffsetNumber(page);
     292       40038 :     Assert(high >= low);
     293             : 
     294       40038 :     high++;
     295             : 
     296      294393 :     while (high > low)
     297             :     {
     298      214339 :         OffsetNumber mid = low + ((high - low) / 2);
     299             : 
     300      214339 :         if (mid == maxoff && GinPageRightMost(page))
     301             :         {
     302             :             /* Right infinity */
     303       32987 :             result = -1;
     304             :         }
     305             :         else
     306             :         {
     307             :             OffsetNumber attnum;
     308             :             Datum       key;
     309             :             GinNullCategory category;
     310             : 
     311      181352 :             itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     312      181352 :             attnum = gintuple_get_attrnum(btree->ginstate, itup);
     313      181352 :             key = gintuple_get_key(btree->ginstate, itup, &category);
     314      544056 :             result = ginCompareAttEntries(btree->ginstate,
     315      181352 :                                           btree->entryAttnum,
     316             :                                           btree->entryKey,
     317      181352 :                                           btree->entryCategory,
     318             :                                           attnum, key, category);
     319             :         }
     320             : 
     321      214339 :         if (result == 0)
     322             :         {
     323          22 :             stack->off = mid;
     324          22 :             Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     325          22 :             return GinGetDownlink(itup);
     326             :         }
     327      214317 :         else if (result > 0)
     328      136231 :             low = mid + 1;
     329             :         else
     330       78086 :             high = mid;
     331             :     }
     332             : 
     333       40016 :     Assert(high >= FirstOffsetNumber && high <= maxoff);
     334             : 
     335       40016 :     stack->off = high;
     336       40016 :     itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
     337       40016 :     Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     338       40016 :     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       41600 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
     348             : {
     349       41600 :     Page        page = BufferGetPage(stack->buffer);
     350             :     OffsetNumber low,
     351             :                 high;
     352             : 
     353       41600 :     Assert(GinPageIsLeaf(page));
     354       41600 :     Assert(!GinPageIsData(page));
     355             : 
     356       41600 :     if (btree->fullScan)
     357             :     {
     358           0 :         stack->off = FirstOffsetNumber;
     359           0 :         return TRUE;
     360             :     }
     361             : 
     362       41600 :     low = FirstOffsetNumber;
     363       41600 :     high = PageGetMaxOffsetNumber(page);
     364             : 
     365       41600 :     if (high < low)
     366             :     {
     367          12 :         stack->off = FirstOffsetNumber;
     368          12 :         return false;
     369             :     }
     370             : 
     371       41588 :     high++;
     372             : 
     373      375637 :     while (high > low)
     374             :     {
     375      299558 :         OffsetNumber mid = low + ((high - low) / 2);
     376             :         IndexTuple  itup;
     377             :         OffsetNumber attnum;
     378             :         Datum       key;
     379             :         GinNullCategory category;
     380             :         int         result;
     381             : 
     382      299558 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     383      299558 :         attnum = gintuple_get_attrnum(btree->ginstate, itup);
     384      299558 :         key = gintuple_get_key(btree->ginstate, itup, &category);
     385      898674 :         result = ginCompareAttEntries(btree->ginstate,
     386      299558 :                                       btree->entryAttnum,
     387             :                                       btree->entryKey,
     388      299558 :                                       btree->entryCategory,
     389             :                                       attnum, key, category);
     390      299558 :         if (result == 0)
     391             :         {
     392        7097 :             stack->off = mid;
     393        7097 :             return true;
     394             :         }
     395      292461 :         else if (result > 0)
     396      261591 :             low = mid + 1;
     397             :         else
     398       30870 :             high = mid;
     399             :     }
     400             : 
     401       34491 :     stack->off = high;
     402       34491 :     return false;
     403             : }
     404             : 
     405             : static OffsetNumber
     406         204 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
     407             : {
     408             :     OffsetNumber i,
     409         204 :                 maxoff = PageGetMaxOffsetNumber(page);
     410             :     IndexTuple  itup;
     411             : 
     412         204 :     Assert(!GinPageIsLeaf(page));
     413         204 :     Assert(!GinPageIsData(page));
     414             : 
     415             :     /* if page isn't changed, we returns storedOff */
     416         204 :     if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
     417             :     {
     418         204 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
     419         204 :         if (GinGetDownlink(itup) == blkno)
     420         204 :             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           0 :     Assert(!GinPageIsLeaf(page));
     452           0 :     Assert(!GinPageIsData(page));
     453           0 :     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       38354 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
     461             :                    GinBtreeEntryInsertData *insertData)
     462             : {
     463       38354 :     Size        releasedsz = 0;
     464             :     Size        addedsz;
     465       38354 :     Page        page = BufferGetPage(buf);
     466             : 
     467       38354 :     Assert(insertData->entry);
     468       38354 :     Assert(!GinPageIsData(page));
     469             : 
     470       38354 :     if (insertData->isDelete)
     471             :     {
     472        3665 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     473             : 
     474        3665 :         releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     475             :     }
     476             : 
     477       38354 :     addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
     478             : 
     479       38354 :     if (PageGetFreeSpace(page) + releasedsz >= addedsz)
     480       38144 :         return true;
     481             : 
     482         210 :     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       38354 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
     492             :                  GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
     493             : {
     494       38354 :     Assert(insertData->entry);
     495       38354 :     Assert(!GinPageIsData(page));
     496             : 
     497       38354 :     if (insertData->isDelete)
     498             :     {
     499        3665 :         Assert(GinPageIsLeaf(page));
     500        3665 :         PageIndexTupleDelete(page, off);
     501             :     }
     502             : 
     503       38354 :     if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
     504             :     {
     505         204 :         IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     506             : 
     507         204 :         GinSetDownlink(itup, updateblkno);
     508             :     }
     509       38354 : }
     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       38354 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     529             :                       void *insertPayload, BlockNumber updateblkno,
     530             :                       void **ptp_workspace,
     531             :                       Page *newlpage, Page *newrpage)
     532             : {
     533       38354 :     GinBtreeEntryInsertData *insertData = insertPayload;
     534       38354 :     OffsetNumber off = stack->off;
     535             : 
     536             :     /* If it doesn't fit, deal with split case */
     537       38354 :     if (!entryIsEnoughSpace(btree, buf, off, insertData))
     538             :     {
     539         210 :         entrySplitPage(btree, buf, stack, insertData, updateblkno,
     540             :                        newlpage, newrpage);
     541         210 :         return GPTP_SPLIT;
     542             :     }
     543             : 
     544             :     /* Else, we're ready to proceed with insertion */
     545       38144 :     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       38144 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     556             :                      void *insertPayload, BlockNumber updateblkno,
     557             :                      void *ptp_workspace)
     558             : {
     559       38144 :     GinBtreeEntryInsertData *insertData = insertPayload;
     560       38144 :     Page        page = BufferGetPage(buf);
     561       38144 :     OffsetNumber off = stack->off;
     562             :     OffsetNumber placed;
     563             : 
     564       38144 :     entryPreparePage(btree, page, off, insertData, updateblkno);
     565             : 
     566       38144 :     placed = PageAddItem(page,
     567             :                          (Item) insertData->entry,
     568             :                          IndexTupleSize(insertData->entry),
     569             :                          off, false, false);
     570       38144 :     if (placed != off)
     571           0 :         elog(ERROR, "failed to add item to index page in \"%s\"",
     572             :              RelationGetRelationName(btree->index));
     573             : 
     574       38144 :     if (RelationNeedsWAL(btree->index))
     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       38127 :         data.isDelete = insertData->isDelete;
     584       38127 :         data.offset = off;
     585             : 
     586       38127 :         XLogRegisterBufData(0, (char *) &data,
     587             :                             offsetof(ginxlogInsertEntry, tuple));
     588       38127 :         XLogRegisterBufData(0, (char *) insertData->entry,
     589       38127 :                             IndexTupleSize(insertData->entry));
     590             :     }
     591       38144 : }
     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         210 : entrySplitPage(GinBtree btree, Buffer origbuf,
     601             :                GinBtreeStack *stack,
     602             :                GinBtreeEntryInsertData *insertData,
     603             :                BlockNumber updateblkno,
     604             :                Page *newlpage, Page *newrpage)
     605             : {
     606         210 :     OffsetNumber off = stack->off;
     607             :     OffsetNumber i,
     608             :                 maxoff,
     609         210 :                 separator = InvalidOffsetNumber;
     610         210 :     Size        totalsize = 0;
     611         210 :     Size        lsize = 0,
     612             :                 size;
     613             :     char       *ptr;
     614             :     IndexTuple  itup;
     615             :     Page        page;
     616         210 :     Page        lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     617         210 :     Page        rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     618         210 :     Size        pageSize = PageGetPageSize(lpage);
     619             :     char        tupstore[2 * BLCKSZ];
     620             : 
     621         210 :     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         210 :     maxoff = PageGetMaxOffsetNumber(lpage);
     628         210 :     ptr = tupstore;
     629       65872 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     630             :     {
     631       65662 :         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       65662 :         itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
     640       65662 :         size = MAXALIGN(IndexTupleSize(itup));
     641       65662 :         memcpy(ptr, itup, size);
     642       65662 :         ptr += size;
     643       65662 :         totalsize += size + sizeof(ItemIdData);
     644             :     }
     645             : 
     646         210 :     if (off == maxoff + 1)
     647             :     {
     648         210 :         size = MAXALIGN(IndexTupleSize(insertData->entry));
     649         210 :         memcpy(ptr, insertData->entry, size);
     650         210 :         ptr += size;
     651         210 :         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         210 :     GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
     659         210 :     GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
     660             : 
     661         210 :     ptr = tupstore;
     662         210 :     maxoff++;
     663         210 :     lsize = 0;
     664             : 
     665         210 :     page = lpage;
     666       66082 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
     667             :     {
     668       65872 :         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       65872 :         if (lsize > totalsize / 2)
     675             :         {
     676       32864 :             if (separator == InvalidOffsetNumber)
     677         210 :                 separator = i - 1;
     678       32864 :             page = rpage;
     679             :         }
     680             :         else
     681             :         {
     682       33008 :             lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     683             :         }
     684             : 
     685       65872 :         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       65872 :         ptr += MAXALIGN(IndexTupleSize(itup));
     689             :     }
     690             : 
     691             :     /* return temp pages to caller */
     692         210 :     *newlpage = lpage;
     693         210 :     *newrpage = rpage;
     694         210 : }
     695             : 
     696             : /*
     697             :  * Construct insertion payload for inserting the downlink for given buffer.
     698             :  */
     699             : static void *
     700         204 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
     701             : {
     702             :     GinBtreeEntryInsertData *insertData;
     703         204 :     Page        lpage = BufferGetPage(lbuf);
     704         204 :     BlockNumber lblkno = BufferGetBlockNumber(lbuf);
     705             :     IndexTuple  itup;
     706             : 
     707         204 :     itup = getRightMostTuple(lpage);
     708             : 
     709         204 :     insertData = palloc(sizeof(GinBtreeEntryInsertData));
     710         204 :     insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
     711         204 :     insertData->isDelete = false;
     712             : 
     713         204 :     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           6 : ginEntryFillRoot(GinBtree btree, Page root,
     722             :                  BlockNumber lblkno, Page lpage,
     723             :                  BlockNumber rblkno, Page rpage)
     724             : {
     725             :     IndexTuple  itup;
     726             : 
     727           6 :     itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
     728           6 :     if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     729           0 :         elog(ERROR, "failed to add item to index root page");
     730           6 :     pfree(itup);
     731             : 
     732           6 :     itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
     733           6 :     if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     734           0 :         elog(ERROR, "failed to add item to index root page");
     735           6 :     pfree(itup);
     736           6 : }
     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       41600 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
     746             :                     Datum key, GinNullCategory category,
     747             :                     GinState *ginstate)
     748             : {
     749       41600 :     memset(btree, 0, sizeof(GinBtreeData));
     750             : 
     751       41600 :     btree->index = ginstate->index;
     752       41600 :     btree->rootBlkno = GIN_ROOT_BLKNO;
     753       41600 :     btree->ginstate = ginstate;
     754             : 
     755       41600 :     btree->findChildPage = entryLocateEntry;
     756       41600 :     btree->getLeftMostChild = entryGetLeftMostPage;
     757       41600 :     btree->isMoveRight = entryIsMoveRight;
     758       41600 :     btree->findItem = entryLocateLeafEntry;
     759       41600 :     btree->findChildPtr = entryFindChildPtr;
     760       41600 :     btree->beginPlaceToPage = entryBeginPlaceToPage;
     761       41600 :     btree->execPlaceToPage = entryExecPlaceToPage;
     762       41600 :     btree->fillRoot = ginEntryFillRoot;
     763       41600 :     btree->prepareDownlink = entryPrepareDownlink;
     764             : 
     765       41600 :     btree->isData = FALSE;
     766       41600 :     btree->fullScan = FALSE;
     767       41600 :     btree->isBuild = FALSE;
     768             : 
     769       41600 :     btree->entryAttnum = attnum;
     770       41600 :     btree->entryKey = key;
     771       41600 :     btree->entryCategory = category;
     772       41600 : }

Generated by: LCOV version 1.11