LCOV - code coverage report
Current view: top level - src/backend/storage/page - bufpage.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 93.9 % 441 414
Test Date: 2026-09-06 21:15:50 Functions: 100.0 % 19 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.4 % 354 235

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * bufpage.c
       4                 :             :  *    POSTGRES standard buffer page code.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *    src/backend/storage/page/bufpage.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/htup_details.h"
      18                 :             : #include "access/itup.h"
      19                 :             : #include "access/xlog.h"
      20                 :             : #include "pgstat.h"
      21                 :             : #include "storage/checksum.h"
      22                 :             : #include "utils/memdebug.h"
      23                 :             : #include "utils/memutils.h"
      24                 :             : 
      25                 :             : 
      26                 :             : /* GUC variable */
      27                 :             : bool        ignore_checksum_failure = false;
      28                 :             : 
      29                 :             : 
      30                 :             : /* ----------------------------------------------------------------
      31                 :             :  *                      Page support functions
      32                 :             :  * ----------------------------------------------------------------
      33                 :             :  */
      34                 :             : 
      35                 :             : /*
      36                 :             :  * PageInit
      37                 :             :  *      Initializes the contents of a page.
      38                 :             :  *      Note that we don't calculate an initial checksum here; that's not done
      39                 :             :  *      until it's time to write.
      40                 :             :  */
      41                 :             : void
      42                 :      436480 : PageInit(Page page, Size pageSize, Size specialSize)
      43                 :             : {
      44                 :      436480 :     PageHeader  p = (PageHeader) page;
      45                 :             : 
      46                 :      436480 :     specialSize = MAXALIGN(specialSize);
      47                 :             : 
      48                 :             :     Assert(pageSize == BLCKSZ);
      49                 :             :     Assert(pageSize > specialSize + SizeOfPageHeaderData);
      50                 :             : 
      51                 :             :     /* Make sure all fields of page are zero, as well as unused space */
      52   [ +  -  +  -  :      436480 :     MemSet(p, 0, pageSize);
          +  -  -  +  -  
                      - ]
      53                 :             : 
      54                 :      436480 :     p->pd_flags = 0;
      55                 :      436480 :     p->pd_lower = SizeOfPageHeaderData;
      56                 :      436480 :     p->pd_upper = pageSize - specialSize;
      57                 :      436480 :     p->pd_special = pageSize - specialSize;
      58                 :      436480 :     PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
      59                 :             :     /* p->pd_prune_xid = InvalidTransactionId;       done by above MemSet */
      60                 :      436480 : }
      61                 :             : 
      62                 :             : 
      63                 :             : /*
      64                 :             :  * PageIsVerified
      65                 :             :  *      Check that the page header and checksum (if any) appear valid.
      66                 :             :  *
      67                 :             :  * This is called when a page has just been read in from disk.  The idea is
      68                 :             :  * to cheaply detect trashed pages before we go nuts following bogus line
      69                 :             :  * pointers, testing invalid transaction identifiers, etc.
      70                 :             :  *
      71                 :             :  * It turns out to be necessary to allow zeroed pages here too.  Even though
      72                 :             :  * this routine is *not* called when deliberately adding a page to a relation,
      73                 :             :  * there are scenarios in which a zeroed page might be found in a table.
      74                 :             :  * (Example: a backend extends a relation, then crashes before it can write
      75                 :             :  * any WAL entry about the new page.  The kernel will already have the
      76                 :             :  * zeroed page in the file, and it will stay that way after restart.)  So we
      77                 :             :  * allow zeroed pages here, and are careful that the page access macros
      78                 :             :  * treat such a page as empty and without free space.  Eventually, VACUUM
      79                 :             :  * will clean up such a page and make it usable.
      80                 :             :  *
      81                 :             :  * If flag PIV_LOG_WARNING/PIV_LOG_LOG is set, a WARNING/LOG message is logged
      82                 :             :  * in the event of a checksum failure.
      83                 :             :  *
      84                 :             :  * If flag PIV_IGNORE_CHECKSUM_FAILURE is set, checksum failures will cause a
      85                 :             :  * message about the failure to be emitted, but will not cause
      86                 :             :  * PageIsVerified() to return false.
      87                 :             :  *
      88                 :             :  * To allow the caller to report statistics about checksum failures,
      89                 :             :  * *checksum_failure_p can be passed in. Note that there may be checksum
      90                 :             :  * failures even if this function returns true, due to
      91                 :             :  * PIV_IGNORE_CHECKSUM_FAILURE.
      92                 :             :  */
      93                 :             : bool
      94                 :     1386455 : PageIsVerified(PageData *page, BlockNumber blkno, int flags, bool *checksum_failure_p)
      95                 :             : {
      96                 :     1386455 :     const PageHeaderData *p = (const PageHeaderData *) page;
      97                 :             :     size_t     *pagebytes;
      98                 :     1386455 :     bool        checksum_failure = false;
      99                 :     1386455 :     bool        header_sane = false;
     100                 :     1386455 :     uint16      checksum = 0;
     101                 :             : 
     102         [ +  - ]:     1386455 :     if (checksum_failure_p)
     103                 :     1386455 :         *checksum_failure_p = false;
     104                 :             : 
     105                 :             :     /*
     106                 :             :      * Don't verify page data unless the page passes basic non-zero test
     107                 :             :      */
     108         [ +  + ]:     1386455 :     if (!PageIsNew(page))
     109                 :             :     {
     110                 :             :         /*
     111                 :             :          * There shouldn't be any check for interrupt calls happening in this
     112                 :             :          * codepath, but just to be on the safe side we hold interrupts since
     113                 :             :          * if they did happen the data checksum state could change during
     114                 :             :          * verifying checksums, which could lead to incorrect verification
     115                 :             :          * results.
     116                 :             :          */
     117                 :     1382178 :         HOLD_INTERRUPTS();
     118         [ +  + ]:     1382178 :         if (DataChecksumsNeedVerify())
     119                 :             :         {
     120                 :     1301049 :             checksum = pg_checksum_page(page, blkno);
     121                 :             : 
     122         [ +  + ]:     1301049 :             if (checksum != p->pd_checksum)
     123                 :             :             {
     124                 :          32 :                 checksum_failure = true;
     125         [ +  - ]:          32 :                 if (checksum_failure_p)
     126                 :          32 :                     *checksum_failure_p = true;
     127                 :             :             }
     128                 :             :         }
     129                 :     1382178 :         RESUME_INTERRUPTS();
     130                 :             : 
     131                 :             :         /*
     132                 :             :          * The following checks don't prove the header is correct, only that
     133                 :             :          * it looks sane enough to allow into the buffer pool. Later usage of
     134                 :             :          * the block can still reveal problems, which is why we offer the
     135                 :             :          * checksum option.
     136                 :             :          */
     137         [ +  - ]:     1382178 :         if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
     138         [ +  - ]:     1382178 :             p->pd_lower <= p->pd_upper &&
     139         [ +  - ]:     1382178 :             p->pd_upper <= p->pd_special &&
     140         [ +  + ]:     1382178 :             p->pd_special <= BLCKSZ &&
     141         [ +  - ]:     1382096 :             p->pd_special == MAXALIGN(p->pd_special))
     142                 :     1382096 :             header_sane = true;
     143                 :             : 
     144   [ +  +  +  + ]:     1382178 :         if (header_sane && !checksum_failure)
     145                 :     1382070 :             return true;
     146                 :             :     }
     147                 :             : 
     148                 :             :     /* Check all-zeroes case */
     149                 :        4385 :     pagebytes = (size_t *) page;
     150                 :             : 
     151         [ +  + ]:        4385 :     if (pg_memory_is_all_zeros(pagebytes, BLCKSZ))
     152                 :        4277 :         return true;
     153                 :             : 
     154                 :             :     /*
     155                 :             :      * Throw a WARNING/LOG, as instructed by PIV_LOG_*, if the checksum fails,
     156                 :             :      * but only after we've checked for the all-zeroes case.
     157                 :             :      */
     158         [ +  + ]:         108 :     if (checksum_failure)
     159                 :             :     {
     160         [ +  - ]:          32 :         if ((flags & (PIV_LOG_WARNING | PIV_LOG_LOG)) != 0)
     161   [ -  +  +  -  :          32 :             ereport(flags & PIV_LOG_WARNING ? WARNING : LOG,
                   +  + ]
     162                 :             :                     (errcode(ERRCODE_DATA_CORRUPTED),
     163                 :             :                      (flags & PIV_ZERO_BUFFERS_ON_ERROR) ?
     164                 :             :                      errmsg("page verification failed, calculated checksum %u but expected %u, buffer will be zeroed",
     165                 :             :                             checksum, p->pd_checksum) :
     166                 :             :                      errmsg("page verification failed, calculated checksum %u but expected %u",
     167                 :             :                             checksum, p->pd_checksum)));
     168                 :             : 
     169   [ +  +  +  + ]:          32 :         if (header_sane && (flags & PIV_IGNORE_CHECKSUM_FAILURE))
     170                 :          12 :             return true;
     171                 :             :     }
     172                 :             : 
     173                 :          96 :     return false;
     174                 :             : }
     175                 :             : 
     176                 :             : 
     177                 :             : /*
     178                 :             :  *  PageAddItemExtended
     179                 :             :  *
     180                 :             :  *  Add an item to a page.  Return value is the offset at which it was
     181                 :             :  *  inserted, or InvalidOffsetNumber if the item is not inserted for any
     182                 :             :  *  reason.  A WARNING is issued indicating the reason for the refusal.
     183                 :             :  *
     184                 :             :  *  offsetNumber must be either InvalidOffsetNumber to specify finding a
     185                 :             :  *  free line pointer, or a value between FirstOffsetNumber and one past
     186                 :             :  *  the last existing item, to specify using that particular line pointer.
     187                 :             :  *
     188                 :             :  *  If offsetNumber is valid and flag PAI_OVERWRITE is set, we just store
     189                 :             :  *  the item at the specified offsetNumber, which must be either a
     190                 :             :  *  currently-unused line pointer, or one past the last existing item.
     191                 :             :  *
     192                 :             :  *  If offsetNumber is valid and flag PAI_OVERWRITE is not set, insert
     193                 :             :  *  the item at the specified offsetNumber, moving existing items later
     194                 :             :  *  in the array to make room.
     195                 :             :  *
     196                 :             :  *  If offsetNumber is not valid, then assign a slot by finding the first
     197                 :             :  *  one that is both unused and deallocated.
     198                 :             :  *
     199                 :             :  *  If flag PAI_IS_HEAP is set, we enforce that there can't be more than
     200                 :             :  *  MaxHeapTuplesPerPage line pointers on the page.
     201                 :             :  *
     202                 :             :  *  !!! EREPORT(ERROR) IS DISALLOWED HERE !!!
     203                 :             :  */
     204                 :             : OffsetNumber
     205                 :    48290998 : PageAddItemExtended(Page page,
     206                 :             :                     const void *item,
     207                 :             :                     Size size,
     208                 :             :                     OffsetNumber offsetNumber,
     209                 :             :                     int flags)
     210                 :             : {
     211                 :    48290998 :     PageHeader  phdr = (PageHeader) page;
     212                 :             :     Size        alignedSize;
     213                 :             :     int         lower;
     214                 :             :     int         upper;
     215                 :             :     ItemId      itemId;
     216                 :             :     OffsetNumber limit;
     217                 :    48290998 :     bool        needshuffle = false;
     218                 :             : 
     219                 :             :     /*
     220                 :             :      * Be wary about corrupted page pointers
     221                 :             :      */
     222         [ +  - ]:    48290998 :     if (phdr->pd_lower < SizeOfPageHeaderData ||
     223         [ +  - ]:    48290998 :         phdr->pd_lower > phdr->pd_upper ||
     224         [ +  - ]:    48290998 :         phdr->pd_upper > phdr->pd_special ||
     225         [ -  + ]:    48290998 :         phdr->pd_special > BLCKSZ)
     226         [ #  # ]:           0 :         ereport(PANIC,
     227                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     228                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
     229                 :             :                         phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
     230                 :             : 
     231                 :             :     /*
     232                 :             :      * Select offsetNumber to place the new item at
     233                 :             :      */
     234                 :    48290998 :     limit = OffsetNumberNext(PageGetMaxOffsetNumber(page));
     235                 :             : 
     236                 :             :     /* was offsetNumber passed in? */
     237   [ +  +  +  -  :    48290998 :     if (OffsetNumberIsValid(offsetNumber))
                   +  + ]
     238                 :             :     {
     239                 :             :         /* yes, check it */
     240         [ +  + ]:    29977259 :         if ((flags & PAI_OVERWRITE) != 0)
     241                 :             :         {
     242         [ +  + ]:     1639060 :             if (offsetNumber < limit)
     243                 :             :             {
     244                 :       25047 :                 itemId = PageGetItemId(page, offsetNumber);
     245   [ +  -  -  + ]:       25047 :                 if (ItemIdIsUsed(itemId) || ItemIdHasStorage(itemId))
     246                 :             :                 {
     247         [ #  # ]:           0 :                     elog(WARNING, "will not overwrite a used ItemId");
     248                 :           0 :                     return InvalidOffsetNumber;
     249                 :             :                 }
     250                 :             :             }
     251                 :             :         }
     252                 :             :         else
     253                 :             :         {
     254         [ +  + ]:    28338199 :             if (offsetNumber < limit)
     255                 :     4214666 :                 needshuffle = true; /* need to move existing linp's */
     256                 :             :         }
     257                 :             :     }
     258                 :             :     else
     259                 :             :     {
     260                 :             :         /* offsetNumber was not passed in, so find a free slot */
     261                 :             :         /* if no free slot, we'll put it at limit (1st open slot) */
     262         [ +  + ]:    18313739 :         if (PageHasFreeLinePointers(page))
     263                 :             :         {
     264                 :             :             /*
     265                 :             :              * Scan line pointer array to locate a "recyclable" (unused)
     266                 :             :              * ItemId.
     267                 :             :              *
     268                 :             :              * Always use earlier items first.  PageTruncateLinePointerArray
     269                 :             :              * can only truncate unused items when they appear as a contiguous
     270                 :             :              * group at the end of the line pointer array.
     271                 :             :              */
     272                 :      201092 :             for (offsetNumber = FirstOffsetNumber;
     273         [ +  + ]:    11674757 :                  offsetNumber < limit;   /* limit is maxoff+1 */
     274                 :    11473665 :                  offsetNumber++)
     275                 :             :             {
     276                 :    11662661 :                 itemId = PageGetItemId(page, offsetNumber);
     277                 :             : 
     278                 :             :                 /*
     279                 :             :                  * We check for no storage as well, just to be paranoid;
     280                 :             :                  * unused items should never have storage.  Assert() that the
     281                 :             :                  * invariant is respected too.
     282                 :             :                  */
     283                 :             :                 Assert(ItemIdIsUsed(itemId) || !ItemIdHasStorage(itemId));
     284                 :             : 
     285   [ +  +  +  - ]:    11662661 :                 if (!ItemIdIsUsed(itemId) && !ItemIdHasStorage(itemId))
     286                 :      188996 :                     break;
     287                 :             :             }
     288         [ +  + ]:      201092 :             if (offsetNumber >= limit)
     289                 :             :             {
     290                 :             :                 /* the hint is wrong, so reset it */
     291                 :       12096 :                 PageClearHasFreeLinePointers(page);
     292                 :             :             }
     293                 :             :         }
     294                 :             :         else
     295                 :             :         {
     296                 :             :             /* don't bother searching if hint says there's no free slot */
     297                 :    18112647 :             offsetNumber = limit;
     298                 :             :         }
     299                 :             :     }
     300                 :             : 
     301                 :             :     /* Reject placing items beyond the first unused line pointer */
     302         [ -  + ]:    48290998 :     if (offsetNumber > limit)
     303                 :             :     {
     304         [ #  # ]:           0 :         elog(WARNING, "specified item offset is too large");
     305                 :           0 :         return InvalidOffsetNumber;
     306                 :             :     }
     307                 :             : 
     308                 :             :     /* Reject placing items beyond heap boundary, if heap */
     309   [ +  +  -  + ]:    48290998 :     if ((flags & PAI_IS_HEAP) != 0 && offsetNumber > MaxHeapTuplesPerPage)
     310                 :             :     {
     311         [ #  # ]:           0 :         elog(WARNING, "can't put more than MaxHeapTuplesPerPage items in a heap page");
     312                 :           0 :         return InvalidOffsetNumber;
     313                 :             :     }
     314                 :             : 
     315                 :             :     /*
     316                 :             :      * Compute new lower and upper pointers for page, see if it'll fit.
     317                 :             :      *
     318                 :             :      * Note: do arithmetic as signed ints, to avoid mistakes if, say,
     319                 :             :      * alignedSize > pd_upper.
     320                 :             :      */
     321   [ +  +  +  + ]:    48290998 :     if (offsetNumber == limit || needshuffle)
     322                 :    48076955 :         lower = phdr->pd_lower + sizeof(ItemIdData);
     323                 :             :     else
     324                 :      214043 :         lower = phdr->pd_lower;
     325                 :             : 
     326                 :    48290998 :     alignedSize = MAXALIGN(size);
     327                 :             : 
     328                 :    48290998 :     upper = (int) phdr->pd_upper - (int) alignedSize;
     329                 :             : 
     330         [ -  + ]:    48290998 :     if (lower > upper)
     331                 :           0 :         return InvalidOffsetNumber;
     332                 :             : 
     333                 :             :     /*
     334                 :             :      * OK to insert the item.  First, shuffle the existing pointers if needed.
     335                 :             :      */
     336                 :    48290998 :     itemId = PageGetItemId(page, offsetNumber);
     337                 :             : 
     338         [ +  + ]:    48290998 :     if (needshuffle)
     339                 :     4214666 :         memmove(itemId + 1, itemId,
     340                 :     4214666 :                 (limit - offsetNumber) * sizeof(ItemIdData));
     341                 :             : 
     342                 :             :     /* set the line pointer */
     343                 :    48290998 :     ItemIdSetNormal(itemId, upper, size);
     344                 :             : 
     345                 :             :     /*
     346                 :             :      * Items normally contain no uninitialized bytes.  Core bufpage consumers
     347                 :             :      * conform, but this is not a necessary coding rule; a new index AM could
     348                 :             :      * opt to depart from it.  However, data type input functions and other
     349                 :             :      * C-language functions that synthesize datums should initialize all
     350                 :             :      * bytes; datumIsEqual() relies on this.  Testing here, along with the
     351                 :             :      * similar check in printtup(), helps to catch such mistakes.
     352                 :             :      *
     353                 :             :      * Values of the "name" type retrieved via index-only scans may contain
     354                 :             :      * uninitialized bytes; see comment in btrescan().  Valgrind will report
     355                 :             :      * this as an error, but it is safe to ignore.
     356                 :             :      */
     357                 :             :     VALGRIND_CHECK_MEM_IS_DEFINED(item, size);
     358                 :             : 
     359                 :             :     /* copy the item's data onto the page */
     360                 :    48290998 :     memcpy((char *) page + upper, item, size);
     361                 :             : 
     362                 :             :     /* adjust page header */
     363                 :    48290998 :     phdr->pd_lower = (LocationIndex) lower;
     364                 :    48290998 :     phdr->pd_upper = (LocationIndex) upper;
     365                 :             : 
     366                 :    48290998 :     return offsetNumber;
     367                 :             : }
     368                 :             : 
     369                 :             : 
     370                 :             : /*
     371                 :             :  * PageGetTempPage
     372                 :             :  *      Get a temporary page in local memory for special processing.
     373                 :             :  *      The returned page is not initialized at all; caller must do that.
     374                 :             :  */
     375                 :             : Page
     376                 :         136 : PageGetTempPage(const PageData *page)
     377                 :             : {
     378                 :             :     Size        pageSize;
     379                 :             :     Page        temp;
     380                 :             : 
     381                 :         136 :     pageSize = PageGetPageSize(page);
     382                 :         136 :     temp = (Page) palloc(pageSize);
     383                 :             : 
     384                 :         136 :     return temp;
     385                 :             : }
     386                 :             : 
     387                 :             : /*
     388                 :             :  * PageGetTempPageCopy
     389                 :             :  *      Get a temporary page in local memory for special processing.
     390                 :             :  *      The page is initialized by copying the contents of the given page.
     391                 :             :  */
     392                 :             : Page
     393                 :        7228 : PageGetTempPageCopy(const PageData *page)
     394                 :             : {
     395                 :             :     Size        pageSize;
     396                 :             :     Page        temp;
     397                 :             : 
     398                 :        7228 :     pageSize = PageGetPageSize(page);
     399                 :        7228 :     temp = (Page) palloc(pageSize);
     400                 :             : 
     401                 :        7228 :     memcpy(temp, page, pageSize);
     402                 :             : 
     403                 :        7228 :     return temp;
     404                 :             : }
     405                 :             : 
     406                 :             : /*
     407                 :             :  * PageGetTempPageCopySpecial
     408                 :             :  *      Get a temporary page in local memory for special processing.
     409                 :             :  *      The page is PageInit'd with the same special-space size as the
     410                 :             :  *      given page, and the special space is copied from the given page.
     411                 :             :  */
     412                 :             : Page
     413                 :       38171 : PageGetTempPageCopySpecial(const PageData *page)
     414                 :             : {
     415                 :             :     Size        pageSize;
     416                 :             :     Page        temp;
     417                 :             : 
     418                 :       38171 :     pageSize = PageGetPageSize(page);
     419                 :       38171 :     temp = (Page) palloc(pageSize);
     420                 :             : 
     421                 :       38171 :     PageInit(temp, pageSize, PageGetSpecialSize(page));
     422                 :      114513 :     memcpy(PageGetSpecialPointer(temp),
     423                 :       38171 :            PageGetSpecialPointer(page),
     424                 :       38171 :            PageGetSpecialSize(page));
     425                 :             : 
     426                 :       38171 :     return temp;
     427                 :             : }
     428                 :             : 
     429                 :             : /*
     430                 :             :  * PageRestoreTempPage
     431                 :             :  *      Copy temporary page back to permanent page after special processing
     432                 :             :  *      and release the temporary page.
     433                 :             :  */
     434                 :             : void
     435                 :       35495 : PageRestoreTempPage(Page tempPage, Page oldPage)
     436                 :             : {
     437                 :             :     Size        pageSize;
     438                 :             : 
     439                 :       35495 :     pageSize = PageGetPageSize(tempPage);
     440                 :       35495 :     memcpy(oldPage, tempPage, pageSize);
     441                 :             : 
     442                 :       35495 :     pfree(tempPage);
     443                 :       35495 : }
     444                 :             : 
     445                 :             : /*
     446                 :             :  * Tuple defrag support for PageRepairFragmentation and PageIndexMultiDelete
     447                 :             :  */
     448                 :             : typedef struct itemIdCompactData
     449                 :             : {
     450                 :             :     uint16      offsetindex;    /* linp array index */
     451                 :             :     int16       itemoff;        /* page offset of item data */
     452                 :             :     uint16      alignedlen;     /* MAXALIGN(item data len) */
     453                 :             : } itemIdCompactData;
     454                 :             : typedef itemIdCompactData *itemIdCompact;
     455                 :             : 
     456                 :             : /*
     457                 :             :  * After removing or marking some line pointers unused, move the tuples to
     458                 :             :  * remove the gaps caused by the removed items and reorder them back into
     459                 :             :  * reverse line pointer order in the page.
     460                 :             :  *
     461                 :             :  * This function can often be fairly hot, so it pays to take some measures to
     462                 :             :  * make it as optimal as possible.
     463                 :             :  *
     464                 :             :  * Callers may pass 'presorted' as true if the 'itemidbase' array is sorted in
     465                 :             :  * descending order of itemoff.  When this is true we can just memmove()
     466                 :             :  * tuples towards the end of the page.  This is quite a common case as it's
     467                 :             :  * the order that tuples are initially inserted into pages.  When we call this
     468                 :             :  * function to defragment the tuples in the page then any new line pointers
     469                 :             :  * added to the page will keep that presorted order, so hitting this case is
     470                 :             :  * still very common for tables that are commonly updated.
     471                 :             :  *
     472                 :             :  * When the 'itemidbase' array is not presorted then we're unable to just
     473                 :             :  * memmove() tuples around freely.  Doing so could cause us to overwrite the
     474                 :             :  * memory belonging to a tuple we've not moved yet.  In this case, we copy all
     475                 :             :  * the tuples that need to be moved into a temporary buffer.  We can then
     476                 :             :  * simply memcpy() out of that temp buffer back into the page at the correct
     477                 :             :  * location.  Tuples are copied back into the page in the same order as the
     478                 :             :  * 'itemidbase' array, so we end up reordering the tuples back into reverse
     479                 :             :  * line pointer order.  This will increase the chances of hitting the
     480                 :             :  * presorted case the next time around.
     481                 :             :  *
     482                 :             :  * Callers must ensure that nitems is > 0
     483                 :             :  */
     484                 :             : static void
     485                 :       82689 : compactify_tuples(itemIdCompact itemidbase, int nitems, Page page, bool presorted)
     486                 :             : {
     487                 :       82689 :     PageHeader  phdr = (PageHeader) page;
     488                 :             :     Offset      upper;
     489                 :             :     Offset      copy_tail;
     490                 :             :     Offset      copy_head;
     491                 :             :     itemIdCompact itemidptr;
     492                 :             :     int         i;
     493                 :             : 
     494                 :             :     /* Code within will not work correctly if nitems == 0 */
     495                 :             :     Assert(nitems > 0);
     496                 :             : 
     497         [ +  + ]:       82689 :     if (presorted)
     498                 :             :     {
     499                 :             : 
     500                 :             : #ifdef USE_ASSERT_CHECKING
     501                 :             :         {
     502                 :             :             /*
     503                 :             :              * Verify we've not gotten any new callers that are incorrectly
     504                 :             :              * passing a true presorted value.
     505                 :             :              */
     506                 :             :             Offset      lastoff = phdr->pd_special;
     507                 :             : 
     508                 :             :             for (i = 0; i < nitems; i++)
     509                 :             :             {
     510                 :             :                 itemidptr = &itemidbase[i];
     511                 :             : 
     512                 :             :                 Assert(lastoff > itemidptr->itemoff);
     513                 :             : 
     514                 :             :                 lastoff = itemidptr->itemoff;
     515                 :             :             }
     516                 :             :         }
     517                 :             : #endif                          /* USE_ASSERT_CHECKING */
     518                 :             : 
     519                 :             :         /*
     520                 :             :          * 'itemidbase' is already in the optimal order, i.e, lower item
     521                 :             :          * pointers have a higher offset.  This allows us to memmove() the
     522                 :             :          * tuples up to the end of the page without having to worry about
     523                 :             :          * overwriting other tuples that have not been moved yet.
     524                 :             :          *
     525                 :             :          * There's a good chance that there are tuples already right at the
     526                 :             :          * end of the page that we can simply skip over because they're
     527                 :             :          * already in the correct location within the page.  We'll do that
     528                 :             :          * first...
     529                 :             :          */
     530                 :       60377 :         upper = phdr->pd_special;
     531                 :       60377 :         i = 0;
     532                 :             :         do
     533                 :             :         {
     534                 :      903278 :             itemidptr = &itemidbase[i];
     535         [ +  + ]:      903278 :             if (upper != itemidptr->itemoff + itemidptr->alignedlen)
     536                 :       53190 :                 break;
     537                 :      850088 :             upper -= itemidptr->alignedlen;
     538                 :             : 
     539                 :      850088 :             i++;
     540         [ +  + ]:      850088 :         } while (i < nitems);
     541                 :             : 
     542                 :             :         /*
     543                 :             :          * Now that we've found the first tuple that needs to be moved, we can
     544                 :             :          * do the tuple compactification.  We try and make the least number of
     545                 :             :          * memmove() calls and only call memmove() when there's a gap.  When
     546                 :             :          * we see a gap we just move all tuples after the gap up until the
     547                 :             :          * point of the last move operation.
     548                 :             :          */
     549                 :       60377 :         copy_tail = copy_head = itemidptr->itemoff + itemidptr->alignedlen;
     550         [ +  + ]:     1371757 :         for (; i < nitems; i++)
     551                 :             :         {
     552                 :             :             ItemId      lp;
     553                 :             : 
     554                 :     1311380 :             itemidptr = &itemidbase[i];
     555                 :     1311380 :             lp = PageGetItemId(page, itemidptr->offsetindex + 1);
     556                 :             : 
     557         [ +  + ]:     1311380 :             if (copy_head != itemidptr->itemoff + itemidptr->alignedlen)
     558                 :             :             {
     559                 :      164610 :                 memmove((char *) page + upper,
     560                 :      164610 :                         page + copy_head,
     561                 :      164610 :                         copy_tail - copy_head);
     562                 :             : 
     563                 :             :                 /*
     564                 :             :                  * We've now moved all tuples already seen, but not the
     565                 :             :                  * current tuple, so we set the copy_tail to the end of this
     566                 :             :                  * tuple so it can be moved in another iteration of the loop.
     567                 :             :                  */
     568                 :      164610 :                 copy_tail = itemidptr->itemoff + itemidptr->alignedlen;
     569                 :             :             }
     570                 :             :             /* shift the target offset down by the length of this tuple */
     571                 :     1311380 :             upper -= itemidptr->alignedlen;
     572                 :             :             /* point the copy_head to the start of this tuple */
     573                 :     1311380 :             copy_head = itemidptr->itemoff;
     574                 :             : 
     575                 :             :             /* update the line pointer to reference the new offset */
     576                 :     1311380 :             lp->lp_off = upper;
     577                 :             :         }
     578                 :             : 
     579                 :             :         /* move the remaining tuples. */
     580                 :       60377 :         memmove((char *) page + upper,
     581                 :       60377 :                 page + copy_head,
     582                 :       60377 :                 copy_tail - copy_head);
     583                 :             :     }
     584                 :             :     else
     585                 :             :     {
     586                 :             :         PGAlignedBlock scratch;
     587                 :       22312 :         char       *scratchptr = scratch.data;
     588                 :             : 
     589                 :             :         /*
     590                 :             :          * Non-presorted case:  The tuples in the itemidbase array may be in
     591                 :             :          * any order.  So, in order to move these to the end of the page we
     592                 :             :          * must make a temp copy of each tuple that needs to be moved before
     593                 :             :          * we copy them back into the page at the new offset.
     594                 :             :          *
     595                 :             :          * If a large percentage of tuples have been pruned (>75%) then we'll
     596                 :             :          * copy these into the temp buffer tuple-by-tuple, otherwise, we'll
     597                 :             :          * just do a single memcpy() for all tuples that need to be moved.
     598                 :             :          * When so many tuples have been removed there's likely to be a lot of
     599                 :             :          * gaps and it's unlikely that many non-movable tuples remain at the
     600                 :             :          * end of the page.
     601                 :             :          */
     602         [ +  + ]:       22312 :         if (nitems < PageGetMaxOffsetNumber(page) / 4)
     603                 :             :         {
     604                 :        1354 :             i = 0;
     605                 :             :             do
     606                 :             :             {
     607                 :       30740 :                 itemidptr = &itemidbase[i];
     608                 :       30740 :                 memcpy(scratchptr + itemidptr->itemoff, page + itemidptr->itemoff,
     609                 :       30740 :                        itemidptr->alignedlen);
     610                 :       30740 :                 i++;
     611         [ +  + ]:       30740 :             } while (i < nitems);
     612                 :             : 
     613                 :             :             /* Set things up for the compactification code below */
     614                 :        1354 :             i = 0;
     615                 :        1354 :             itemidptr = &itemidbase[0];
     616                 :        1354 :             upper = phdr->pd_special;
     617                 :             :         }
     618                 :             :         else
     619                 :             :         {
     620                 :       20958 :             upper = phdr->pd_special;
     621                 :             : 
     622                 :             :             /*
     623                 :             :              * Many tuples are likely to already be in the correct location.
     624                 :             :              * There's no need to copy these into the temp buffer.  Instead
     625                 :             :              * we'll just skip forward in the itemidbase array to the position
     626                 :             :              * that we do need to move tuples from so that the code below just
     627                 :             :              * leaves these ones alone.
     628                 :             :              */
     629                 :       20958 :             i = 0;
     630                 :             :             do
     631                 :             :             {
     632                 :      517553 :                 itemidptr = &itemidbase[i];
     633         [ +  + ]:      517553 :                 if (upper != itemidptr->itemoff + itemidptr->alignedlen)
     634                 :       20958 :                     break;
     635                 :      496595 :                 upper -= itemidptr->alignedlen;
     636                 :             : 
     637                 :      496595 :                 i++;
     638         [ +  - ]:      496595 :             } while (i < nitems);
     639                 :             : 
     640                 :             :             /* Copy all tuples that need to be moved into the temp buffer */
     641                 :       20958 :             memcpy(scratchptr + phdr->pd_upper,
     642                 :       20958 :                    page + phdr->pd_upper,
     643                 :       20958 :                    upper - phdr->pd_upper);
     644                 :             :         }
     645                 :             : 
     646                 :             :         /*
     647                 :             :          * Do the tuple compactification.  itemidptr is already pointing to
     648                 :             :          * the first tuple that we're going to move.  Here we collapse the
     649                 :             :          * memcpy calls for adjacent tuples into a single call.  This is done
     650                 :             :          * by delaying the memcpy call until we find a gap that needs to be
     651                 :             :          * closed.
     652                 :             :          */
     653                 :       22312 :         copy_tail = copy_head = itemidptr->itemoff + itemidptr->alignedlen;
     654         [ +  + ]:     2327325 :         for (; i < nitems; i++)
     655                 :             :         {
     656                 :             :             ItemId      lp;
     657                 :             : 
     658                 :     2305013 :             itemidptr = &itemidbase[i];
     659                 :     2305013 :             lp = PageGetItemId(page, itemidptr->offsetindex + 1);
     660                 :             : 
     661                 :             :             /* copy pending tuples when we detect a gap */
     662         [ +  + ]:     2305013 :             if (copy_head != itemidptr->itemoff + itemidptr->alignedlen)
     663                 :             :             {
     664                 :      640597 :                 memcpy((char *) page + upper,
     665                 :      640597 :                        scratchptr + copy_head,
     666                 :      640597 :                        copy_tail - copy_head);
     667                 :             : 
     668                 :             :                 /*
     669                 :             :                  * We've now copied all tuples already seen, but not the
     670                 :             :                  * current tuple, so we set the copy_tail to the end of this
     671                 :             :                  * tuple.
     672                 :             :                  */
     673                 :      640597 :                 copy_tail = itemidptr->itemoff + itemidptr->alignedlen;
     674                 :             :             }
     675                 :             :             /* shift the target offset down by the length of this tuple */
     676                 :     2305013 :             upper -= itemidptr->alignedlen;
     677                 :             :             /* point the copy_head to the start of this tuple */
     678                 :     2305013 :             copy_head = itemidptr->itemoff;
     679                 :             : 
     680                 :             :             /* update the line pointer to reference the new offset */
     681                 :     2305013 :             lp->lp_off = upper;
     682                 :             :         }
     683                 :             : 
     684                 :             :         /* Copy the remaining chunk */
     685                 :       22312 :         memcpy((char *) page + upper,
     686                 :       22312 :                scratchptr + copy_head,
     687                 :       22312 :                copy_tail - copy_head);
     688                 :             :     }
     689                 :             : 
     690                 :       82689 :     phdr->pd_upper = upper;
     691                 :       82689 : }
     692                 :             : 
     693                 :             : /*
     694                 :             :  * PageRepairFragmentation
     695                 :             :  *
     696                 :             :  * Frees fragmented space on a heap page following pruning.
     697                 :             :  *
     698                 :             :  * This routine is usable for heap pages only, but see PageIndexMultiDelete.
     699                 :             :  *
     700                 :             :  * This routine removes unused line pointers from the end of the line pointer
     701                 :             :  * array.  This is possible when dead heap-only tuples get removed by pruning,
     702                 :             :  * especially when there were HOT chains with several tuples each beforehand.
     703                 :             :  *
     704                 :             :  * Caller had better have a full cleanup lock on page's buffer.  As a side
     705                 :             :  * effect the page's PD_HAS_FREE_LINES hint bit will be set or unset as
     706                 :             :  * needed.  Caller might also need to account for a reduction in the length of
     707                 :             :  * the line pointer array following array truncation.
     708                 :             :  */
     709                 :             : void
     710                 :       82006 : PageRepairFragmentation(Page page)
     711                 :             : {
     712                 :       82006 :     Offset      pd_lower = ((PageHeader) page)->pd_lower;
     713                 :       82006 :     Offset      pd_upper = ((PageHeader) page)->pd_upper;
     714                 :       82006 :     Offset      pd_special = ((PageHeader) page)->pd_special;
     715                 :             :     Offset      last_offset;
     716                 :             :     itemIdCompactData itemidbase[MaxHeapTuplesPerPage];
     717                 :             :     itemIdCompact itemidptr;
     718                 :             :     ItemId      lp;
     719                 :             :     int         nline,
     720                 :             :                 nstorage,
     721                 :             :                 nunused;
     722                 :       82006 :     OffsetNumber finalusedlp = InvalidOffsetNumber;
     723                 :             :     int         i;
     724                 :             :     Size        totallen;
     725                 :       82006 :     bool        presorted = true;   /* For now */
     726                 :             : 
     727                 :             :     /*
     728                 :             :      * It's worth the trouble to be more paranoid here than in most places,
     729                 :             :      * because we are about to reshuffle data in (what is usually) a shared
     730                 :             :      * disk buffer.  If we aren't careful then corrupted pointers, lengths,
     731                 :             :      * etc could cause us to clobber adjacent disk buffers, spreading the data
     732                 :             :      * loss further.  So, check everything.
     733                 :             :      */
     734   [ +  -  +  - ]:       82006 :     if (pd_lower < SizeOfPageHeaderData ||
     735         [ +  - ]:       82006 :         pd_lower > pd_upper ||
     736         [ +  - ]:       82006 :         pd_upper > pd_special ||
     737                 :       82006 :         pd_special > BLCKSZ ||
     738         [ -  + ]:       82006 :         pd_special != MAXALIGN(pd_special))
     739         [ #  # ]:           0 :         ereport(ERROR,
     740                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     741                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
     742                 :             :                         pd_lower, pd_upper, pd_special)));
     743                 :             : 
     744                 :             :     /*
     745                 :             :      * Run through the line pointer array and collect data about live items.
     746                 :             :      */
     747                 :       82006 :     nline = PageGetMaxOffsetNumber(page);
     748                 :       82006 :     itemidptr = itemidbase;
     749                 :       82006 :     nunused = totallen = 0;
     750                 :       82006 :     last_offset = pd_special;
     751         [ +  + ]:     8398264 :     for (i = FirstOffsetNumber; i <= nline; i++)
     752                 :             :     {
     753                 :     8316258 :         lp = PageGetItemId(page, i);
     754         [ +  + ]:     8316258 :         if (ItemIdIsUsed(lp))
     755                 :             :         {
     756         [ +  + ]:     8007610 :             if (ItemIdHasStorage(lp))
     757                 :             :             {
     758                 :     2038556 :                 itemidptr->offsetindex = i - 1;
     759                 :     2038556 :                 itemidptr->itemoff = ItemIdGetOffset(lp);
     760                 :             : 
     761         [ +  + ]:     2038556 :                 if (last_offset > itemidptr->itemoff)
     762                 :     1734811 :                     last_offset = itemidptr->itemoff;
     763                 :             :                 else
     764                 :      303745 :                     presorted = false;
     765                 :             : 
     766   [ +  -  -  +  :     2038556 :                 if (unlikely(itemidptr->itemoff < (int) pd_upper ||
                   -  + ]
     767                 :             :                              itemidptr->itemoff >= (int) pd_special))
     768         [ #  # ]:           0 :                     ereport(ERROR,
     769                 :             :                             (errcode(ERRCODE_DATA_CORRUPTED),
     770                 :             :                              errmsg("corrupted line pointer: %u",
     771                 :             :                                     itemidptr->itemoff)));
     772                 :     2038556 :                 itemidptr->alignedlen = MAXALIGN(ItemIdGetLength(lp));
     773                 :     2038556 :                 totallen += itemidptr->alignedlen;
     774                 :     2038556 :                 itemidptr++;
     775                 :             :             }
     776                 :             : 
     777                 :     8007610 :             finalusedlp = i;    /* Could be the final non-LP_UNUSED item */
     778                 :             :         }
     779                 :             :         else
     780                 :             :         {
     781                 :             :             /* Unused entries should have lp_len = 0, but make sure */
     782                 :             :             Assert(!ItemIdHasStorage(lp));
     783                 :      308648 :             ItemIdSetUnused(lp);
     784                 :      308648 :             nunused++;
     785                 :             :         }
     786                 :             :     }
     787                 :             : 
     788                 :       82006 :     nstorage = itemidptr - itemidbase;
     789         [ +  + ]:       82006 :     if (nstorage == 0)
     790                 :             :     {
     791                 :             :         /* Page is completely empty, so just reset it quickly */
     792                 :       24177 :         ((PageHeader) page)->pd_upper = pd_special;
     793                 :             :     }
     794                 :             :     else
     795                 :             :     {
     796                 :             :         /* Need to compact the page the hard way */
     797         [ -  + ]:       57829 :         if (totallen > (Size) (pd_special - pd_lower))
     798         [ #  # ]:           0 :             ereport(ERROR,
     799                 :             :                     (errcode(ERRCODE_DATA_CORRUPTED),
     800                 :             :                      errmsg("corrupted item lengths: total %zu, available space %u",
     801                 :             :                             totallen, pd_special - pd_lower)));
     802                 :             : 
     803                 :       57829 :         compactify_tuples(itemidbase, nstorage, page, presorted);
     804                 :             :     }
     805                 :             : 
     806         [ +  + ]:       82006 :     if (finalusedlp != nline)
     807                 :             :     {
     808                 :             :         /* The last line pointer is not the last used line pointer */
     809                 :        2247 :         int         nunusedend = nline - finalusedlp;
     810                 :             : 
     811                 :             :         Assert(nunused >= nunusedend && nunusedend > 0);
     812                 :             : 
     813                 :             :         /* remove trailing unused line pointers from the count */
     814                 :        2247 :         nunused -= nunusedend;
     815                 :             :         /* truncate the line pointer array */
     816                 :        2247 :         ((PageHeader) page)->pd_lower -= (sizeof(ItemIdData) * nunusedend);
     817                 :             :     }
     818                 :             : 
     819                 :             :     /* Set hint bit for PageAddItemExtended */
     820         [ +  + ]:       82006 :     if (nunused > 0)
     821                 :       19265 :         PageSetHasFreeLinePointers(page);
     822                 :             :     else
     823                 :       62741 :         PageClearHasFreeLinePointers(page);
     824                 :       82006 : }
     825                 :             : 
     826                 :             : /*
     827                 :             :  * PageTruncateLinePointerArray
     828                 :             :  *
     829                 :             :  * Removes unused line pointers at the end of the line pointer array.
     830                 :             :  *
     831                 :             :  * This routine is usable for heap pages only.  It is called by VACUUM during
     832                 :             :  * its second pass over the heap.  We expect at least one LP_UNUSED line
     833                 :             :  * pointer on the page (if VACUUM didn't have an LP_DEAD item on the page that
     834                 :             :  * it just set to LP_UNUSED then it should not call here).
     835                 :             :  *
     836                 :             :  * We avoid truncating the line pointer array to 0 items, if necessary by
     837                 :             :  * leaving behind a single remaining LP_UNUSED item.  This is a little
     838                 :             :  * arbitrary, but it seems like a good idea to avoid leaving a PageIsEmpty()
     839                 :             :  * page behind.
     840                 :             :  *
     841                 :             :  * Caller can have either an exclusive lock or a full cleanup lock on page's
     842                 :             :  * buffer.  The page's PD_HAS_FREE_LINES hint bit will be set or unset based
     843                 :             :  * on whether or not we leave behind any remaining LP_UNUSED items.
     844                 :             :  */
     845                 :             : void
     846                 :       20746 : PageTruncateLinePointerArray(Page page)
     847                 :             : {
     848                 :       20746 :     PageHeader  phdr = (PageHeader) page;
     849                 :       20746 :     bool        countdone = false,
     850                 :       20746 :                 sethint = false;
     851                 :       20746 :     int         nunusedend = 0;
     852                 :             : 
     853                 :             :     /* Scan line pointer array back-to-front */
     854         [ +  + ]:     1385061 :     for (int i = PageGetMaxOffsetNumber(page); i >= FirstOffsetNumber; i--)
     855                 :             :     {
     856                 :     1384324 :         ItemId      lp = PageGetItemId(page, i);
     857                 :             : 
     858   [ +  +  +  + ]:     1384324 :         if (!countdone && i > FirstOffsetNumber)
     859                 :             :         {
     860                 :             :             /*
     861                 :             :              * Still determining which line pointers from the end of the array
     862                 :             :              * will be truncated away.  Either count another line pointer as
     863                 :             :              * safe to truncate, or notice that it's not safe to truncate
     864                 :             :              * additional line pointers (stop counting line pointers).
     865                 :             :              */
     866         [ +  + ]:     1256550 :             if (!ItemIdIsUsed(lp))
     867                 :     1247179 :                 nunusedend++;
     868                 :             :             else
     869                 :        9371 :                 countdone = true;
     870                 :             :         }
     871                 :             :         else
     872                 :             :         {
     873                 :             :             /*
     874                 :             :              * Once we've stopped counting we still need to figure out if
     875                 :             :              * there are any remaining LP_UNUSED line pointers somewhere more
     876                 :             :              * towards the front of the array.
     877                 :             :              */
     878         [ +  + ]:      127774 :             if (!ItemIdIsUsed(lp))
     879                 :             :             {
     880                 :             :                 /*
     881                 :             :                  * This is an unused line pointer that we won't be truncating
     882                 :             :                  * away -- so there is at least one.  Set hint on page.
     883                 :             :                  */
     884                 :       20009 :                 sethint = true;
     885                 :       20009 :                 break;
     886                 :             :             }
     887                 :             :         }
     888                 :             :     }
     889                 :             : 
     890         [ +  + ]:       20746 :     if (nunusedend > 0)
     891                 :             :     {
     892                 :       14768 :         phdr->pd_lower -= sizeof(ItemIdData) * nunusedend;
     893                 :             : 
     894                 :             : #ifdef CLOBBER_FREED_MEMORY
     895                 :             :         memset((char *) page + phdr->pd_lower, 0x7F,
     896                 :             :                sizeof(ItemIdData) * nunusedend);
     897                 :             : #endif
     898                 :             :     }
     899                 :             :     else
     900                 :             :         Assert(sethint);
     901                 :             : 
     902                 :             :     /* Set hint bit for PageAddItemExtended */
     903         [ +  + ]:       20746 :     if (sethint)
     904                 :       20009 :         PageSetHasFreeLinePointers(page);
     905                 :             :     else
     906                 :         737 :         PageClearHasFreeLinePointers(page);
     907                 :       20746 : }
     908                 :             : 
     909                 :             : /*
     910                 :             :  * PageGetFreeSpace
     911                 :             :  *      Returns the size of the free (allocatable) space on a page,
     912                 :             :  *      reduced by the space needed for a new line pointer.
     913                 :             :  *
     914                 :             :  * Note: this should usually only be used on index pages.  Use
     915                 :             :  * PageGetHeapFreeSpace on heap pages.
     916                 :             :  */
     917                 :             : Size
     918                 :    43977665 : PageGetFreeSpace(const PageData *page)
     919                 :             : {
     920                 :    43977665 :     const PageHeaderData *phdr = (const PageHeaderData *) page;
     921                 :             :     int         space;
     922                 :             : 
     923                 :             :     /*
     924                 :             :      * Use signed arithmetic here so that we behave sensibly if pd_lower >
     925                 :             :      * pd_upper.
     926                 :             :      */
     927                 :    43977665 :     space = (int) phdr->pd_upper - (int) phdr->pd_lower;
     928                 :             : 
     929         [ +  + ]:    43977665 :     if (space < (int) sizeof(ItemIdData))
     930                 :        8923 :         return 0;
     931                 :    43968742 :     space -= sizeof(ItemIdData);
     932                 :             : 
     933                 :    43968742 :     return (Size) space;
     934                 :             : }
     935                 :             : 
     936                 :             : /*
     937                 :             :  * PageGetFreeSpaceForMultipleTuples
     938                 :             :  *      Returns the size of the free (allocatable) space on a page,
     939                 :             :  *      reduced by the space needed for multiple new line pointers.
     940                 :             :  *
     941                 :             :  * Note: this should usually only be used on index pages.  Use
     942                 :             :  * PageGetHeapFreeSpace on heap pages.
     943                 :             :  */
     944                 :             : Size
     945                 :       78819 : PageGetFreeSpaceForMultipleTuples(const PageData *page, int ntups)
     946                 :             : {
     947                 :       78819 :     const PageHeaderData *phdr = (const PageHeaderData *) page;
     948                 :             :     int         space;
     949                 :             : 
     950                 :             :     /*
     951                 :             :      * Use signed arithmetic here so that we behave sensibly if pd_lower >
     952                 :             :      * pd_upper.
     953                 :             :      */
     954                 :       78819 :     space = (int) phdr->pd_upper - (int) phdr->pd_lower;
     955                 :             : 
     956         [ -  + ]:       78819 :     if (space < (int) (ntups * sizeof(ItemIdData)))
     957                 :           0 :         return 0;
     958                 :       78819 :     space -= ntups * sizeof(ItemIdData);
     959                 :             : 
     960                 :       78819 :     return (Size) space;
     961                 :             : }
     962                 :             : 
     963                 :             : /*
     964                 :             :  * PageGetExactFreeSpace
     965                 :             :  *      Returns the size of the free (allocatable) space on a page,
     966                 :             :  *      without any consideration for adding/removing line pointers.
     967                 :             :  */
     968                 :             : Size
     969                 :     2150200 : PageGetExactFreeSpace(const PageData *page)
     970                 :             : {
     971                 :     2150200 :     const PageHeaderData *phdr = (const PageHeaderData *) page;
     972                 :             :     int         space;
     973                 :             : 
     974                 :             :     /*
     975                 :             :      * Use signed arithmetic here so that we behave sensibly if pd_lower >
     976                 :             :      * pd_upper.
     977                 :             :      */
     978                 :     2150200 :     space = (int) phdr->pd_upper - (int) phdr->pd_lower;
     979                 :             : 
     980         [ -  + ]:     2150200 :     if (space < 0)
     981                 :           0 :         return 0;
     982                 :             : 
     983                 :     2150200 :     return (Size) space;
     984                 :             : }
     985                 :             : 
     986                 :             : 
     987                 :             : /*
     988                 :             :  * PageGetHeapFreeSpace
     989                 :             :  *      Returns the size of the free (allocatable) space on a page,
     990                 :             :  *      reduced by the space needed for a new line pointer.
     991                 :             :  *
     992                 :             :  * The difference between this and PageGetFreeSpace is that this will return
     993                 :             :  * zero if there are already MaxHeapTuplesPerPage line pointers in the page
     994                 :             :  * and none are free.  We use this to enforce that no more than
     995                 :             :  * MaxHeapTuplesPerPage line pointers are created on a heap page.  (Although
     996                 :             :  * no more tuples than that could fit anyway, in the presence of redirected
     997                 :             :  * or dead line pointers it'd be possible to have too many line pointers.
     998                 :             :  * To avoid breaking code that assumes MaxHeapTuplesPerPage is a hard limit
     999                 :             :  * on the number of line pointers, we make this extra check.)
    1000                 :             :  */
    1001                 :             : Size
    1002                 :    23703489 : PageGetHeapFreeSpace(const PageData *page)
    1003                 :             : {
    1004                 :             :     Size        space;
    1005                 :             : 
    1006                 :    23703489 :     space = PageGetFreeSpace(page);
    1007         [ +  + ]:    23703489 :     if (space > 0)
    1008                 :             :     {
    1009                 :             :         OffsetNumber offnum,
    1010                 :             :                     nline;
    1011                 :             : 
    1012                 :             :         /*
    1013                 :             :          * Are there already MaxHeapTuplesPerPage line pointers in the page?
    1014                 :             :          */
    1015                 :    23674063 :         nline = PageGetMaxOffsetNumber(page);
    1016         [ +  + ]:    23674063 :         if (nline >= MaxHeapTuplesPerPage)
    1017                 :             :         {
    1018         [ +  + ]:        6079 :             if (PageHasFreeLinePointers(page))
    1019                 :             :             {
    1020                 :             :                 /*
    1021                 :             :                  * Since this is just a hint, we must confirm that there is
    1022                 :             :                  * indeed a free line pointer
    1023                 :             :                  */
    1024         [ +  + ]:      504508 :                 for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum))
    1025                 :             :                 {
    1026                 :      504398 :                     ItemId      lp = PageGetItemId(unconstify(PageData *, page), offnum);
    1027                 :             : 
    1028         [ +  + ]:      504398 :                     if (!ItemIdIsUsed(lp))
    1029                 :        3796 :                         break;
    1030                 :             :                 }
    1031                 :             : 
    1032         [ +  + ]:        3906 :                 if (offnum > nline)
    1033                 :             :                 {
    1034                 :             :                     /*
    1035                 :             :                      * The hint is wrong, but we can't clear it here since we
    1036                 :             :                      * don't have the ability to mark the page dirty.
    1037                 :             :                      */
    1038                 :         110 :                     space = 0;
    1039                 :             :                 }
    1040                 :             :             }
    1041                 :             :             else
    1042                 :             :             {
    1043                 :             :                 /*
    1044                 :             :                  * Although the hint might be wrong, PageAddItem will believe
    1045                 :             :                  * it anyway, so we must believe it too.
    1046                 :             :                  */
    1047                 :        2173 :                 space = 0;
    1048                 :             :             }
    1049                 :             :         }
    1050                 :             :     }
    1051                 :    23703489 :     return space;
    1052                 :             : }
    1053                 :             : 
    1054                 :             : 
    1055                 :             : /*
    1056                 :             :  * PageIndexTupleDelete
    1057                 :             :  *
    1058                 :             :  * This routine does the work of removing a tuple from an index page.
    1059                 :             :  *
    1060                 :             :  * Unlike heap pages, we compact out the line pointer for the removed tuple.
    1061                 :             :  */
    1062                 :             : void
    1063                 :      629304 : PageIndexTupleDelete(Page page, OffsetNumber offnum)
    1064                 :             : {
    1065                 :      629304 :     PageHeader  phdr = (PageHeader) page;
    1066                 :             :     char       *addr;
    1067                 :             :     ItemId      tup;
    1068                 :             :     Size        size;
    1069                 :             :     unsigned    offset;
    1070                 :             :     int         nbytes;
    1071                 :             :     int         offidx;
    1072                 :             :     int         nline;
    1073                 :             : 
    1074                 :             :     /*
    1075                 :             :      * As with PageRepairFragmentation, paranoia seems justified.
    1076                 :             :      */
    1077         [ +  - ]:      629304 :     if (phdr->pd_lower < SizeOfPageHeaderData ||
    1078         [ +  - ]:      629304 :         phdr->pd_lower > phdr->pd_upper ||
    1079         [ +  - ]:      629304 :         phdr->pd_upper > phdr->pd_special ||
    1080         [ +  - ]:      629304 :         phdr->pd_special > BLCKSZ ||
    1081         [ -  + ]:      629304 :         phdr->pd_special != MAXALIGN(phdr->pd_special))
    1082         [ #  # ]:           0 :         ereport(ERROR,
    1083                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1084                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
    1085                 :             :                         phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
    1086                 :             : 
    1087                 :      629304 :     nline = PageGetMaxOffsetNumber(page);
    1088   [ +  -  -  + ]:      629304 :     if ((int) offnum <= 0 || (int) offnum > nline)
    1089         [ #  # ]:           0 :         elog(ERROR, "invalid index offnum: %u", offnum);
    1090                 :             : 
    1091                 :             :     /* change offset number to offset index */
    1092                 :      629304 :     offidx = offnum - 1;
    1093                 :             : 
    1094                 :      629304 :     tup = PageGetItemId(page, offnum);
    1095                 :             :     Assert(ItemIdHasStorage(tup));
    1096                 :      629304 :     size = ItemIdGetLength(tup);
    1097                 :      629304 :     offset = ItemIdGetOffset(tup);
    1098                 :             : 
    1099   [ +  -  +  - ]:      629304 :     if (offset < phdr->pd_upper || (offset + size) > phdr->pd_special ||
    1100         [ -  + ]:      629304 :         offset != MAXALIGN(offset))
    1101         [ #  # ]:           0 :         ereport(ERROR,
    1102                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1103                 :             :                  errmsg("corrupted line pointer: offset = %u, size = %zu",
    1104                 :             :                         offset, size)));
    1105                 :             : 
    1106                 :             :     /* Amount of space to actually be deleted */
    1107                 :      629304 :     size = MAXALIGN(size);
    1108                 :             : 
    1109                 :             :     /*
    1110                 :             :      * First, we want to get rid of the pd_linp entry for the index tuple. We
    1111                 :             :      * copy all subsequent linp's back one slot in the array. We don't use
    1112                 :             :      * PageGetItemId, because we are manipulating the _array_, not individual
    1113                 :             :      * linp's.
    1114                 :             :      */
    1115                 :      629304 :     nbytes = phdr->pd_lower -
    1116                 :      629304 :         ((char *) &phdr->pd_linp[offidx + 1] - (char *) phdr);
    1117                 :             : 
    1118         [ +  + ]:      629304 :     if (nbytes > 0)
    1119                 :      613294 :         memmove(&(phdr->pd_linp[offidx]),
    1120                 :      613294 :                 &(phdr->pd_linp[offidx + 1]),
    1121                 :             :                 nbytes);
    1122                 :             : 
    1123                 :             :     /*
    1124                 :             :      * Now move everything between the old upper bound (beginning of tuple
    1125                 :             :      * space) and the beginning of the deleted tuple forward, so that space in
    1126                 :             :      * the middle of the page is left free.  If we've just deleted the tuple
    1127                 :             :      * at the beginning of tuple space, then there's no need to do the copy.
    1128                 :             :      */
    1129                 :             : 
    1130                 :             :     /* beginning of tuple space */
    1131                 :      629304 :     addr = (char *) page + phdr->pd_upper;
    1132                 :             : 
    1133         [ +  + ]:      629304 :     if (offset > phdr->pd_upper)
    1134                 :      613948 :         memmove(addr + size, addr, offset - phdr->pd_upper);
    1135                 :             : 
    1136                 :             :     /* adjust free space boundary pointers */
    1137                 :      629304 :     phdr->pd_upper += size;
    1138                 :      629304 :     phdr->pd_lower -= sizeof(ItemIdData);
    1139                 :             : 
    1140                 :             :     /*
    1141                 :             :      * Finally, we need to adjust the linp entries that remain.
    1142                 :             :      *
    1143                 :             :      * Anything that used to be before the deleted tuple's data was moved
    1144                 :             :      * forward by the size of the deleted tuple.
    1145                 :             :      */
    1146         [ +  + ]:      629304 :     if (!PageIsEmpty(page))
    1147                 :             :     {
    1148                 :             :         int         i;
    1149                 :             : 
    1150                 :      628505 :         nline--;                /* there's one less than when we started */
    1151         [ +  + ]:    96919354 :         for (i = 1; i <= nline; i++)
    1152                 :             :         {
    1153                 :    96290849 :             ItemId      ii = PageGetItemId(page, i);
    1154                 :             : 
    1155                 :             :             Assert(ItemIdHasStorage(ii));
    1156         [ +  + ]:    96290849 :             if (ItemIdGetOffset(ii) <= offset)
    1157                 :    62861122 :                 ii->lp_off += size;
    1158                 :             :         }
    1159                 :             :     }
    1160                 :      629304 : }
    1161                 :             : 
    1162                 :             : 
    1163                 :             : /*
    1164                 :             :  * PageIndexMultiDelete
    1165                 :             :  *
    1166                 :             :  * This routine handles the case of deleting multiple tuples from an
    1167                 :             :  * index page at once.  It is considerably faster than a loop around
    1168                 :             :  * PageIndexTupleDelete ... however, the caller *must* supply the array
    1169                 :             :  * of item numbers to be deleted in item number order!
    1170                 :             :  */
    1171                 :             : void
    1172                 :       28353 : PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
    1173                 :             : {
    1174                 :       28353 :     PageHeader  phdr = (PageHeader) page;
    1175                 :       28353 :     Offset      pd_lower = phdr->pd_lower;
    1176                 :       28353 :     Offset      pd_upper = phdr->pd_upper;
    1177                 :       28353 :     Offset      pd_special = phdr->pd_special;
    1178                 :             :     Offset      last_offset;
    1179                 :             :     itemIdCompactData itemidbase[MaxIndexTuplesPerPage];
    1180                 :             :     ItemIdData  newitemids[MaxIndexTuplesPerPage];
    1181                 :             :     itemIdCompact itemidptr;
    1182                 :             :     ItemId      lp;
    1183                 :             :     int         nline,
    1184                 :             :                 nused;
    1185                 :             :     Size        totallen;
    1186                 :             :     Size        size;
    1187                 :             :     unsigned    offset;
    1188                 :             :     int         nextitm;
    1189                 :             :     OffsetNumber offnum;
    1190                 :       28353 :     bool        presorted = true;   /* For now */
    1191                 :             : 
    1192                 :             :     Assert(nitems <= MaxIndexTuplesPerPage);
    1193                 :             : 
    1194                 :             :     /*
    1195                 :             :      * If there aren't very many items to delete, then retail
    1196                 :             :      * PageIndexTupleDelete is the best way.  Delete the items in reverse
    1197                 :             :      * order so we don't have to think about adjusting item numbers for
    1198                 :             :      * previous deletions.
    1199                 :             :      *
    1200                 :             :      * TODO: tune the magic number here
    1201                 :             :      */
    1202         [ +  + ]:       28353 :     if (nitems <= 2)
    1203                 :             :     {
    1204         [ +  + ]:        6842 :         while (--nitems >= 0)
    1205                 :        3927 :             PageIndexTupleDelete(page, itemnos[nitems]);
    1206                 :        2915 :         return;
    1207                 :             :     }
    1208                 :             : 
    1209                 :             :     /*
    1210                 :             :      * As with PageRepairFragmentation, paranoia seems justified.
    1211                 :             :      */
    1212   [ +  -  +  - ]:       25438 :     if (pd_lower < SizeOfPageHeaderData ||
    1213         [ +  - ]:       25438 :         pd_lower > pd_upper ||
    1214         [ +  - ]:       25438 :         pd_upper > pd_special ||
    1215                 :       25438 :         pd_special > BLCKSZ ||
    1216         [ -  + ]:       25438 :         pd_special != MAXALIGN(pd_special))
    1217         [ #  # ]:           0 :         ereport(ERROR,
    1218                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1219                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
    1220                 :             :                         pd_lower, pd_upper, pd_special)));
    1221                 :             : 
    1222                 :             :     /*
    1223                 :             :      * Scan the line pointer array and build a list of just the ones we are
    1224                 :             :      * going to keep.  Notice we do not modify the page yet, since we are
    1225                 :             :      * still validity-checking.
    1226                 :             :      */
    1227                 :       25438 :     nline = PageGetMaxOffsetNumber(page);
    1228                 :       25438 :     itemidptr = itemidbase;
    1229                 :       25438 :     totallen = 0;
    1230                 :       25438 :     nused = 0;
    1231                 :       25438 :     nextitm = 0;
    1232                 :       25438 :     last_offset = pd_special;
    1233         [ +  + ]:     5788513 :     for (offnum = FirstOffsetNumber; offnum <= nline; offnum = OffsetNumberNext(offnum))
    1234                 :             :     {
    1235                 :     5763075 :         lp = PageGetItemId(page, offnum);
    1236                 :             :         Assert(ItemIdHasStorage(lp));
    1237                 :     5763075 :         size = ItemIdGetLength(lp);
    1238                 :     5763075 :         offset = ItemIdGetOffset(lp);
    1239         [ +  - ]:     5763075 :         if (offset < pd_upper ||
    1240         [ +  - ]:     5763075 :             (offset + size) > pd_special ||
    1241         [ -  + ]:     5763075 :             offset != MAXALIGN(offset))
    1242         [ #  # ]:           0 :             ereport(ERROR,
    1243                 :             :                     (errcode(ERRCODE_DATA_CORRUPTED),
    1244                 :             :                      errmsg("corrupted line pointer: offset = %u, size = %zu",
    1245                 :             :                             offset, size)));
    1246                 :             : 
    1247   [ +  +  +  + ]:     5763075 :         if (nextitm < nitems && offnum == itemnos[nextitm])
    1248                 :             :         {
    1249                 :             :             /* skip item to be deleted */
    1250                 :     2838555 :             nextitm++;
    1251                 :             :         }
    1252                 :             :         else
    1253                 :             :         {
    1254                 :     2924520 :             itemidptr->offsetindex = nused; /* where it will go */
    1255                 :     2924520 :             itemidptr->itemoff = offset;
    1256                 :             : 
    1257         [ +  + ]:     2924520 :             if (last_offset > itemidptr->itemoff)
    1258                 :     1530752 :                 last_offset = itemidptr->itemoff;
    1259                 :             :             else
    1260                 :     1393768 :                 presorted = false;
    1261                 :             : 
    1262                 :     2924520 :             itemidptr->alignedlen = MAXALIGN(size);
    1263                 :     2924520 :             totallen += itemidptr->alignedlen;
    1264                 :     2924520 :             newitemids[nused] = *lp;
    1265                 :     2924520 :             itemidptr++;
    1266                 :     2924520 :             nused++;
    1267                 :             :         }
    1268                 :             :     }
    1269                 :             : 
    1270                 :             :     /* this will catch invalid or out-of-order itemnos[] */
    1271         [ -  + ]:       25438 :     if (nextitm != nitems)
    1272         [ #  # ]:           0 :         elog(ERROR, "incorrect index offsets supplied");
    1273                 :             : 
    1274         [ -  + ]:       25438 :     if (totallen > (Size) (pd_special - pd_lower))
    1275         [ #  # ]:           0 :         ereport(ERROR,
    1276                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1277                 :             :                  errmsg("corrupted item lengths: total %zu, available space %u",
    1278                 :             :                         totallen, pd_special - pd_lower)));
    1279                 :             : 
    1280                 :             :     /*
    1281                 :             :      * Looks good. Overwrite the line pointers with the copy, from which we've
    1282                 :             :      * removed all the unused items.
    1283                 :             :      */
    1284                 :       25438 :     memcpy(phdr->pd_linp, newitemids, nused * sizeof(ItemIdData));
    1285                 :       25438 :     phdr->pd_lower = SizeOfPageHeaderData + nused * sizeof(ItemIdData);
    1286                 :             : 
    1287                 :             :     /* and compactify the tuple data */
    1288         [ +  + ]:       25438 :     if (nused > 0)
    1289                 :       24860 :         compactify_tuples(itemidbase, nused, page, presorted);
    1290                 :             :     else
    1291                 :         578 :         phdr->pd_upper = pd_special;
    1292                 :             : }
    1293                 :             : 
    1294                 :             : 
    1295                 :             : /*
    1296                 :             :  * PageIndexTupleDeleteNoCompact
    1297                 :             :  *
    1298                 :             :  * Remove the specified tuple from an index page, but set its line pointer
    1299                 :             :  * to "unused" instead of compacting it out, except that it can be removed
    1300                 :             :  * if it's the last line pointer on the page.
    1301                 :             :  *
    1302                 :             :  * This is used for index AMs that require that existing TIDs of live tuples
    1303                 :             :  * remain unchanged, and are willing to allow unused line pointers instead.
    1304                 :             :  */
    1305                 :             : void
    1306                 :         350 : PageIndexTupleDeleteNoCompact(Page page, OffsetNumber offnum)
    1307                 :             : {
    1308                 :         350 :     PageHeader  phdr = (PageHeader) page;
    1309                 :             :     char       *addr;
    1310                 :             :     ItemId      tup;
    1311                 :             :     Size        size;
    1312                 :             :     unsigned    offset;
    1313                 :             :     int         nline;
    1314                 :             : 
    1315                 :             :     /*
    1316                 :             :      * As with PageRepairFragmentation, paranoia seems justified.
    1317                 :             :      */
    1318         [ +  - ]:         350 :     if (phdr->pd_lower < SizeOfPageHeaderData ||
    1319         [ +  - ]:         350 :         phdr->pd_lower > phdr->pd_upper ||
    1320         [ +  - ]:         350 :         phdr->pd_upper > phdr->pd_special ||
    1321         [ +  - ]:         350 :         phdr->pd_special > BLCKSZ ||
    1322         [ -  + ]:         350 :         phdr->pd_special != MAXALIGN(phdr->pd_special))
    1323         [ #  # ]:           0 :         ereport(ERROR,
    1324                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1325                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
    1326                 :             :                         phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
    1327                 :             : 
    1328                 :         350 :     nline = PageGetMaxOffsetNumber(page);
    1329   [ +  -  -  + ]:         350 :     if ((int) offnum <= 0 || (int) offnum > nline)
    1330         [ #  # ]:           0 :         elog(ERROR, "invalid index offnum: %u", offnum);
    1331                 :             : 
    1332                 :         350 :     tup = PageGetItemId(page, offnum);
    1333                 :             :     Assert(ItemIdHasStorage(tup));
    1334                 :         350 :     size = ItemIdGetLength(tup);
    1335                 :         350 :     offset = ItemIdGetOffset(tup);
    1336                 :             : 
    1337   [ +  -  +  - ]:         350 :     if (offset < phdr->pd_upper || (offset + size) > phdr->pd_special ||
    1338         [ -  + ]:         350 :         offset != MAXALIGN(offset))
    1339         [ #  # ]:           0 :         ereport(ERROR,
    1340                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1341                 :             :                  errmsg("corrupted line pointer: offset = %u, size = %zu",
    1342                 :             :                         offset, size)));
    1343                 :             : 
    1344                 :             :     /* Amount of space to actually be deleted */
    1345                 :         350 :     size = MAXALIGN(size);
    1346                 :             : 
    1347                 :             :     /*
    1348                 :             :      * Either set the line pointer to "unused", or zap it if it's the last
    1349                 :             :      * one.  (Note: it's possible that the next-to-last one(s) are already
    1350                 :             :      * unused, but we do not trouble to try to compact them out if so.)
    1351                 :             :      */
    1352         [ +  + ]:         350 :     if ((int) offnum < nline)
    1353                 :         309 :         ItemIdSetUnused(tup);
    1354                 :             :     else
    1355                 :             :     {
    1356                 :          41 :         phdr->pd_lower -= sizeof(ItemIdData);
    1357                 :          41 :         nline--;                /* there's one less than when we started */
    1358                 :             :     }
    1359                 :             : 
    1360                 :             :     /*
    1361                 :             :      * Now move everything between the old upper bound (beginning of tuple
    1362                 :             :      * space) and the beginning of the deleted tuple forward, so that space in
    1363                 :             :      * the middle of the page is left free.  If we've just deleted the tuple
    1364                 :             :      * at the beginning of tuple space, then there's no need to do the copy.
    1365                 :             :      */
    1366                 :             : 
    1367                 :             :     /* beginning of tuple space */
    1368                 :         350 :     addr = (char *) page + phdr->pd_upper;
    1369                 :             : 
    1370         [ +  + ]:         350 :     if (offset > phdr->pd_upper)
    1371                 :         309 :         memmove(addr + size, addr, offset - phdr->pd_upper);
    1372                 :             : 
    1373                 :             :     /* adjust free space boundary pointer */
    1374                 :         350 :     phdr->pd_upper += size;
    1375                 :             : 
    1376                 :             :     /*
    1377                 :             :      * Finally, we need to adjust the linp entries that remain.
    1378                 :             :      *
    1379                 :             :      * Anything that used to be before the deleted tuple's data was moved
    1380                 :             :      * forward by the size of the deleted tuple.
    1381                 :             :      */
    1382         [ +  + ]:         350 :     if (!PageIsEmpty(page))
    1383                 :             :     {
    1384                 :             :         int         i;
    1385                 :             : 
    1386         [ +  + ]:       86613 :         for (i = 1; i <= nline; i++)
    1387                 :             :         {
    1388                 :       86269 :             ItemId      ii = PageGetItemId(page, i);
    1389                 :             : 
    1390   [ +  +  +  + ]:       86269 :             if (ItemIdHasStorage(ii) && ItemIdGetOffset(ii) <= offset)
    1391                 :       42311 :                 ii->lp_off += size;
    1392                 :             :         }
    1393                 :             :     }
    1394                 :         350 : }
    1395                 :             : 
    1396                 :             : 
    1397                 :             : /*
    1398                 :             :  * PageIndexTupleOverwrite
    1399                 :             :  *
    1400                 :             :  * Replace a specified tuple on an index page.
    1401                 :             :  *
    1402                 :             :  * The new tuple is placed exactly where the old one had been, shifting
    1403                 :             :  * other tuples' data up or down as needed to keep the page compacted.
    1404                 :             :  * This is better than deleting and reinserting the tuple, because it
    1405                 :             :  * avoids any data shifting when the tuple size doesn't change; and
    1406                 :             :  * even when it does, we avoid moving the line pointers around.
    1407                 :             :  * This could be used by an index AM that doesn't want to unset the
    1408                 :             :  * LP_DEAD bit when it happens to be set.  It could conceivably also be
    1409                 :             :  * used by an index AM that cares about the physical order of tuples as
    1410                 :             :  * well as their logical/ItemId order.
    1411                 :             :  *
    1412                 :             :  * If there's insufficient space for the new tuple, return false.  Other
    1413                 :             :  * errors represent data-corruption problems, so we just elog.
    1414                 :             :  */
    1415                 :             : bool
    1416                 :      606121 : PageIndexTupleOverwrite(Page page, OffsetNumber offnum,
    1417                 :             :                         const void *newtup, Size newsize)
    1418                 :             : {
    1419                 :      606121 :     PageHeader  phdr = (PageHeader) page;
    1420                 :             :     ItemId      tupid;
    1421                 :             :     int         oldsize;
    1422                 :             :     unsigned    offset;
    1423                 :             :     Size        alignednewsize;
    1424                 :             :     int         size_diff;
    1425                 :             :     int         itemcount;
    1426                 :             : 
    1427                 :             :     /*
    1428                 :             :      * As with PageRepairFragmentation, paranoia seems justified.
    1429                 :             :      */
    1430         [ +  - ]:      606121 :     if (phdr->pd_lower < SizeOfPageHeaderData ||
    1431         [ +  - ]:      606121 :         phdr->pd_lower > phdr->pd_upper ||
    1432         [ +  - ]:      606121 :         phdr->pd_upper > phdr->pd_special ||
    1433         [ +  - ]:      606121 :         phdr->pd_special > BLCKSZ ||
    1434         [ -  + ]:      606121 :         phdr->pd_special != MAXALIGN(phdr->pd_special))
    1435         [ #  # ]:           0 :         ereport(ERROR,
    1436                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1437                 :             :                  errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
    1438                 :             :                         phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
    1439                 :             : 
    1440                 :      606121 :     itemcount = PageGetMaxOffsetNumber(page);
    1441   [ +  -  -  + ]:      606121 :     if ((int) offnum <= 0 || (int) offnum > itemcount)
    1442         [ #  # ]:           0 :         elog(ERROR, "invalid index offnum: %u", offnum);
    1443                 :             : 
    1444                 :      606121 :     tupid = PageGetItemId(page, offnum);
    1445                 :             :     Assert(ItemIdHasStorage(tupid));
    1446                 :      606121 :     oldsize = ItemIdGetLength(tupid);
    1447                 :      606121 :     offset = ItemIdGetOffset(tupid);
    1448                 :             : 
    1449   [ +  -  +  - ]:      606121 :     if (offset < phdr->pd_upper || (offset + oldsize) > phdr->pd_special ||
    1450         [ -  + ]:      606121 :         offset != MAXALIGN(offset))
    1451         [ #  # ]:           0 :         ereport(ERROR,
    1452                 :             :                 (errcode(ERRCODE_DATA_CORRUPTED),
    1453                 :             :                  errmsg("corrupted line pointer: offset = %u, size = %d",
    1454                 :             :                         offset, oldsize)));
    1455                 :             : 
    1456                 :             :     /*
    1457                 :             :      * Determine actual change in space requirement, check for page overflow.
    1458                 :             :      */
    1459                 :      606121 :     oldsize = MAXALIGN(oldsize);
    1460                 :      606121 :     alignednewsize = MAXALIGN(newsize);
    1461         [ -  + ]:      606121 :     if (alignednewsize > oldsize + (phdr->pd_upper - phdr->pd_lower))
    1462                 :           0 :         return false;
    1463                 :             : 
    1464                 :             :     /*
    1465                 :             :      * Relocate existing data and update line pointers, unless the new tuple
    1466                 :             :      * is the same size as the old (after alignment), in which case there's
    1467                 :             :      * nothing to do.  Notice that what we have to relocate is data before the
    1468                 :             :      * target tuple, not data after, so it's convenient to express size_diff
    1469                 :             :      * as the amount by which the tuple's size is decreasing, making it the
    1470                 :             :      * delta to add to pd_upper and affected line pointers.
    1471                 :             :      */
    1472                 :      606121 :     size_diff = oldsize - (int) alignednewsize;
    1473         [ +  + ]:      606121 :     if (size_diff != 0)
    1474                 :             :     {
    1475                 :      111581 :         char       *addr = (char *) page + phdr->pd_upper;
    1476                 :             :         int         i;
    1477                 :             : 
    1478                 :             :         /* relocate all tuple data before the target tuple */
    1479                 :      111581 :         memmove(addr + size_diff, addr, offset - phdr->pd_upper);
    1480                 :             : 
    1481                 :             :         /* adjust free space boundary pointer */
    1482                 :      111581 :         phdr->pd_upper += size_diff;
    1483                 :             : 
    1484                 :             :         /* adjust affected line pointers too */
    1485         [ +  + ]:    21008154 :         for (i = FirstOffsetNumber; i <= itemcount; i++)
    1486                 :             :         {
    1487                 :    20896573 :             ItemId      ii = PageGetItemId(page, i);
    1488                 :             : 
    1489                 :             :             /* Allow items without storage; currently only BRIN needs that */
    1490   [ +  +  +  + ]:    20896573 :             if (ItemIdHasStorage(ii) && ItemIdGetOffset(ii) <= offset)
    1491                 :    10692726 :                 ii->lp_off += size_diff;
    1492                 :             :         }
    1493                 :             :     }
    1494                 :             : 
    1495                 :             :     /* Update the item's tuple length without changing its lp_flags field */
    1496                 :      606121 :     tupid->lp_off = offset + size_diff;
    1497                 :      606121 :     tupid->lp_len = newsize;
    1498                 :             : 
    1499                 :             :     /* Copy new tuple data onto page */
    1500                 :      606121 :     memcpy(PageGetItem(page, tupid), newtup, newsize);
    1501                 :             : 
    1502                 :      606121 :     return true;
    1503                 :             : }
    1504                 :             : 
    1505                 :             : 
    1506                 :             : /*
    1507                 :             :  * Set checksum on a page.
    1508                 :             :  *
    1509                 :             :  * If the page is in shared buffers, it needs to be locked in at least
    1510                 :             :  * share-exclusive mode.
    1511                 :             :  *
    1512                 :             :  * If checksums are disabled, or if the page is not initialized, just
    1513                 :             :  * return. Otherwise compute and set the checksum.
    1514                 :             :  *
    1515                 :             :  * In the past this needed to be done on a copy of the page, due to the
    1516                 :             :  * possibility of e.g., hint bits being set concurrently. However, this is not
    1517                 :             :  * necessary anymore as hint bits won't be set while IO is going on.
    1518                 :             :  */
    1519                 :             : void
    1520                 :      790239 : PageSetChecksum(Page page, BlockNumber blkno)
    1521                 :             : {
    1522                 :      790239 :     HOLD_INTERRUPTS();
    1523                 :             :     /* If we don't need a checksum, just return */
    1524   [ +  +  +  + ]:      790239 :     if (PageIsNew(page) || !DataChecksumsNeedWrite())
    1525                 :             :     {
    1526                 :       48489 :         RESUME_INTERRUPTS();
    1527                 :       48489 :         return;
    1528                 :             :     }
    1529                 :             : 
    1530                 :      741750 :     ((PageHeader) page)->pd_checksum = pg_checksum_page(page, blkno);
    1531                 :      741750 :     RESUME_INTERRUPTS();
    1532                 :             : }
        

Generated by: LCOV version 2.0-1