LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginentrypage.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.5 % 275 249
Test Date: 2026-07-11 14:15:42 Functions: 94.1 % 17 16
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 71.3 % 122 87

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

Generated by: LCOV version 2.0-1