LCOV - differential code coverage report
Current view: top level - contrib/pg_surgery - heap_surgery.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 92.7 % 124 115 9 115
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 9 9 9
Baseline: 16@8cea358b128 Branches: 63.8 % 116 74 42 74
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 92.7 % 124 115 9 115
Function coverage date bins:
(240..) days: 100.0 % 9 9 9
Branch coverage date bins:
(240..) days: 63.8 % 116 74 42 74

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

Generated by: LCOV version 2.1-beta2-3-g6141622