LCOV - differential code coverage report
Current view: top level - src/backend/access/heap - visibilitymap.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: 77aeca80249c9e640c811e80633a2e334a9320de vs 38afc3dcb25c45b744d4025029ce0a6c90b7059f Lines: 94.5 % 128 121 7 1 120 1
Current Date: 2026-07-25 19:08:27 +0900 Functions: 100.0 % 10 10 1 9 1
Baseline: lcov-20260725-baseline Branches: 67.3 % 104 70 34 70
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: 85.7 % 7 6 1 1 5
(30,360] days: 94.7 % 19 18 1 18
(360..) days: 95.1 % 102 97 5 97
Function coverage date bins:
(7,30] days: 100.0 % 1 1 1
(30,360] days: 100.0 % 2 2 2
(360..) days: 100.0 % 7 7 7
Branch coverage date bins:
(7,30] days: 37.5 % 8 3 5 3
(30,360] days: 50.0 % 18 9 9 9
(360..) days: 74.4 % 78 58 20 58

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * visibilitymap.c
                                  4                 :                :  *    bitmap for tracking visibility of heap tuples
                                  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/access/heap/visibilitymap.c
                                 12                 :                :  *
                                 13                 :                :  * INTERFACE ROUTINES
                                 14                 :                :  *      visibilitymap_clear  - clear bits for one page in the visibility map
                                 15                 :                :  *      visibilitymap_pin    - pin a map page for setting a bit
                                 16                 :                :  *      visibilitymap_pin_ok - check whether correct map page is already pinned
                                 17                 :                :  *      visibilitymap_set    - set bit(s) in a previously pinned page
                                 18                 :                :  *      visibilitymap_get_status - get status of bits
                                 19                 :                :  *      visibilitymap_count  - count number of bits set in visibility map
                                 20                 :                :  *      visibilitymap_prepare_truncate -
                                 21                 :                :  *          prepare for truncation of the visibility map
                                 22                 :                :  *
                                 23                 :                :  * NOTES
                                 24                 :                :  *
                                 25                 :                :  * The visibility map is a bitmap with two bits (all-visible and all-frozen)
                                 26                 :                :  * per heap page. A set all-visible bit means that all tuples on the page are
                                 27                 :                :  * known visible to all transactions, and therefore the page doesn't need to
                                 28                 :                :  * be vacuumed. A set all-frozen bit means that all tuples on the page are
                                 29                 :                :  * completely frozen, and therefore the page doesn't need to be vacuumed even
                                 30                 :                :  * if whole table scanning vacuum is required (e.g. anti-wraparound vacuum).
                                 31                 :                :  * The all-frozen bit must be set only when the page is already all-visible.
                                 32                 :                :  *
                                 33                 :                :  * The map is conservative in the sense that we make sure that whenever a bit
                                 34                 :                :  * is set, we know the condition is true, but if a bit is not set, it might or
                                 35                 :                :  * might not be true.
                                 36                 :                :  *
                                 37                 :                :  * Changes to the visibility map bits are not separately WAL-logged. Callers
                                 38                 :                :  * must make sure that whenever a visibility map bit is cleared, the bit is
                                 39                 :                :  * cleared on WAL replay of the updating operation. And whenever a visibility
                                 40                 :                :  * map bit is set, the bit is set on WAL replay of the operation that rendered
                                 41                 :                :  * the page all-visible/all-frozen.
                                 42                 :                :  *
                                 43                 :                :  * The visibility map bits operate as a hint in one direction: if they are
                                 44                 :                :  * clear, it may still be the case that every tuple on the page is visible to
                                 45                 :                :  * all transactions (we just don't know that for certain). However, if they
                                 46                 :                :  * are set, we may skip vacuuming pages and advance relfrozenxid or skip
                                 47                 :                :  * reading heap pages for an index-only scan. If they are incorrectly set,
                                 48                 :                :  * this can lead to data corruption and wrong results.
                                 49                 :                :  *
                                 50                 :                :  * Additionally, it is critical that the heap-page level PD_ALL_VISIBLE bit be
                                 51                 :                :  * correctly set and cleared along with the VM bits.
                                 52                 :                :  *
                                 53                 :                :  * When clearing the VM, if a crash occurs after the heap page makes it to
                                 54                 :                :  * disk but before the VM page makes it to disk, replay must clear the VM or
                                 55                 :                :  * the next index-only scan can return wrong results or vacuum may incorrectly
                                 56                 :                :  * advance relfrozenxid.
                                 57                 :                :  *
                                 58                 :                :  * When setting the VM, if a crash occurs after the visibility map page makes
                                 59                 :                :  * it to disk and before the updated heap page makes it to disk, redo must set
                                 60                 :                :  * the bit on the heap page. Otherwise, the next insert, update, or delete on
                                 61                 :                :  * the heap page will fail to realize that the visibility map bit must be
                                 62                 :                :  * cleared, possibly causing index-only scans to return wrong answers.
                                 63                 :                :  *
                                 64                 :                :  * VACUUM will normally skip pages for which the visibility map bit is set;
                                 65                 :                :  * such pages can't contain any dead tuples and therefore don't need vacuuming.
                                 66                 :                :  *
                                 67                 :                :  * LOCKING
                                 68                 :                :  *
                                 69                 :                :  * In heapam.c, whenever a page is modified so that not all tuples on the
                                 70                 :                :  * page are visible to everyone anymore, the corresponding bit in the
                                 71                 :                :  * visibility map is cleared. In order to be crash-safe, we need to do this
                                 72                 :                :  * while still holding a lock on the heap page and in the same critical
                                 73                 :                :  * section that logs the page modification. However, we don't want to hold
                                 74                 :                :  * the buffer lock over any I/O that may be required to read in the visibility
                                 75                 :                :  * map page.  To avoid this, we examine the heap page before locking it;
                                 76                 :                :  * if the page-level PD_ALL_VISIBLE bit is set, we pin the visibility map
                                 77                 :                :  * bit.  Then, we lock the buffer.  But this creates a race condition: there
                                 78                 :                :  * is a possibility that in the time it takes to lock the buffer, the
                                 79                 :                :  * PD_ALL_VISIBLE bit gets set.  If that happens, we have to unlock the
                                 80                 :                :  * buffer, pin the visibility map page, and relock the buffer.  This shouldn't
                                 81                 :                :  * happen often, because only VACUUM currently sets visibility map bits,
                                 82                 :                :  * and the race will only occur if VACUUM processes a given page at almost
                                 83                 :                :  * exactly the same time that someone tries to further modify it.
                                 84                 :                :  *
                                 85                 :                :  * To set a bit, you need to hold a lock on the heap page. That prevents
                                 86                 :                :  * the race condition where VACUUM sees that all tuples on the page are
                                 87                 :                :  * visible to everyone, but another backend modifies the page before VACUUM
                                 88                 :                :  * sets the bit in the visibility map.
                                 89                 :                :  *
                                 90                 :                :  * When a bit is set, the LSN of the visibility map page is updated to make
                                 91                 :                :  * sure that the visibility map update doesn't get written to disk before the
                                 92                 :                :  * WAL record of the changes that made it possible to set the bit is flushed.
                                 93                 :                :  * But when a bit is cleared, we don't have to do that because it's always
                                 94                 :                :  * safe to clear a bit in the map from correctness point of view.
                                 95                 :                :  *
                                 96                 :                :  *-------------------------------------------------------------------------
                                 97                 :                :  */
                                 98                 :                : #include "postgres.h"
                                 99                 :                : 
                                100                 :                : #include "access/heapam_xlog.h"
                                101                 :                : #include "access/visibilitymap.h"
                                102                 :                : #include "access/xloginsert.h"
                                103                 :                : #include "access/xlogutils.h"
                                104                 :                : #include "miscadmin.h"
                                105                 :                : #include "port/pg_bitutils.h"
                                106                 :                : #include "storage/bufmgr.h"
                                107                 :                : #include "storage/smgr.h"
                                108                 :                : #include "utils/inval.h"
                                109                 :                : #include "utils/rel.h"
                                110                 :                : 
                                111                 :                : 
                                112                 :                : /*#define TRACE_VISIBILITYMAP */
                                113                 :                : 
                                114                 :                : /*
                                115                 :                :  * Size of the bitmap on each visibility map page, in bytes. There's no
                                116                 :                :  * extra headers, so the whole page minus the standard page header is
                                117                 :                :  * used for the bitmap.
                                118                 :                :  */
                                119                 :                : #define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData))
                                120                 :                : 
                                121                 :                : /* Number of heap blocks we can represent in one byte */
                                122                 :                : #define HEAPBLOCKS_PER_BYTE (BITS_PER_BYTE / BITS_PER_HEAPBLOCK)
                                123                 :                : 
                                124                 :                : /* Number of heap blocks we can represent in one visibility map page. */
                                125                 :                : #define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE)
                                126                 :                : 
                                127                 :                : /* Mapping from heap block number to the right bit in the visibility map */
                                128                 :                : #define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE)
                                129                 :                : #define HEAPBLK_TO_MAPBLOCK_LIMIT(x) \
                                130                 :                :     (((x) + HEAPBLOCKS_PER_PAGE - 1) / HEAPBLOCKS_PER_PAGE)
                                131                 :                : #define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE)
                                132                 :                : #define HEAPBLK_TO_OFFSET(x) (((x) % HEAPBLOCKS_PER_BYTE) * BITS_PER_HEAPBLOCK)
                                133                 :                : 
                                134                 :                : /* Masks for counting subsets of bits in the visibility map. */
                                135                 :                : #define VISIBLE_MASK8   (0x55)  /* The lower bit of each bit pair */
                                136                 :                : #define FROZEN_MASK8    (0xaa)  /* The upper bit of each bit pair */
                                137                 :                : 
                                138                 :                : /* prototypes for internal routines */
                                139                 :                : static Buffer vm_readbuf(Relation rel, BlockNumber blkno, bool extend);
                                140                 :                : static Buffer vm_extend(Relation rel, BlockNumber vm_nblocks);
                                141                 :                : 
                                142                 :                : /*
                                143                 :                :  *  visibilitymap_clear - clear specified bits for one page in visibility map
                                144                 :                :  *
                                145                 :                :  * You must pass a buffer containing the correct map page to this function,
                                146                 :                :  * which already needs to be pinned and locked exclusively.
                                147                 :                :  *
                                148                 :                :  * This function doesn't do any I/O. Returns true if any bits have been
                                149                 :                :  * cleared and false otherwise.
                                150                 :                :  */
                                151                 :                : bool
   10 melanieplageman@gmai      152                 :GNC       30803 : visibilitymap_clear(RelFileLocator rlocator, BlockNumber heapBlk,
                                153                 :                :                     Buffer vmbuf, uint8 flags)
                                154                 :                : {
 6443 heikki.linnakangas@i      155                 :CBC       30803 :     int         mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
 3704 rhaas@postgresql.org      156                 :          30803 :     int         mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
   10 melanieplageman@gmai      157                 :          30803 :     BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
 3659 andres@anarazel.de        158                 :          30803 :     uint8       mask = flags << mapOffset;
                                159                 :                :     Page        page;
                                160                 :                :     char       *map;
                                161                 :          30803 :     bool        cleared = false;
                                162                 :                : 
                                163                 :                :     /* Must never clear all_visible bit while leaving all_frozen bit set */
                                164         [ -  + ]:          30803 :     Assert(flags & VISIBILITYMAP_VALID_BITS);
 1286 pg@bowt.ie                165         [ -  + ]:          30803 :     Assert(flags != VISIBILITYMAP_ALL_VISIBLE);
                                166                 :                : 
                                167                 :                : #ifdef TRACE_VISIBILITYMAP
                                168                 :                :     elog(DEBUG1, "vm_clear %s %d",
                                169                 :                :          relpathbackend(rlocator, MyProcNumber, MAIN_FORKNUM).str,
                                170                 :                :          heapBlk);
                                171                 :                : #endif
                                172                 :                : 
    8 melanieplageman@gmai      173   [ +  -  -  + ]:          30803 :     if (!BufferIsValid(vmbuf) || BufferGetBlockNumber(vmbuf) != mapBlock)
    8 melanieplageman@gmai      174         [ #  # ]:UBC           0 :         elog(ERROR, "wrong buffer passed to visibilitymap_clear");
                                175                 :                : 
   10 melanieplageman@gmai      176         [ -  + ]:CBC       30803 :     Assert(BufferIsLockedByMeInMode(vmbuf, BUFFER_LOCK_EXCLUSIVE));
                                177                 :                : 
                                178                 :          30803 :     page = BufferGetPage(vmbuf);
                                179                 :          30803 :     map = PageGetContents(page);
                                180                 :                : 
 6443 heikki.linnakangas@i      181         [ +  + ]:          30803 :     if (map[mapByte] & mask)
                                182                 :                :     {
                                183                 :          26041 :         map[mapByte] &= ~mask;
                                184                 :                : 
 1405 pg@bowt.ie                185                 :          26041 :         MarkBufferDirty(vmbuf);
 3659 andres@anarazel.de        186                 :          26041 :         cleared = true;
                                187                 :                :     }
                                188                 :                : 
                                189                 :          30803 :     return cleared;
                                190                 :                : }
                                191                 :                : 
                                192                 :                : /*
                                193                 :                :  *  visibilitymap_pin - pin a map page for setting a bit
                                194                 :                :  *
                                195                 :                :  * Setting a bit in the visibility map is a two-phase operation. First, call
                                196                 :                :  * visibilitymap_pin, to pin the visibility map page containing the bit for
                                197                 :                :  * the heap page. Because that can require I/O to read the map page, you
                                198                 :                :  * shouldn't hold a lock on the heap page while doing that. Then, call
                                199                 :                :  * visibilitymap_set to actually set the bit.
                                200                 :                :  *
                                201                 :                :  * On entry, *vmbuf should be InvalidBuffer or a valid buffer returned by
                                202                 :                :  * an earlier call to visibilitymap_pin or visibilitymap_get_status on the same
                                203                 :                :  * relation. On return, *vmbuf is a valid buffer with the map page containing
                                204                 :                :  * the bit for heapBlk.
                                205                 :                :  *
                                206                 :                :  * If the page doesn't exist in the map file yet, it is extended.
                                207                 :                :  */
                                208                 :                : void
 1405 pg@bowt.ie                209                 :         703786 : visibilitymap_pin(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
                                210                 :                : {
 6443 heikki.linnakangas@i      211                 :         703786 :     BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
                                212                 :                : 
                                213                 :                :     /* Reuse the old pinned buffer if possible */
 1405 pg@bowt.ie                214         [ +  + ]:         703786 :     if (BufferIsValid(*vmbuf))
                                215                 :                :     {
                                216         [ +  - ]:         198513 :         if (BufferGetBlockNumber(*vmbuf) == mapBlock)
 6443 heikki.linnakangas@i      217                 :         198513 :             return;
                                218                 :                : 
 1405 pg@bowt.ie                219                 :UBC           0 :         ReleaseBuffer(*vmbuf);
                                220                 :                :     }
 1405 pg@bowt.ie                221                 :CBC      505273 :     *vmbuf = vm_readbuf(rel, mapBlock, true);
                                222                 :                : }
                                223                 :                : 
                                224                 :                : /*
                                225                 :                :  *  visibilitymap_pin_ok - do we already have the correct page pinned?
                                226                 :                :  *
                                227                 :                :  * On entry, vmbuf should be InvalidBuffer or a valid buffer returned by
                                228                 :                :  * an earlier call to visibilitymap_pin or visibilitymap_get_status on the same
                                229                 :                :  * relation.  The return value indicates whether the buffer covers the
                                230                 :                :  * given heapBlk.
                                231                 :                :  */
                                232                 :                : bool
                                233                 :          22248 : visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf)
                                234                 :                : {
 5513 rhaas@postgresql.org      235                 :          22248 :     BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
                                236                 :                : 
 1405 pg@bowt.ie                237   [ +  +  +  - ]:          22248 :     return BufferIsValid(vmbuf) && BufferGetBlockNumber(vmbuf) == mapBlock;
                                238                 :                : }
                                239                 :                : 
                                240                 :                : /*
                                241                 :                :  * Set VM (visibility map) flags in the VM block in vmBuf.
                                242                 :                :  *
                                243                 :                :  * This function is intended for callers that log VM changes together
                                244                 :                :  * with the heap page modifications that rendered the page all-visible.
                                245                 :                :  *
                                246                 :                :  * vmBuf must be pinned and exclusively locked, and it must cover the VM bits
                                247                 :                :  * corresponding to heapBlk.
                                248                 :                :  *
                                249                 :                :  * In normal operation (not recovery), this must be called inside a critical
                                250                 :                :  * section that also applies the necessary heap page changes and, if
                                251                 :                :  * applicable, emits WAL.
                                252                 :                :  *
                                253                 :                :  * The caller is responsible for ensuring consistency between the heap page
                                254                 :                :  * and the VM page by holding a pin and exclusive lock on the buffer
                                255                 :                :  * containing heapBlk.
                                256                 :                :  *
                                257                 :                :  * rlocator is used only for debugging messages.
                                258                 :                :  */
                                259                 :                : void
  123 melanieplageman@gmai      260                 :         101053 : visibilitymap_set(BlockNumber heapBlk,
                                261                 :                :                   Buffer vmBuf, uint8 flags,
                                262                 :                :                   RelFileLocator rlocator)
                                263                 :                : {
  289                           264                 :         101053 :     BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
                                265                 :         101053 :     uint32      mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
                                266                 :         101053 :     uint8       mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
                                267                 :                :     Page        page;
                                268                 :                :     uint8      *map;
                                269                 :                :     uint8       status;
                                270                 :                : 
                                271                 :                : #ifdef TRACE_VISIBILITYMAP
                                272                 :                :     elog(DEBUG1, "vm_set flags 0x%02X for %s %d",
                                273                 :                :          flags,
                                274                 :                :          relpathbackend(rlocator, MyProcNumber, MAIN_FORKNUM).str,
                                275                 :                :          heapBlk);
                                276                 :                : #endif
                                277                 :                : 
                                278                 :                :     /* Call in same critical section where WAL is emitted. */
                                279   [ +  +  -  + ]:         101053 :     Assert(InRecovery || CritSectionCount > 0);
                                280                 :                : 
                                281                 :                :     /* Flags should be valid. Also never clear bits with this function */
                                282         [ -  + ]:         101053 :     Assert((flags & VISIBILITYMAP_VALID_BITS) == flags);
                                283                 :                : 
                                284                 :                :     /* Must never set all_frozen bit without also setting all_visible bit */
                                285         [ -  + ]:         101053 :     Assert(flags != VISIBILITYMAP_ALL_FROZEN);
                                286                 :                : 
                                287                 :                :     /* Check that we have the right VM page pinned */
                                288   [ +  -  -  + ]:         101053 :     if (!BufferIsValid(vmBuf) || BufferGetBlockNumber(vmBuf) != mapBlock)
  289 melanieplageman@gmai      289         [ #  # ]:UBC           0 :         elog(ERROR, "wrong VM buffer passed to visibilitymap_set");
                                290                 :                : 
  289 melanieplageman@gmai      291         [ -  + ]:CBC      101053 :     Assert(BufferIsLockedByMeInMode(vmBuf, BUFFER_LOCK_EXCLUSIVE));
                                292                 :                : 
                                293                 :         101053 :     page = BufferGetPage(vmBuf);
                                294                 :         101053 :     map = (uint8 *) PageGetContents(page);
                                295                 :                : 
                                296                 :         101053 :     status = (map[mapByte] >> mapOffset) & VISIBILITYMAP_VALID_BITS;
                                297         [ +  - ]:         101053 :     if (flags != status)
                                298                 :                :     {
                                299                 :         101053 :         map[mapByte] |= (flags << mapOffset);
                                300                 :         101053 :         MarkBufferDirty(vmBuf);
                                301                 :                :     }
                                302                 :         101053 : }
                                303                 :                : 
                                304                 :                : /*
                                305                 :                :  *  visibilitymap_get_status - get status of bits
                                306                 :                :  *
                                307                 :                :  * Are all tuples on heapBlk visible to all or are marked frozen, according
                                308                 :                :  * to the visibility map?
                                309                 :                :  *
                                310                 :                :  * On entry, *vmbuf should be InvalidBuffer or a valid buffer returned by an
                                311                 :                :  * earlier call to visibilitymap_pin or visibilitymap_get_status on the same
                                312                 :                :  * relation. On return, *vmbuf is a valid buffer with the map page containing
                                313                 :                :  * the bit for heapBlk, or InvalidBuffer. The caller is responsible for
                                314                 :                :  * releasing *vmbuf after it's done testing and setting bits.
                                315                 :                :  *
                                316                 :                :  * NOTE: This function is typically called without a lock on the heap page,
                                317                 :                :  * so somebody else could change the bit just after we look at it.  In fact,
                                318                 :                :  * since we don't lock the visibility map page either, it's even possible that
                                319                 :                :  * someone else could have changed the bit just before we look at it, but yet
                                320                 :                :  * we might see the old value.  It is the caller's responsibility to deal with
                                321                 :                :  * all concurrency issues!
                                322                 :                :  */
                                323                 :                : uint8
 1405 pg@bowt.ie                324                 :        4310710 : visibilitymap_get_status(Relation rel, BlockNumber heapBlk, Buffer *vmbuf)
                                325                 :                : {
 6443 heikki.linnakangas@i      326                 :        4310710 :     BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
                                327                 :        4310710 :     uint32      mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
 3704 rhaas@postgresql.org      328                 :        4310710 :     uint8       mapOffset = HEAPBLK_TO_OFFSET(heapBlk);
                                329                 :                :     char       *map;
                                330                 :                :     uint8       result;
                                331                 :                : 
                                332                 :                : #ifdef TRACE_VISIBILITYMAP
                                333                 :                :     elog(DEBUG1, "vm_get_status %s %d", RelationGetRelationName(rel), heapBlk);
                                334                 :                : #endif
                                335                 :                : 
                                336                 :                :     /* Reuse the old pinned buffer if possible */
 1405 pg@bowt.ie                337         [ +  + ]:        4310710 :     if (BufferIsValid(*vmbuf))
                                338                 :                :     {
                                339         [ -  + ]:        4175504 :         if (BufferGetBlockNumber(*vmbuf) != mapBlock)
                                340                 :                :         {
 1405 pg@bowt.ie                341                 :UBC           0 :             ReleaseBuffer(*vmbuf);
                                342                 :              0 :             *vmbuf = InvalidBuffer;
                                343                 :                :         }
                                344                 :                :     }
                                345                 :                : 
 1405 pg@bowt.ie                346         [ +  + ]:CBC     4310710 :     if (!BufferIsValid(*vmbuf))
                                347                 :                :     {
                                348                 :         135206 :         *vmbuf = vm_readbuf(rel, mapBlock, false);
                                349         [ +  + ]:         135206 :         if (!BufferIsValid(*vmbuf))
  366 nathan@postgresql.or      350                 :          49737 :             return (uint8) 0;
                                351                 :                :     }
                                352                 :                : 
 1405 pg@bowt.ie                353                 :        4260973 :     map = PageGetContents(BufferGetPage(*vmbuf));
                                354                 :                : 
                                355                 :                :     /*
                                356                 :                :      * A single byte read is atomic.  There could be memory-ordering effects
                                357                 :                :      * here, but for performance reasons we make it the caller's job to worry
                                358                 :                :      * about that.
                                359                 :                :      */
 3704 rhaas@postgresql.org      360                 :        4260973 :     result = ((map[mapByte] >> mapOffset) & VISIBILITYMAP_VALID_BITS);
                                361                 :        4260973 :     return result;
                                362                 :                : }
                                363                 :                : 
                                364                 :                : /*
                                365                 :                :  *  visibilitymap_count  - count number of bits set in visibility map
                                366                 :                :  *
                                367                 :                :  * Note: we ignore the possibility of race conditions when the table is being
                                368                 :                :  * extended concurrently with the call.  New pages added to the table aren't
                                369                 :                :  * going to be marked all-visible or all-frozen, so they won't affect the result.
                                370                 :                :  */
                                371                 :                : void
 3798                           372                 :          34458 : visibilitymap_count(Relation rel, BlockNumber *all_visible, BlockNumber *all_frozen)
                                373                 :                : {
                                374                 :                :     BlockNumber mapBlock;
 2717 tgl@sss.pgh.pa.us         375                 :          34458 :     BlockNumber nvisible = 0;
                                376                 :          34458 :     BlockNumber nfrozen = 0;
                                377                 :                : 
                                378                 :                :     /* all_visible must be specified */
 3798 rhaas@postgresql.org      379         [ -  + ]:          34458 :     Assert(all_visible);
                                380                 :                : 
 5158 bruce@momjian.us          381                 :          34458 :     for (mapBlock = 0;; mapBlock++)
 5398 tgl@sss.pgh.pa.us         382                 :          10999 :     {
                                383                 :                :         Buffer      mapBuffer;
                                384                 :                :         uint64     *map;
                                385                 :                : 
                                386                 :                :         /*
                                387                 :                :          * Read till we fall off the end of the map.  We assume that any extra
                                388                 :                :          * bytes in the last page are zeroed, so we don't bother excluding
                                389                 :                :          * them from the count.
                                390                 :                :          */
                                391                 :          45457 :         mapBuffer = vm_readbuf(rel, mapBlock, false);
                                392         [ +  + ]:          45457 :         if (!BufferIsValid(mapBuffer))
                                393                 :          34458 :             break;
                                394                 :                : 
                                395                 :                :         /*
                                396                 :                :          * We choose not to lock the page, since the result is going to be
                                397                 :                :          * immediately stale anyway if anyone is concurrently setting or
                                398                 :                :          * clearing bits, and we only really need an approximate value.
                                399                 :                :          */
 2717                           400                 :          10999 :         map = (uint64 *) PageGetContents(BufferGetPage(mapBuffer));
                                401                 :                : 
  840 nathan@postgresql.or      402                 :          10999 :         nvisible += pg_popcount_masked((const char *) map, MAPSIZE, VISIBLE_MASK8);
                                403         [ +  - ]:          10999 :         if (all_frozen)
                                404                 :          10999 :             nfrozen += pg_popcount_masked((const char *) map, MAPSIZE, FROZEN_MASK8);
                                405                 :                : 
 5398 tgl@sss.pgh.pa.us         406                 :          10999 :         ReleaseBuffer(mapBuffer);
                                407                 :                :     }
                                408                 :                : 
 2717                           409                 :          34458 :     *all_visible = nvisible;
                                410         [ +  - ]:          34458 :     if (all_frozen)
                                411                 :          34458 :         *all_frozen = nfrozen;
 5398                           412                 :          34458 : }
                                413                 :                : 
                                414                 :                : /*
                                415                 :                :  *  visibilitymap_prepare_truncate -
                                416                 :                :  *          prepare for truncation of the visibility map
                                417                 :                :  *
                                418                 :                :  * nheapblocks is the new size of the heap.
                                419                 :                :  *
                                420                 :                :  * Return the number of blocks of new visibility map.
                                421                 :                :  * If it's InvalidBlockNumber, there is nothing to truncate;
                                422                 :                :  * otherwise the caller is responsible for calling smgrtruncate()
                                423                 :                :  * to truncate the visibility map pages.
                                424                 :                :  */
                                425                 :                : BlockNumber
 2496 fujii@postgresql.org      426                 :            280 : visibilitymap_prepare_truncate(Relation rel, BlockNumber nheapblocks)
                                427                 :                : {
                                428                 :                :     BlockNumber newnblocks;
                                429                 :                : 
                                430                 :                :     /* last remaining block, byte, and bit */
 6443 heikki.linnakangas@i      431                 :            280 :     BlockNumber truncBlock = HEAPBLK_TO_MAPBLOCK(nheapblocks);
 6253 bruce@momjian.us          432                 :            280 :     uint32      truncByte = HEAPBLK_TO_MAPBYTE(nheapblocks);
 3704 rhaas@postgresql.org      433                 :            280 :     uint8       truncOffset = HEAPBLK_TO_OFFSET(nheapblocks);
                                434                 :                : 
                                435                 :                : #ifdef TRACE_VISIBILITYMAP
                                436                 :                :     elog(DEBUG1, "vm_truncate %s %d", RelationGetRelationName(rel), nheapblocks);
                                437                 :                : #endif
                                438                 :                : 
                                439                 :                :     /*
                                440                 :                :      * If no visibility map has been created yet for this relation, there's
                                441                 :                :      * nothing to truncate.
                                442                 :                :      */
 1839 tgl@sss.pgh.pa.us         443         [ -  + ]:            280 :     if (!smgrexists(RelationGetSmgr(rel), VISIBILITYMAP_FORKNUM))
 2496 fujii@postgresql.org      444                 :UBC           0 :         return InvalidBlockNumber;
                                445                 :                : 
                                446                 :                :     /*
                                447                 :                :      * Unless the new size is exactly at a visibility map page boundary, the
                                448                 :                :      * tail bits in the last remaining map page, representing truncated heap
                                449                 :                :      * blocks, need to be cleared. This is not only tidy, but also necessary
                                450                 :                :      * because we don't get a chance to clear the bits if the heap is extended
                                451                 :                :      * again.
                                452                 :                :      */
 3704 rhaas@postgresql.org      453   [ +  +  +  + ]:CBC         280 :     if (truncByte != 0 || truncOffset != 0)
 6443 heikki.linnakangas@i      454                 :            173 :     {
                                455                 :                :         Buffer      mapBuffer;
                                456                 :                :         Page        page;
                                457                 :                :         char       *map;
                                458                 :                : 
                                459                 :            173 :         newnblocks = truncBlock + 1;
                                460                 :                : 
                                461                 :            173 :         mapBuffer = vm_readbuf(rel, truncBlock, false);
                                462         [ -  + ]:            173 :         if (!BufferIsValid(mapBuffer))
                                463                 :                :         {
                                464                 :                :             /* nothing to do, the file was already smaller */
 2496 fujii@postgresql.org      465                 :UBC           0 :             return InvalidBlockNumber;
                                466                 :                :         }
                                467                 :                : 
 3748 kgrittn@postgresql.o      468                 :CBC         173 :         page = BufferGetPage(mapBuffer);
 6443 heikki.linnakangas@i      469                 :            173 :         map = PageGetContents(page);
                                470                 :                : 
                                471                 :            173 :         LockBuffer(mapBuffer, BUFFER_LOCK_EXCLUSIVE);
                                472                 :                : 
                                473                 :                :         /* NO EREPORT(ERROR) from here till changes are logged */
 3566                           474                 :            173 :         START_CRIT_SECTION();
                                475                 :                : 
                                476                 :                :         /* Clear out the unwanted bytes. */
 6443                           477   [ +  +  +  -  :            173 :         MemSet(&map[truncByte + 1], 0, MAPSIZE - (truncByte + 1));
                                     +  -  -  +  -  
                                                 - ]
                                478                 :                : 
                                479                 :                :         /*----
                                480                 :                :          * Mask out the unwanted bits of the last remaining byte.
                                481                 :                :          *
                                482                 :                :          * ((1 << 0) - 1) = 00000000
                                483                 :                :          * ((1 << 1) - 1) = 00000001
                                484                 :                :          * ...
                                485                 :                :          * ((1 << 6) - 1) = 00111111
                                486                 :                :          * ((1 << 7) - 1) = 01111111
                                487                 :                :          *----
                                488                 :                :          */
 3704 rhaas@postgresql.org      489                 :            173 :         map[truncByte] &= (1 << truncOffset) - 1;
                                490                 :                : 
                                491                 :                :         /*
                                492                 :                :          * Truncation of a relation is WAL-logged at a higher-level, and we
                                493                 :                :          * will be called at WAL replay. But if checksums are enabled, we need
                                494                 :                :          * to still write a WAL record to protect against a torn page, if the
                                495                 :                :          * page is flushed to disk before the truncation WAL record. We cannot
                                496                 :                :          * use MarkBufferDirtyHint here, because that will not dirty the page
                                497                 :                :          * during recovery.
                                498                 :                :          */
 6443 heikki.linnakangas@i      499                 :            173 :         MarkBufferDirty(mapBuffer);
 3566                           500   [ +  +  +  +  :            173 :         if (!InRecovery && RelationNeedsWAL(rel) && XLogHintBitIsNeeded())
                                     +  +  +  -  +  
                                        -  +  +  +  
                                                 - ]
                                501                 :            151 :             log_newpage_buffer(mapBuffer, false);
                                502                 :                : 
                                503         [ -  + ]:            173 :         END_CRIT_SECTION();
                                504                 :                : 
 6443                           505                 :            173 :         UnlockReleaseBuffer(mapBuffer);
                                506                 :                :     }
                                507                 :                :     else
                                508                 :            107 :         newnblocks = truncBlock;
                                509                 :                : 
 1839 tgl@sss.pgh.pa.us         510         [ +  + ]:            280 :     if (smgrnblocks(RelationGetSmgr(rel), VISIBILITYMAP_FORKNUM) <= newnblocks)
                                511                 :                :     {
                                512                 :                :         /* nothing to do, the file was already smaller than requested size */
 2496 fujii@postgresql.org      513                 :            173 :         return InvalidBlockNumber;
                                514                 :                :     }
                                515                 :                : 
                                516                 :            107 :     return newnblocks;
                                517                 :                : }
                                518                 :                : 
                                519                 :                : /*
                                520                 :                :  *  visibilitymap_truncation_length -
                                521                 :                :  *          compute truncation length for visibility map
                                522                 :                :  *
                                523                 :                :  * Given a proposed truncation length for the main fork, compute the
                                524                 :                :  * correct truncation length for the visibility map. Should return the
                                525                 :                :  * same answer as visibilitymap_prepare_truncate(), but without modifying
                                526                 :                :  * anything.
                                527                 :                :  */
                                528                 :                : BlockNumber
  138 rhaas@postgresql.org      529                 :              1 : visibilitymap_truncation_length(BlockNumber nheapblocks)
                                530                 :                : {
                                531                 :              1 :     return HEAPBLK_TO_MAPBLOCK_LIMIT(nheapblocks);
                                532                 :                : }
                                533                 :                : 
                                534                 :                : /*
                                535                 :                :  * Read a visibility map page.
                                536                 :                :  *
                                537                 :                :  * If the page doesn't exist, InvalidBuffer is returned, or if 'extend' is
                                538                 :                :  * true, the visibility map file is extended.
                                539                 :                :  */
                                540                 :                : static Buffer
 6443 heikki.linnakangas@i      541                 :         686109 : vm_readbuf(Relation rel, BlockNumber blkno, bool extend)
                                542                 :                : {
                                543                 :                :     Buffer      buf;
                                544                 :                :     SMgrRelation reln;
                                545                 :                : 
                                546                 :                :     /*
                                547                 :                :      * Caution: re-using this smgr pointer could fail if the relcache entry
                                548                 :                :      * gets closed.  It's safe as long as we only do smgr-level operations
                                549                 :                :      * between here and the last use of the pointer.
                                550                 :                :      */
 1839 tgl@sss.pgh.pa.us         551                 :         686109 :     reln = RelationGetSmgr(rel);
                                552                 :                : 
                                553                 :                :     /*
                                554                 :                :      * If we haven't cached the size of the visibility map fork yet, check it
                                555                 :                :      * first.
                                556                 :                :      */
                                557         [ +  + ]:         686109 :     if (reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] == InvalidBlockNumber)
                                558                 :                :     {
                                559         [ +  + ]:          55773 :         if (smgrexists(reln, VISIBILITYMAP_FORKNUM))
                                560                 :          29505 :             smgrnblocks(reln, VISIBILITYMAP_FORKNUM);
                                561                 :                :         else
                                562                 :          26268 :             reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM] = 0;
                                563                 :                :     }
                                564                 :                : 
                                565                 :                :     /*
                                566                 :                :      * For reading we use ZERO_ON_ERROR mode, and initialize the page if
                                567                 :                :      * necessary. It's always safe to clear bits, so it's better to clear
                                568                 :                :      * corrupt pages than error out.
                                569                 :                :      *
                                570                 :                :      * We use the same path below to initialize pages when extending the
                                571                 :                :      * relation, as a concurrent extension can end up with vm_extend()
                                572                 :                :      * returning an already-initialized page.
                                573                 :                :      */
                                574         [ +  + ]:         686109 :     if (blkno >= reln->smgr_cached_nblocks[VISIBILITYMAP_FORKNUM])
                                575                 :                :     {
 6443 heikki.linnakangas@i      576         [ +  + ]:          88727 :         if (extend)
 1207 andres@anarazel.de        577                 :           4532 :             buf = vm_extend(rel, blkno + 1);
                                578                 :                :         else
 6443 heikki.linnakangas@i      579                 :          84195 :             return InvalidBuffer;
                                580                 :                :     }
                                581                 :                :     else
 1207 andres@anarazel.de        582                 :         597382 :         buf = ReadBufferExtended(rel, VISIBILITYMAP_FORKNUM, blkno,
                                583                 :                :                                  RBM_ZERO_ON_ERROR, NULL);
                                584                 :                : 
                                585                 :                :     /*
                                586                 :                :      * Initializing the page when needed is trickier than it looks, because of
                                587                 :                :      * the possibility of multiple backends doing this concurrently, and our
                                588                 :                :      * desire to not uselessly take the buffer lock in the normal path where
                                589                 :                :      * the page is OK.  We must take the lock to initialize the page, so
                                590                 :                :      * recheck page newness after we have the lock, in case someone else
                                591                 :                :      * already did it.  Also, because we initially check PageIsNew with no
                                592                 :                :      * lock, it's possible to fall through and return the buffer while someone
                                593                 :                :      * else is still initializing the page (i.e., we might see pd_upper as set
                                594                 :                :      * but other page header fields are still zeroes).  This is harmless for
                                595                 :                :      * callers that will take a buffer lock themselves, but some callers
                                596                 :                :      * inspect the page without any lock at all.  The latter is OK only so
                                597                 :                :      * long as it doesn't depend on the page header having correct contents.
                                598                 :                :      * Current usage is safe because PageGetContents() does not require that.
                                599                 :                :      */
 3748 kgrittn@postgresql.o      600         [ +  + ]:         601914 :     if (PageIsNew(BufferGetPage(buf)))
                                601                 :                :     {
 2934 tgl@sss.pgh.pa.us         602                 :           4934 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
                                603         [ +  - ]:           4934 :         if (PageIsNew(BufferGetPage(buf)))
                                604                 :           4934 :             PageInit(BufferGetPage(buf), BLCKSZ, 0);
                                605                 :           4934 :         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
                                606                 :                :     }
 6443 heikki.linnakangas@i      607                 :         601914 :     return buf;
                                608                 :                : }
                                609                 :                : 
                                610                 :                : /*
                                611                 :                :  * Ensure that the visibility map fork is at least vm_nblocks long, extending
                                612                 :                :  * it if necessary with zeroed pages.
                                613                 :                :  */
                                614                 :                : static Buffer
                                615                 :           4532 : vm_extend(Relation rel, BlockNumber vm_nblocks)
                                616                 :                : {
                                617                 :                :     Buffer      buf;
                                618                 :                : 
 1067 tmunro@postgresql.or      619                 :           4532 :     buf = ExtendBufferedRelTo(BMR_REL(rel), VISIBILITYMAP_FORKNUM, NULL,
                                620                 :                :                               EB_CREATE_FORK_IF_NEEDED |
                                621                 :                :                               EB_CLEAR_SIZE_CACHE,
                                622                 :                :                               vm_nblocks,
                                623                 :                :                               RBM_ZERO_ON_ERROR);
                                624                 :                : 
                                625                 :                :     /*
                                626                 :                :      * Send a shared-inval message to force other backends to close any smgr
                                627                 :                :      * references they may have for this rel, which we are about to change.
                                628                 :                :      * This is a useful optimization because it means that backends don't have
                                629                 :                :      * to keep checking for creation or extension of the file, which happens
                                630                 :                :      * infrequently.
                                631                 :                :      */
 1207 andres@anarazel.de        632                 :           4532 :     CacheInvalidateSmgr(RelationGetSmgr(rel)->smgr_rlocator);
                                633                 :                : 
                                634                 :           4532 :     return buf;
                                635                 :                : }
        

Generated by: LCOV version 2.0-1