LCOV - differential code coverage report
Current view: top level - contrib/pg_surgery - heap_surgery.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DCB
Current: 77aeca80249c9e640c811e80633a2e334a9320de vs 38afc3dcb25c45b744d4025029ce0a6c90b7059f Lines: 92.5 % 134 124 10 1 123 1
Current Date: 2026-07-25 19:08:27 +0900 Functions: 100.0 % 9 9 1 8
Baseline: lcov-20260725-baseline Branches: 69.1 % 110 76 1 33 1 75
Baseline Date: 2026-07-25 19:09:19 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 87.5 % 16 14 2 1 13
(30,360] days: 100.0 % 5 5 5
(360..) days: 92.9 % 113 105 8 105
Function coverage date bins:
(360..) days: 100.0 % 9 9 1 8
Branch coverage date bins:
(7,30] days: 70.0 % 10 7 1 2 1 6
(30,360] days: 75.0 % 4 3 1 3
(360..) days: 68.8 % 96 66 30 66

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * heap_surgery.c
                                  4                 :                :  *    Functions to perform surgery on the damaged heap table.
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2020-2026, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  * IDENTIFICATION
                                  9                 :                :  *    contrib/pg_surgery/heap_surgery.c
                                 10                 :                :  *
                                 11                 :                :  *-------------------------------------------------------------------------
                                 12                 :                :  */
                                 13                 :                : #include "postgres.h"
                                 14                 :                : 
                                 15                 :                : #include "access/htup_details.h"
                                 16                 :                : #include "access/relation.h"
                                 17                 :                : #include "access/visibilitymap.h"
                                 18                 :                : #include "access/xloginsert.h"
                                 19                 :                : #include "catalog/pg_am_d.h"
                                 20                 :                : #include "catalog/pg_control.h"
                                 21                 :                : #include "miscadmin.h"
                                 22                 :                : #include "storage/bufmgr.h"
                                 23                 :                : #include "utils/acl.h"
                                 24                 :                : #include "utils/array.h"
                                 25                 :                : #include "utils/rel.h"
                                 26                 :                : 
  486 tgl@sss.pgh.pa.us          27                 :CBC           1 : PG_MODULE_MAGIC_EXT(
                                 28                 :                :                     .name = "pg_surgery",
                                 29                 :                :                     .version = PG_VERSION
                                 30                 :                : );
                                 31                 :                : 
                                 32                 :                : /* Options to forcefully change the state of a heap tuple. */
                                 33                 :                : typedef enum HeapTupleForceOption
                                 34                 :                : {
                                 35                 :                :     HEAP_FORCE_KILL,
                                 36                 :                :     HEAP_FORCE_FREEZE,
                                 37                 :                : } HeapTupleForceOption;
                                 38                 :                : 
 2144 rhaas@postgresql.org       39                 :              2 : PG_FUNCTION_INFO_V1(heap_force_kill);
                                 40                 :              2 : PG_FUNCTION_INFO_V1(heap_force_freeze);
                                 41                 :                : 
                                 42                 :                : static int32 tidcmp(const void *a, const void *b);
                                 43                 :                : static Datum heap_force_common(FunctionCallInfo fcinfo,
                                 44                 :                :                                HeapTupleForceOption heap_force_opt);
                                 45                 :                : static void sanity_check_tid_array(ArrayType *ta, int *ntids);
                                 46                 :                : static BlockNumber find_tids_one_page(ItemPointer tids, int ntids,
                                 47                 :                :                                       OffsetNumber *next_start_ptr);
                                 48                 :                : 
                                 49                 :                : /*-------------------------------------------------------------------------
                                 50                 :                :  * heap_force_kill()
                                 51                 :                :  *
                                 52                 :                :  * Force kill the tuple(s) pointed to by the item pointer(s) stored in the
                                 53                 :                :  * given TID array.
                                 54                 :                :  *
                                 55                 :                :  * Usage: SELECT heap_force_kill(regclass, tid[]);
                                 56                 :                :  *-------------------------------------------------------------------------
                                 57                 :                :  */
                                 58                 :                : Datum
                                 59                 :              9 : heap_force_kill(PG_FUNCTION_ARGS)
                                 60                 :                : {
                                 61                 :              9 :     PG_RETURN_DATUM(heap_force_common(fcinfo, HEAP_FORCE_KILL));
                                 62                 :                : }
                                 63                 :                : 
                                 64                 :                : /*-------------------------------------------------------------------------
                                 65                 :                :  * heap_force_freeze()
                                 66                 :                :  *
                                 67                 :                :  * Force freeze the tuple(s) pointed to by the item pointer(s) stored in the
                                 68                 :                :  * given TID array.
                                 69                 :                :  *
                                 70                 :                :  * Usage: SELECT heap_force_freeze(regclass, tid[]);
                                 71                 :                :  *-------------------------------------------------------------------------
                                 72                 :                :  */
                                 73                 :                : Datum
                                 74                 :              7 : heap_force_freeze(PG_FUNCTION_ARGS)
                                 75                 :                : {
                                 76                 :              7 :     PG_RETURN_DATUM(heap_force_common(fcinfo, HEAP_FORCE_FREEZE));
                                 77                 :                : }
                                 78                 :                : 
                                 79                 :                : /*-------------------------------------------------------------------------
                                 80                 :                :  * heap_force_common()
                                 81                 :                :  *
                                 82                 :                :  * Common code for heap_force_kill and heap_force_freeze
                                 83                 :                :  *-------------------------------------------------------------------------
                                 84                 :                :  */
                                 85                 :                : static Datum
                                 86                 :             16 : heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
                                 87                 :                : {
                                 88                 :             16 :     Oid         relid = PG_GETARG_OID(0);
                                 89                 :             16 :     ArrayType  *ta = PG_GETARG_ARRAYTYPE_P_COPY(1);
                                 90                 :                :     ItemPointer tids;
                                 91                 :                :     int         ntids,
                                 92                 :                :                 nblocks;
                                 93                 :                :     Relation    rel;
                                 94                 :                :     OffsetNumber curr_start_ptr,
                                 95                 :                :                 next_start_ptr;
                                 96                 :                :     bool        include_this_tid[MaxHeapTuplesPerPage];
                                 97                 :                : 
                                 98         [ -  + ]:             16 :     if (RecoveryInProgress())
 2144 rhaas@postgresql.org       99         [ #  # ]:UBC           0 :         ereport(ERROR,
                                100                 :                :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                101                 :                :                  errmsg("recovery is in progress"),
                                102                 :                :                  errhint("Heap surgery functions cannot be executed during recovery.")));
                                103                 :                : 
                                104                 :                :     /* Check inputs. */
 2144 rhaas@postgresql.org      105                 :CBC          16 :     sanity_check_tid_array(ta, &ntids);
                                106                 :                : 
                                107                 :             14 :     rel = relation_open(relid, RowExclusiveLock);
                                108                 :                : 
                                109                 :                :     /*
                                110                 :                :      * Check target relation.
                                111                 :                :      */
 1695 peter@eisentraut.org      112   [ +  +  +  -  :             14 :     if (!RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind))
                                              +  + ]
 1843                           113         [ +  - ]:              2 :         ereport(ERROR,
                                114                 :                :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                115                 :                :                  errmsg("cannot operate on relation \"%s\"",
                                116                 :                :                         RelationGetRelationName(rel)),
                                117                 :                :                  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
                                118                 :                : 
                                119         [ -  + ]:             12 :     if (rel->rd_rel->relam != HEAP_TABLE_AM_OID)
 1843 peter@eisentraut.org      120         [ #  # ]:UBC           0 :         ereport(ERROR,
                                121                 :                :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                122                 :                :                  errmsg("only heap AM is supported")));
                                123                 :                : 
                                124                 :                :     /* Must be owner of the table or superuser. */
 1350 peter@eisentraut.org      125         [ -  + ]:CBC          12 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
 1843 peter@eisentraut.org      126                 :UBC           0 :         aclcheck_error(ACLCHECK_NOT_OWNER,
                                127                 :              0 :                        get_relkind_objtype(rel->rd_rel->relkind),
                                128                 :              0 :                        RelationGetRelationName(rel));
                                129                 :                : 
 2144 rhaas@postgresql.org      130         [ -  + ]:CBC          12 :     tids = ((ItemPointer) ARR_DATA_PTR(ta));
                                131                 :                : 
                                132                 :                :     /*
                                133                 :                :      * If there is more than one TID in the array, sort them so that we can
                                134                 :                :      * easily fetch all the TIDs belonging to one particular page from the
                                135                 :                :      * array.
                                136                 :                :      */
                                137         [ +  + ]:             12 :     if (ntids > 1)
 1264 peter@eisentraut.org      138                 :              2 :         qsort(tids, ntids, sizeof(ItemPointerData), tidcmp);
                                139                 :                : 
 2144 rhaas@postgresql.org      140                 :             12 :     curr_start_ptr = next_start_ptr = 0;
                                141                 :             12 :     nblocks = RelationGetNumberOfBlocks(rel);
                                142                 :                : 
                                143                 :                :     /*
                                144                 :                :      * Loop, performing the necessary actions for each block.
                                145                 :                :      */
                                146         [ +  + ]:             24 :     while (next_start_ptr != ntids)
                                147                 :                :     {
                                148                 :                :         Buffer      buf;
                                149                 :             12 :         Buffer      vmbuf = InvalidBuffer;
   10 melanieplageman@gmai      150                 :             12 :         bool        unlock_vmbuf = false;
                                151                 :                :         Page        page;
                                152                 :                :         BlockNumber blkno;
                                153                 :                :         OffsetNumber curoff;
                                154                 :                :         OffsetNumber maxoffset;
                                155                 :                :         int         i;
 2144 rhaas@postgresql.org      156                 :             12 :         bool        did_modify_page = false;
                                157                 :             12 :         bool        did_modify_vm = false;
                                158                 :                : 
                                159         [ -  + ]:             12 :         CHECK_FOR_INTERRUPTS();
                                160                 :                : 
                                161                 :                :         /*
                                162                 :                :          * Find all the TIDs belonging to one particular page starting from
                                163                 :                :          * next_start_ptr and process them one by one.
                                164                 :                :          */
                                165                 :             12 :         blkno = find_tids_one_page(tids, ntids, &next_start_ptr);
                                166                 :                : 
                                167                 :                :         /* Check whether the block number is valid. */
                                168         [ +  + ]:             12 :         if (blkno >= nblocks)
                                169                 :                :         {
                                170                 :                :             /* Update the current_start_ptr before moving to the next page. */
                                171                 :              1 :             curr_start_ptr = next_start_ptr;
                                172                 :                : 
                                173         [ +  - ]:              1 :             ereport(NOTICE,
                                174                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                175                 :                :                      errmsg("skipping block %u for relation \"%s\" because the block number is out of range",
                                176                 :                :                             blkno, RelationGetRelationName(rel))));
                                177                 :              1 :             continue;
                                178                 :                :         }
                                179                 :                : 
                                180                 :             11 :         buf = ReadBuffer(rel, blkno);
                                181                 :             11 :         LockBufferForCleanup(buf);
                                182                 :                : 
                                183                 :             11 :         page = BufferGetPage(buf);
                                184                 :                : 
                                185                 :             11 :         maxoffset = PageGetMaxOffsetNumber(page);
                                186                 :                : 
                                187                 :                :         /*
                                188                 :                :          * Figure out which TIDs we are going to process and which ones we are
                                189                 :                :          * going to skip.
                                190                 :                :          */
                                191                 :             11 :         memset(include_this_tid, 0, sizeof(include_this_tid));
                                192         [ +  + ]:             24 :         for (i = curr_start_ptr; i < next_start_ptr; i++)
                                193                 :                :         {
                                194                 :             13 :             OffsetNumber offno = ItemPointerGetOffsetNumberNoCheck(&tids[i]);
                                195                 :                :             ItemId      itemid;
                                196                 :                : 
                                197                 :                :             /* Check whether the offset number is valid. */
                                198   [ +  +  +  + ]:             13 :             if (offno == InvalidOffsetNumber || offno > maxoffset)
                                199                 :                :             {
                                200         [ +  - ]:              2 :                 ereport(NOTICE,
                                201                 :                :                         errmsg("skipping tid (%u, %u) for relation \"%s\" because the item number is out of range",
                                202                 :                :                                blkno, offno, RelationGetRelationName(rel)));
                                203                 :              2 :                 continue;
                                204                 :                :             }
                                205                 :                : 
                                206                 :             11 :             itemid = PageGetItemId(page, offno);
                                207                 :                : 
                                208                 :                :             /* Only accept an item ID that is used. */
                                209         [ +  + ]:             11 :             if (ItemIdIsRedirected(itemid))
                                210                 :                :             {
                                211         [ +  - ]:              1 :                 ereport(NOTICE,
                                212                 :                :                         errmsg("skipping tid (%u, %u) for relation \"%s\" because it redirects to item %u",
                                213                 :                :                                blkno, offno, RelationGetRelationName(rel),
                                214                 :                :                                ItemIdGetRedirect(itemid)));
                                215                 :              1 :                 continue;
                                216                 :                :             }
                                217         [ +  + ]:             10 :             else if (ItemIdIsDead(itemid))
                                218                 :                :             {
                                219         [ +  - ]:              2 :                 ereport(NOTICE,
                                220                 :                :                         (errmsg("skipping tid (%u, %u) for relation \"%s\" because it is marked dead",
                                221                 :                :                                 blkno, offno, RelationGetRelationName(rel))));
                                222                 :              2 :                 continue;
                                223                 :                :             }
                                224         [ +  + ]:              8 :             else if (!ItemIdIsUsed(itemid))
                                225                 :                :             {
                                226         [ +  - ]:              1 :                 ereport(NOTICE,
                                227                 :                :                         (errmsg("skipping tid (%u, %u) for relation \"%s\" because it is marked unused",
                                228                 :                :                                 blkno, offno, RelationGetRelationName(rel))));
                                229                 :              1 :                 continue;
                                230                 :                :             }
                                231                 :                : 
                                232                 :                :             /* Mark it for processing. */
   49 michael@paquier.xyz       233         [ -  + ]:              7 :             Assert(offno <= MaxHeapTuplesPerPage);
                                234                 :              7 :             include_this_tid[offno - 1] = true;
                                235                 :                :         }
                                236                 :                : 
                                237                 :                :         /*
                                238                 :                :          * Before entering the critical section, pin and lock the visibility
                                239                 :                :          * map page if it appears to be necessary.
                                240                 :                :          */
 2144 rhaas@postgresql.org      241   [ +  +  +  + ]:             11 :         if (heap_force_opt == HEAP_FORCE_KILL && PageIsAllVisible(page))
                                242                 :                :         {
                                243                 :              3 :             visibilitymap_pin(rel, blkno, &vmbuf);
   10 melanieplageman@gmai      244                 :              3 :             LockBuffer(vmbuf, BUFFER_LOCK_EXCLUSIVE);
                                245                 :              3 :             unlock_vmbuf = true;
                                246                 :                :         }
                                247                 :                : 
                                248                 :                :         /* No ereport(ERROR) from here until all the changes are logged. */
 2144 rhaas@postgresql.org      249                 :             11 :         START_CRIT_SECTION();
                                250                 :                : 
                                251         [ +  + ]:             55 :         for (curoff = FirstOffsetNumber; curoff <= maxoffset;
                                252                 :             44 :              curoff = OffsetNumberNext(curoff))
                                253                 :                :         {
                                254                 :                :             ItemId      itemid;
                                255                 :                : 
   49 michael@paquier.xyz       256         [ +  + ]:             44 :             if (!include_this_tid[curoff - 1])
 2144 rhaas@postgresql.org      257                 :             37 :                 continue;
                                258                 :                : 
                                259                 :              7 :             itemid = PageGetItemId(page, curoff);
                                260         [ -  + ]:              7 :             Assert(ItemIdIsNormal(itemid));
                                261                 :                : 
                                262                 :              7 :             did_modify_page = true;
                                263                 :                : 
                                264         [ +  + ]:              7 :             if (heap_force_opt == HEAP_FORCE_KILL)
                                265                 :                :             {
                                266                 :              3 :                 ItemIdSetDead(itemid);
                                267                 :                : 
                                268                 :                :                 /*
                                269                 :                :                  * If the page is marked all-visible, we must clear
                                270                 :                :                  * PD_ALL_VISIBLE flag on the page header and an all-visible
                                271                 :                :                  * bit on the visibility map corresponding to the page.
                                272                 :                :                  */
                                273         [ +  + ]:              3 :                 if (PageIsAllVisible(page))
                                274                 :                :                 {
   10 melanieplageman@gmai      275         [ +  - ]:GNC           1 :                     if (visibilitymap_clear(rel->rd_locator, blkno, vmbuf,
                                276                 :                :                                             VISIBILITYMAP_VALID_BITS))
   10 melanieplageman@gmai      277                 :CBC           1 :                         did_modify_vm = true;
                                278                 :                : 
 2144 rhaas@postgresql.org      279                 :              1 :                     PageClearAllVisible(page);
                                280                 :                :                 }
                                281                 :                :             }
                                282                 :                :             else
                                283                 :                :             {
                                284                 :                :                 HeapTupleHeader htup;
                                285                 :                : 
                                286         [ -  + ]:              4 :                 Assert(heap_force_opt == HEAP_FORCE_FREEZE);
                                287                 :                : 
                                288                 :              4 :                 htup = (HeapTupleHeader) PageGetItem(page, itemid);
                                289                 :                : 
                                290                 :                :                 /*
                                291                 :                :                  * Reset all visibility-related fields of the tuple. This
                                292                 :                :                  * logic should mimic heap_execute_freeze_tuple(), but we
                                293                 :                :                  * choose to reset xmin and ctid just to be sure that no
                                294                 :                :                  * potentially-garbled data is left behind.
                                295                 :                :                  */
                                296                 :              4 :                 ItemPointerSet(&htup->t_ctid, blkno, curoff);
                                297                 :              4 :                 HeapTupleHeaderSetXmin(htup, FrozenTransactionId);
                                298                 :              4 :                 HeapTupleHeaderSetXmax(htup, InvalidTransactionId);
                                299         [ -  + ]:              4 :                 if (htup->t_infomask & HEAP_MOVED)
                                300                 :                :                 {
 2144 rhaas@postgresql.org      301         [ #  # ]:UBC           0 :                     if (htup->t_infomask & HEAP_MOVED_OFF)
                                302                 :              0 :                         HeapTupleHeaderSetXvac(htup, InvalidTransactionId);
                                303                 :                :                     else
                                304                 :              0 :                         HeapTupleHeaderSetXvac(htup, FrozenTransactionId);
                                305                 :                :                 }
                                306                 :                : 
                                307                 :                :                 /*
                                308                 :                :                  * Clear all the visibility-related bits of this tuple and
                                309                 :                :                  * mark it as frozen. Also, get rid of HOT_UPDATED and
                                310                 :                :                  * KEYS_UPDATES bits.
                                311                 :                :                  */
 2144 rhaas@postgresql.org      312                 :CBC           4 :                 htup->t_infomask &= ~HEAP_XACT_MASK;
                                313                 :              4 :                 htup->t_infomask |= (HEAP_XMIN_FROZEN | HEAP_XMAX_INVALID);
                                314                 :              4 :                 htup->t_infomask2 &= ~HEAP_HOT_UPDATED;
                                315                 :              4 :                 htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
                                316                 :                :             }
                                317                 :                :         }
                                318                 :                : 
                                319                 :                :         /*
                                320                 :                :          * If the page was modified, only then, we mark the buffer dirty or do
                                321                 :                :          * the WAL logging.
                                322                 :                :          */
                                323         [ +  + ]:             11 :         if (did_modify_page)
                                324                 :                :         {
                                325                 :                :             /* Mark buffer dirty before we write WAL. */
                                326                 :              6 :             MarkBufferDirty(buf);
                                327                 :                : 
                                328                 :                :             /* XLOG stuff */
                                329   [ +  +  -  +  :              6 :             if (RelationNeedsWAL(rel))
                                        -  -  -  - ]
                                330                 :                :             {
                                331                 :                :                 XLogRecPtr  recptr;
                                332                 :                : 
   10 melanieplageman@gmai      333                 :              2 :                 XLogBeginInsert();
                                334                 :              2 :                 XLogRegisterBuffer(0, buf, REGBUF_STANDARD | REGBUF_FORCE_IMAGE);
                                335                 :                :                 /* Include the VM page if it was modified */
                                336         [ -  + ]:              2 :                 if (did_modify_vm)
   10 melanieplageman@gmai      337                 :UBC           0 :                     XLogRegisterBuffer(1, vmbuf, REGBUF_FORCE_IMAGE);
   10 melanieplageman@gmai      338                 :CBC           2 :                 recptr = XLogInsert(RM_XLOG_ID, XLOG_FPI);
                                339         [ -  + ]:              2 :                 if (did_modify_vm)
   10 melanieplageman@gmai      340                 :UBC           0 :                     PageSetLSN(BufferGetPage(vmbuf), recptr);
   10 melanieplageman@gmai      341                 :CBC           2 :                 PageSetLSN(BufferGetPage(buf), recptr);
                                342                 :                :             }
                                343                 :                :         }
                                344                 :                : 
 2144 rhaas@postgresql.org      345         [ -  + ]:             11 :         END_CRIT_SECTION();
                                346                 :                : 
                                347                 :             11 :         UnlockReleaseBuffer(buf);
                                348                 :                : 
   10 melanieplageman@gmai      349         [ +  + ]:             11 :         if (unlock_vmbuf)
                                350                 :              3 :             LockBuffer(vmbuf, BUFFER_LOCK_UNLOCK);
                                351                 :                : 
                                352         [ +  + ]:             11 :         if (BufferIsValid(vmbuf))
 2144 rhaas@postgresql.org      353                 :              3 :             ReleaseBuffer(vmbuf);
                                354                 :                : 
                                355                 :                :         /* Update the current_start_ptr before moving to the next page. */
                                356                 :             11 :         curr_start_ptr = next_start_ptr;
                                357                 :                :     }
                                358                 :                : 
                                359                 :             12 :     relation_close(rel, RowExclusiveLock);
                                360                 :                : 
                                361                 :             12 :     pfree(ta);
                                362                 :                : 
                                363                 :             12 :     PG_RETURN_VOID();
                                364                 :                : }
                                365                 :                : 
                                366                 :                : /*-------------------------------------------------------------------------
                                367                 :                :  * tidcmp()
                                368                 :                :  *
                                369                 :                :  * Compare two item pointers, return -1, 0, or +1.
                                370                 :                :  *
                                371                 :                :  * See ItemPointerCompare for details.
                                372                 :                :  * ------------------------------------------------------------------------
                                373                 :                :  */
                                374                 :                : static int32
                                375                 :              3 : tidcmp(const void *a, const void *b)
                                376                 :                : {
  268 peter@eisentraut.org      377                 :              3 :     const ItemPointerData *iptr1 = a;
                                378                 :              3 :     const ItemPointerData *iptr2 = b;
                                379                 :                : 
 2144 rhaas@postgresql.org      380                 :              3 :     return ItemPointerCompare(iptr1, iptr2);
                                381                 :                : }
                                382                 :                : 
                                383                 :                : /*-------------------------------------------------------------------------
                                384                 :                :  * sanity_check_tid_array()
                                385                 :                :  *
                                386                 :                :  * Perform sanity checks on the given tid array, and set *ntids to the
                                387                 :                :  * number of items in the array.
                                388                 :                :  * ------------------------------------------------------------------------
                                389                 :                :  */
                                390                 :                : static void
                                391                 :             16 : sanity_check_tid_array(ArrayType *ta, int *ntids)
                                392                 :                : {
                                393   [ +  +  +  - ]:             16 :     if (ARR_HASNULL(ta) && array_contains_nulls(ta))
                                394         [ +  - ]:              1 :         ereport(ERROR,
                                395                 :                :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                396                 :                :                  errmsg("array must not contain nulls")));
                                397                 :                : 
                                398         [ +  + ]:             15 :     if (ARR_NDIM(ta) > 1)
                                399         [ +  - ]:              1 :         ereport(ERROR,
                                400                 :                :                 (errcode(ERRCODE_DATA_EXCEPTION),
                                401                 :                :                  errmsg("argument must be empty or one-dimensional array")));
                                402                 :                : 
                                403                 :             14 :     *ntids = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
                                404                 :             14 : }
                                405                 :                : 
                                406                 :                : /*-------------------------------------------------------------------------
                                407                 :                :  * find_tids_one_page()
                                408                 :                :  *
                                409                 :                :  * Find all the tids residing in the same page as tids[next_start_ptr], and
                                410                 :                :  * update next_start_ptr so that it points to the first tid in the next page.
                                411                 :                :  *
                                412                 :                :  * NOTE: The input tids[] array must be sorted.
                                413                 :                :  * ------------------------------------------------------------------------
                                414                 :                :  */
                                415                 :                : static BlockNumber
                                416                 :             12 : find_tids_one_page(ItemPointer tids, int ntids, OffsetNumber *next_start_ptr)
                                417                 :                : {
                                418                 :                :     int         i;
                                419                 :                :     BlockNumber prev_blkno,
                                420                 :                :                 blkno;
                                421                 :                : 
                                422                 :             12 :     prev_blkno = blkno = InvalidBlockNumber;
                                423                 :                : 
                                424         [ +  + ]:             26 :     for (i = *next_start_ptr; i < ntids; i++)
                                425                 :                :     {
                                426                 :             15 :         ItemPointerData tid = tids[i];
                                427                 :                : 
                                428                 :             15 :         blkno = ItemPointerGetBlockNumberNoCheck(&tid);
                                429                 :                : 
                                430         [ +  + ]:             15 :         if (i == *next_start_ptr)
                                431                 :             12 :             prev_blkno = blkno;
                                432                 :                : 
                                433         [ +  + ]:             15 :         if (prev_blkno != blkno)
                                434                 :              1 :             break;
                                435                 :                :     }
                                436                 :                : 
                                437                 :             12 :     *next_start_ptr = i;
                                438                 :             12 :     return prev_blkno;
                                439                 :                : }
        

Generated by: LCOV version 2.0-1