LCOV - code coverage report
Current view: top level - src/backend/access/gin - ginentrypage.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.7 % 371 329
Test Date: 2026-01-26 10:56:24 Functions: 94.1 % 17 16
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 56.8 % 176 100

             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                 :      309964 : GinFormTuple(GinState *ginstate,
      45                 :             :                          OffsetNumber attnum, Datum key, GinNullCategory category,
      46                 :             :                          Pointer data, Size dataSize, int nipd,
      47                 :             :                          bool errorTooBig)
      48                 :             : {
      49                 :      309964 :         Datum           datums[2];
      50                 :      309964 :         bool            isnull[2];
      51                 :      309964 :         IndexTuple      itup;
      52                 :      309964 :         uint32          newsize;
      53                 :             : 
      54                 :             :         /* Build the basic tuple: optional column number, plus key datum */
      55         [ +  + ]:      309964 :         if (ginstate->oneCol)
      56                 :             :         {
      57                 :      109655 :                 datums[0] = key;
      58                 :      109655 :                 isnull[0] = (category != GIN_CAT_NORM_KEY);
      59                 :      109655 :         }
      60                 :             :         else
      61                 :             :         {
      62                 :      200309 :                 datums[0] = UInt16GetDatum(attnum);
      63                 :      200309 :                 isnull[0] = false;
      64                 :      200309 :                 datums[1] = key;
      65                 :      200309 :                 isnull[1] = (category != GIN_CAT_NORM_KEY);
      66                 :             :         }
      67                 :             : 
      68                 :      309964 :         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                 :      309964 :         newsize = IndexTupleSize(itup);
      81                 :             : 
      82         [ +  + ]:      309964 :         if (IndexTupleHasNulls(itup))
      83                 :             :         {
      84                 :          38 :                 uint32          minsize;
      85                 :             : 
      86         [ +  - ]:          38 :                 Assert(category != GIN_CAT_NORM_KEY);
      87                 :          38 :                 minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
      88         [ +  + ]:          38 :                 newsize = Max(newsize, minsize);
      89                 :          38 :         }
      90                 :             : 
      91                 :      309964 :         newsize = SHORTALIGN(newsize);
      92                 :             : 
      93                 :      309964 :         GinSetPostingOffset(itup, newsize);
      94                 :      309964 :         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                 :      309964 :         newsize += dataSize;
     101                 :             : 
     102                 :      309964 :         newsize = MAXALIGN(newsize);
     103                 :             : 
     104         [ +  + ]:      309964 :         if (newsize > GinMaxItemSize)
     105                 :             :         {
     106         [ +  - ]:           1 :                 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                 :           1 :                 pfree(itup);
     113                 :           1 :                 return NULL;
     114                 :             :         }
     115                 :             : 
     116                 :             :         /*
     117                 :             :          * Resize tuple if needed
     118                 :             :          */
     119         [ +  + ]:      309963 :         if (newsize != IndexTupleSize(itup))
     120                 :             :         {
     121                 :       78155 :                 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                 :       78155 :                 memset((char *) itup + IndexTupleSize(itup),
     128                 :             :                            0, newsize - IndexTupleSize(itup));
     129                 :             :                 /* set new size in tuple header */
     130                 :       78155 :                 itup->t_info &= ~INDEX_SIZE_MASK;
     131                 :       78155 :                 itup->t_info |= newsize;
     132                 :       78155 :         }
     133                 :             : 
     134                 :             :         /*
     135                 :             :          * Copy in the posting list, if provided
     136                 :             :          */
     137         [ +  + ]:      309963 :         if (data)
     138                 :             :         {
     139                 :       78154 :                 char       *ptr = GinGetPosting(itup);
     140                 :             : 
     141                 :       78154 :                 memcpy(ptr, data, dataSize);
     142                 :       78154 :         }
     143                 :             : 
     144                 :             :         /*
     145                 :             :          * Insert category byte, if needed
     146                 :             :          */
     147         [ +  + ]:      309963 :         if (category != GIN_CAT_NORM_KEY)
     148                 :             :         {
     149         [ +  - ]:          38 :                 Assert(IndexTupleHasNulls(itup));
     150                 :          38 :                 GinSetNullCategory(itup, ginstate, category);
     151                 :          38 :         }
     152                 :      309963 :         return itup;
     153                 :      309964 : }
     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                 :       70353 : ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
     163                 :             :                          int *nitems)
     164                 :             : {
     165                 :       70353 :         Pointer         ptr = GinGetPosting(itup);
     166                 :       70353 :         int                     nipd = GinGetNPosting(itup);
     167                 :       70353 :         ItemPointer ipd;
     168                 :       70353 :         int                     ndecoded;
     169                 :             : 
     170         [ -  + ]:       70353 :         if (GinItupIsCompressed(itup))
     171                 :             :         {
     172         [ +  + ]:       70353 :                 if (nipd > 0)
     173                 :             :                 {
     174                 :       50354 :                         ipd = ginPostingListDecode(ptr, &ndecoded);
     175         [ +  - ]:       50354 :                         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                 :       50354 :                 }
     179                 :             :                 else
     180                 :             :                 {
     181                 :       19999 :                         ipd = palloc(0);
     182                 :             :                 }
     183                 :       70353 :         }
     184                 :             :         else
     185                 :             :         {
     186                 :           0 :                 ipd = palloc_array(ItemPointerData, nipd);
     187                 :           0 :                 memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
     188                 :             :         }
     189                 :       70353 :         *nitems = nipd;
     190                 :      140706 :         return ipd;
     191                 :       70353 : }
     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                 :         540 : GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
     202                 :             : {
     203                 :         540 :         IndexTuple      nitup;
     204                 :             : 
     205   [ +  -  -  + ]:         540 :         if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
     206                 :             :         {
     207                 :             :                 /* Tuple contains a posting list, just copy stuff before that */
     208                 :         540 :                 uint32          origsize = GinGetPostingOffset(itup);
     209                 :             : 
     210                 :         540 :                 origsize = MAXALIGN(origsize);
     211                 :         540 :                 nitup = (IndexTuple) palloc(origsize);
     212                 :         540 :                 memcpy(nitup, itup, origsize);
     213                 :             :                 /* ... be sure to fix the size header field ... */
     214                 :         540 :                 nitup->t_info &= ~INDEX_SIZE_MASK;
     215                 :         540 :                 nitup->t_info |= origsize;
     216                 :         540 :         }
     217                 :             :         else
     218                 :             :         {
     219                 :             :                 /* Copy the tuple as-is */
     220                 :           0 :                 nitup = (IndexTuple) palloc(IndexTupleSize(itup));
     221                 :           0 :                 memcpy(nitup, itup, IndexTupleSize(itup));
     222                 :             :         }
     223                 :             : 
     224                 :             :         /* Now insert the correct downlink */
     225                 :         540 :         GinSetDownlink(nitup, childblk);
     226                 :             : 
     227                 :        1080 :         return nitup;
     228                 :         540 : }
     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                 :        7726 : getRightMostTuple(Page page)
     236                 :             : {
     237                 :        7726 :         OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
     238                 :             : 
     239                 :       15452 :         return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
     240                 :        7726 : }
     241                 :             : 
     242                 :             : static bool
     243                 :       80163 : entryIsMoveRight(GinBtree btree, Page page)
     244                 :             : {
     245                 :       80163 :         IndexTuple      itup;
     246                 :       80163 :         OffsetNumber attnum;
     247                 :       80163 :         Datum           key;
     248                 :       80163 :         GinNullCategory category;
     249                 :             : 
     250         [ +  + ]:       80163 :         if (GinPageRightMost(page))
     251                 :       72977 :                 return false;
     252                 :             : 
     253                 :        7186 :         itup = getRightMostTuple(page);
     254                 :        7186 :         attnum = gintuple_get_attrnum(btree->ginstate, itup);
     255                 :        7186 :         key = gintuple_get_key(btree->ginstate, itup, &category);
     256                 :             : 
     257                 :       14372 :         if (ginCompareAttEntries(btree->ginstate,
     258                 :        7186 :                                                          btree->entryAttnum, btree->entryKey, btree->entryCategory,
     259   [ -  +  -  + ]:       14372 :                                                          attnum, key, category) > 0)
     260                 :           0 :                 return true;
     261                 :             : 
     262                 :        7186 :         return false;
     263                 :       80163 : }
     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                 :       80163 : entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
     271                 :             : {
     272                 :       80163 :         OffsetNumber low,
     273                 :             :                                 high,
     274                 :             :                                 maxoff;
     275                 :       80163 :         IndexTuple      itup = NULL;
     276                 :       80163 :         int                     result;
     277                 :       80163 :         Page            page = BufferGetPage(stack->buffer);
     278                 :             : 
     279         [ +  - ]:       80163 :         Assert(!GinPageIsLeaf(page));
     280         [ +  - ]:       80163 :         Assert(!GinPageIsData(page));
     281                 :             : 
     282         [ -  + ]:       80163 :         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                 :       80163 :         low = FirstOffsetNumber;
     290                 :       80163 :         maxoff = high = PageGetMaxOffsetNumber(page);
     291         [ +  - ]:       80163 :         Assert(high >= low);
     292                 :             : 
     293                 :       80163 :         high++;
     294                 :             : 
     295         [ +  + ]:      578661 :         while (high > low)
     296                 :             :         {
     297                 :      498527 :                 OffsetNumber mid = low + ((high - low) / 2);
     298                 :             : 
     299   [ +  +  -  + ]:      498527 :                 if (mid == maxoff && GinPageRightMost(page))
     300                 :             :                 {
     301                 :             :                         /* Right infinity */
     302                 :       72992 :                         result = -1;
     303                 :       72992 :                 }
     304                 :             :                 else
     305                 :             :                 {
     306                 :      425535 :                         OffsetNumber attnum;
     307                 :      425535 :                         Datum           key;
     308                 :      425535 :                         GinNullCategory category;
     309                 :             : 
     310                 :      425535 :                         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     311                 :      425535 :                         attnum = gintuple_get_attrnum(btree->ginstate, itup);
     312                 :      425535 :                         key = gintuple_get_key(btree->ginstate, itup, &category);
     313                 :      851070 :                         result = ginCompareAttEntries(btree->ginstate,
     314                 :      425535 :                                                                                   btree->entryAttnum,
     315                 :      425535 :                                                                                   btree->entryKey,
     316                 :      425535 :                                                                                   btree->entryCategory,
     317                 :      425535 :                                                                                   attnum, key, category);
     318                 :      425535 :                 }
     319                 :             : 
     320         [ +  + ]:      498527 :                 if (result == 0)
     321                 :             :                 {
     322                 :          29 :                         stack->off = mid;
     323         [ -  + ]:          29 :                         Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     324                 :          29 :                         return GinGetDownlink(itup);
     325                 :             :                 }
     326         [ +  + ]:      498498 :                 else if (result > 0)
     327                 :      375848 :                         low = mid + 1;
     328                 :             :                 else
     329                 :      122650 :                         high = mid;
     330         [ +  + ]:      498527 :         }
     331                 :             : 
     332         [ +  - ]:       80134 :         Assert(high >= FirstOffsetNumber && high <= maxoff);
     333                 :             : 
     334                 :       80134 :         stack->off = high;
     335                 :       80134 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
     336         [ +  - ]:       80134 :         Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
     337                 :       80134 :         return GinGetDownlink(itup);
     338                 :       80163 : }
     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                 :       81785 : entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
     347                 :             : {
     348                 :       81785 :         Page            page = BufferGetPage(stack->buffer);
     349                 :       81785 :         OffsetNumber low,
     350                 :             :                                 high;
     351                 :             : 
     352         [ +  - ]:       81785 :         Assert(GinPageIsLeaf(page));
     353         [ +  - ]:       81785 :         Assert(!GinPageIsData(page));
     354                 :             : 
     355         [ -  + ]:       81785 :         if (btree->fullScan)
     356                 :             :         {
     357                 :           0 :                 stack->off = FirstOffsetNumber;
     358                 :           0 :                 return true;
     359                 :             :         }
     360                 :             : 
     361                 :       81785 :         low = FirstOffsetNumber;
     362                 :       81785 :         high = PageGetMaxOffsetNumber(page);
     363                 :             : 
     364         [ +  + ]:       81785 :         if (high < low)
     365                 :             :         {
     366                 :          50 :                 stack->off = FirstOffsetNumber;
     367                 :          50 :                 return false;
     368                 :             :         }
     369                 :             : 
     370                 :       81735 :         high++;
     371                 :             : 
     372         [ +  + ]:      651069 :         while (high > low)
     373                 :             :         {
     374                 :      576538 :                 OffsetNumber mid = low + ((high - low) / 2);
     375                 :      576538 :                 IndexTuple      itup;
     376                 :      576538 :                 OffsetNumber attnum;
     377                 :      576538 :                 Datum           key;
     378                 :      576538 :                 GinNullCategory category;
     379                 :      576538 :                 int                     result;
     380                 :             : 
     381                 :      576538 :                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
     382                 :      576538 :                 attnum = gintuple_get_attrnum(btree->ginstate, itup);
     383                 :      576538 :                 key = gintuple_get_key(btree->ginstate, itup, &category);
     384                 :     1153076 :                 result = ginCompareAttEntries(btree->ginstate,
     385                 :      576538 :                                                                           btree->entryAttnum,
     386                 :      576538 :                                                                           btree->entryKey,
     387                 :      576538 :                                                                           btree->entryCategory,
     388                 :      576538 :                                                                           attnum, key, category);
     389         [ +  + ]:      576538 :                 if (result == 0)
     390                 :             :                 {
     391                 :        7204 :                         stack->off = mid;
     392                 :        7204 :                         return true;
     393                 :             :                 }
     394         [ +  + ]:      569334 :                 else if (result > 0)
     395                 :      539813 :                         low = mid + 1;
     396                 :             :                 else
     397                 :       29521 :                         high = mid;
     398         [ +  + ]:      576538 :         }
     399                 :             : 
     400                 :       74531 :         stack->off = high;
     401                 :       74531 :         return false;
     402                 :       81785 : }
     403                 :             : 
     404                 :             : static OffsetNumber
     405                 :         524 : entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
     406                 :             : {
     407                 :         524 :         OffsetNumber i,
     408                 :         524 :                                 maxoff = PageGetMaxOffsetNumber(page);
     409                 :         524 :         IndexTuple      itup;
     410                 :             : 
     411         [ +  - ]:         524 :         Assert(!GinPageIsLeaf(page));
     412         [ +  - ]:         524 :         Assert(!GinPageIsData(page));
     413                 :             : 
     414                 :             :         /* if page isn't changed, we returns storedOff */
     415   [ +  -  -  + ]:         524 :         if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
     416                 :             :         {
     417                 :         524 :                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
     418         [ +  - ]:         524 :                 if (GinGetDownlink(itup) == blkno)
     419                 :         524 :                         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                 :           0 :                 }
     431                 :           0 :                 maxoff = storedOff - 1;
     432                 :           0 :         }
     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                 :           0 :         }
     441                 :             : 
     442                 :           0 :         return InvalidOffsetNumber;
     443                 :         524 : }
     444                 :             : 
     445                 :             : static BlockNumber
     446                 :           0 : entryGetLeftMostPage(GinBtree btree, Page page)
     447                 :             : {
     448                 :           0 :         IndexTuple      itup;
     449                 :             : 
     450         [ #  # ]:           0 :         Assert(!GinPageIsLeaf(page));
     451         [ #  # ]:           0 :         Assert(!GinPageIsData(page));
     452         [ #  # ]:           0 :         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
     453                 :             : 
     454                 :           0 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
     455                 :           0 :         return GinGetDownlink(itup);
     456                 :           0 : }
     457                 :             : 
     458                 :             : static bool
     459                 :       78678 : entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
     460                 :             :                                    GinBtreeEntryInsertData *insertData)
     461                 :             : {
     462                 :       78678 :         Size            releasedsz = 0;
     463                 :       78678 :         Size            addedsz;
     464                 :       78678 :         Page            page = BufferGetPage(buf);
     465                 :             : 
     466         [ +  - ]:       78678 :         Assert(insertData->entry);
     467         [ +  - ]:       78678 :         Assert(!GinPageIsData(page));
     468                 :             : 
     469         [ +  + ]:       78678 :         if (insertData->isDelete)
     470                 :             :         {
     471                 :        3660 :                 IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     472                 :             : 
     473                 :        3660 :                 releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     474                 :        3660 :         }
     475                 :             : 
     476                 :       78678 :         addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
     477                 :             : 
     478         [ +  + ]:       78678 :         if (PageGetFreeSpace(page) + releasedsz >= addedsz)
     479                 :       78146 :                 return true;
     480                 :             : 
     481                 :         532 :         return false;
     482                 :       78678 : }
     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                 :       78678 : entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
     491                 :             :                                  GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
     492                 :             : {
     493         [ +  - ]:       78678 :         Assert(insertData->entry);
     494         [ +  - ]:       78678 :         Assert(!GinPageIsData(page));
     495                 :             : 
     496         [ +  + ]:       78678 :         if (insertData->isDelete)
     497                 :             :         {
     498         [ +  - ]:        3660 :                 Assert(GinPageIsLeaf(page));
     499                 :        3660 :                 PageIndexTupleDelete(page, off);
     500                 :        3660 :         }
     501                 :             : 
     502   [ +  +  -  + ]:       78678 :         if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
     503                 :             :         {
     504                 :         524 :                 IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
     505                 :             : 
     506                 :         524 :                 GinSetDownlink(itup, updateblkno);
     507                 :         524 :         }
     508                 :       78678 : }
     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                 :       78678 : entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     528                 :             :                                           void *insertPayload, BlockNumber updateblkno,
     529                 :             :                                           void **ptp_workspace,
     530                 :             :                                           Page *newlpage, Page *newrpage)
     531                 :             : {
     532                 :       78678 :         GinBtreeEntryInsertData *insertData = insertPayload;
     533                 :       78678 :         OffsetNumber off = stack->off;
     534                 :             : 
     535                 :             :         /* If it doesn't fit, deal with split case */
     536         [ +  + ]:       78678 :         if (!entryIsEnoughSpace(btree, buf, off, insertData))
     537                 :             :         {
     538                 :        1064 :                 entrySplitPage(btree, buf, stack, insertData, updateblkno,
     539                 :         532 :                                            newlpage, newrpage);
     540                 :         532 :                 return GPTP_SPLIT;
     541                 :             :         }
     542                 :             : 
     543                 :             :         /* Else, we're ready to proceed with insertion */
     544                 :       78146 :         return GPTP_INSERT;
     545                 :       78678 : }
     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                 :       78146 : entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
     555                 :             :                                          void *insertPayload, BlockNumber updateblkno,
     556                 :             :                                          void *ptp_workspace)
     557                 :             : {
     558                 :       78146 :         GinBtreeEntryInsertData *insertData = insertPayload;
     559                 :       78146 :         Page            page = BufferGetPage(buf);
     560                 :       78146 :         OffsetNumber off = stack->off;
     561                 :       78146 :         OffsetNumber placed;
     562                 :             : 
     563                 :       78146 :         entryPreparePage(btree, page, off, insertData, updateblkno);
     564                 :             : 
     565                 :       78146 :         placed = PageAddItem(page,
     566                 :             :                                                  insertData->entry,
     567                 :             :                                                  IndexTupleSize(insertData->entry),
     568                 :             :                                                  off, false, false);
     569         [ +  - ]:       78146 :         if (placed != off)
     570   [ #  #  #  # ]:           0 :                 elog(ERROR, "failed to add item to index page in \"%s\"",
     571                 :             :                          RelationGetRelationName(btree->index));
     572                 :             : 
     573                 :       78146 :         MarkBufferDirty(buf);
     574                 :             : 
     575   [ +  +  +  -  :       78146 :         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                 :       23659 :                 data.isDelete = insertData->isDelete;
     585                 :       23659 :                 data.offset = off;
     586                 :             : 
     587                 :       23659 :                 XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
     588                 :       23659 :                 XLogRegisterBufData(0, &data,
     589                 :             :                                                         offsetof(ginxlogInsertEntry, tuple));
     590                 :       47318 :                 XLogRegisterBufData(0, insertData->entry,
     591                 :       23659 :                                                         IndexTupleSize(insertData->entry));
     592                 :       23659 :         }
     593                 :      125464 : }
     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                 :         532 : entrySplitPage(GinBtree btree, Buffer origbuf,
     603                 :             :                            GinBtreeStack *stack,
     604                 :             :                            GinBtreeEntryInsertData *insertData,
     605                 :             :                            BlockNumber updateblkno,
     606                 :             :                            Page *newlpage, Page *newrpage)
     607                 :             : {
     608                 :         532 :         OffsetNumber off = stack->off;
     609                 :         532 :         OffsetNumber i,
     610                 :             :                                 maxoff,
     611                 :         532 :                                 separator = InvalidOffsetNumber;
     612                 :         532 :         Size            totalsize = 0;
     613                 :         532 :         Size            lsize = 0,
     614                 :             :                                 size;
     615                 :         532 :         char       *ptr;
     616                 :         532 :         IndexTuple      itup;
     617                 :         532 :         Page            page;
     618                 :         532 :         Page            lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     619                 :         532 :         Page            rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
     620                 :         532 :         Size            pageSize = PageGetPageSize(lpage);
     621                 :         532 :         PGAlignedBlock tupstore[2]; /* could need 2 pages' worth of tuples */
     622                 :             : 
     623                 :         532 :         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                 :         532 :         maxoff = PageGetMaxOffsetNumber(lpage);
     630                 :         532 :         ptr = tupstore[0].data;
     631         [ +  + ]:      145572 :         for (i = FirstOffsetNumber; i <= maxoff; i++)
     632                 :             :         {
     633         [ +  - ]:      145040 :                 if (i == off)
     634                 :             :                 {
     635                 :           0 :                         size = MAXALIGN(IndexTupleSize(insertData->entry));
     636                 :           0 :                         memcpy(ptr, insertData->entry, size);
     637                 :           0 :                         ptr += size;
     638                 :           0 :                         totalsize += size + sizeof(ItemIdData);
     639                 :           0 :                 }
     640                 :             : 
     641                 :      145040 :                 itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
     642                 :      145040 :                 size = MAXALIGN(IndexTupleSize(itup));
     643                 :      145040 :                 memcpy(ptr, itup, size);
     644                 :      145040 :                 ptr += size;
     645                 :      145040 :                 totalsize += size + sizeof(ItemIdData);
     646                 :      145040 :         }
     647                 :             : 
     648         [ -  + ]:         532 :         if (off == maxoff + 1)
     649                 :             :         {
     650                 :         532 :                 size = MAXALIGN(IndexTupleSize(insertData->entry));
     651                 :         532 :                 memcpy(ptr, insertData->entry, size);
     652                 :         532 :                 ptr += size;
     653                 :         532 :                 totalsize += size + sizeof(ItemIdData);
     654                 :         532 :         }
     655                 :             : 
     656                 :             :         /*
     657                 :             :          * Initialize the left and right pages, and copy all the tuples back to
     658                 :             :          * them.
     659                 :             :          */
     660                 :         532 :         GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
     661                 :         532 :         GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
     662                 :             : 
     663                 :         532 :         ptr = tupstore[0].data;
     664                 :         532 :         maxoff++;
     665                 :         532 :         lsize = 0;
     666                 :             : 
     667                 :         532 :         page = lpage;
     668         [ +  + ]:      146104 :         for (i = FirstOffsetNumber; i <= maxoff; i++)
     669                 :             :         {
     670                 :      145572 :                 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         [ +  + ]:      145572 :                 if (lsize > totalsize / 2)
     677                 :             :                 {
     678         [ +  + ]:       72366 :                         if (separator == InvalidOffsetNumber)
     679                 :         532 :                                 separator = i - 1;
     680                 :       72366 :                         page = rpage;
     681                 :       72366 :                 }
     682                 :             :                 else
     683                 :             :                 {
     684                 :       73206 :                         lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
     685                 :             :                 }
     686                 :             : 
     687         [ +  - ]:      145572 :                 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                 :      145572 :                 ptr += MAXALIGN(IndexTupleSize(itup));
     691                 :      145572 :         }
     692                 :             : 
     693                 :             :         /* return temp pages to caller */
     694                 :         532 :         *newlpage = lpage;
     695                 :         532 :         *newrpage = rpage;
     696                 :         532 : }
     697                 :             : 
     698                 :             : /*
     699                 :             :  * Construct insertion payload for inserting the downlink for given buffer.
     700                 :             :  */
     701                 :             : static void *
     702                 :         524 : entryPrepareDownlink(GinBtree btree, Buffer lbuf)
     703                 :             : {
     704                 :         524 :         GinBtreeEntryInsertData *insertData;
     705                 :         524 :         Page            lpage = BufferGetPage(lbuf);
     706                 :         524 :         BlockNumber lblkno = BufferGetBlockNumber(lbuf);
     707                 :         524 :         IndexTuple      itup;
     708                 :             : 
     709                 :         524 :         itup = getRightMostTuple(lpage);
     710                 :             : 
     711                 :         524 :         insertData = palloc_object(GinBtreeEntryInsertData);
     712                 :         524 :         insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
     713                 :         524 :         insertData->isDelete = false;
     714                 :             : 
     715                 :        1048 :         return insertData;
     716                 :         524 : }
     717                 :             : 
     718                 :             : /*
     719                 :             :  * Fills new root by rightest values from child.
     720                 :             :  * Also called from ginxlog, should not use btree
     721                 :             :  */
     722                 :             : void
     723                 :           8 : ginEntryFillRoot(GinBtree btree, Page root,
     724                 :             :                                  BlockNumber lblkno, Page lpage,
     725                 :             :                                  BlockNumber rblkno, Page rpage)
     726                 :             : {
     727                 :           8 :         IndexTuple      itup;
     728                 :             : 
     729                 :           8 :         itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
     730         [ +  - ]:           8 :         if (PageAddItem(root, itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     731   [ #  #  #  # ]:           0 :                 elog(ERROR, "failed to add item to index root page");
     732                 :           8 :         pfree(itup);
     733                 :             : 
     734                 :           8 :         itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
     735         [ +  - ]:           8 :         if (PageAddItem(root, itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
     736   [ #  #  #  # ]:           0 :                 elog(ERROR, "failed to add item to index root page");
     737                 :           8 :         pfree(itup);
     738                 :           8 : }
     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                 :       81785 : ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
     748                 :             :                                         Datum key, GinNullCategory category,
     749                 :             :                                         GinState *ginstate)
     750                 :             : {
     751                 :       81785 :         memset(btree, 0, sizeof(GinBtreeData));
     752                 :             : 
     753                 :       81785 :         btree->index = ginstate->index;
     754                 :       81785 :         btree->rootBlkno = GIN_ROOT_BLKNO;
     755                 :       81785 :         btree->ginstate = ginstate;
     756                 :             : 
     757                 :       81785 :         btree->findChildPage = entryLocateEntry;
     758                 :       81785 :         btree->getLeftMostChild = entryGetLeftMostPage;
     759                 :       81785 :         btree->isMoveRight = entryIsMoveRight;
     760                 :       81785 :         btree->findItem = entryLocateLeafEntry;
     761                 :       81785 :         btree->findChildPtr = entryFindChildPtr;
     762                 :       81785 :         btree->beginPlaceToPage = entryBeginPlaceToPage;
     763                 :       81785 :         btree->execPlaceToPage = entryExecPlaceToPage;
     764                 :       81785 :         btree->fillRoot = ginEntryFillRoot;
     765                 :       81785 :         btree->prepareDownlink = entryPrepareDownlink;
     766                 :             : 
     767                 :       81785 :         btree->isData = false;
     768                 :       81785 :         btree->fullScan = false;
     769                 :       81785 :         btree->isBuild = false;
     770                 :             : 
     771                 :       81785 :         btree->entryAttnum = attnum;
     772                 :       81785 :         btree->entryKey = key;
     773                 :       81785 :         btree->entryCategory = category;
     774                 :       81785 : }
        

Generated by: LCOV version 2.3.2-1