LCOV - code coverage report
Current view: top level - src/backend/access/heap - heapam_xlog.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 485 540 89.8 %
Date: 2024-11-21 08:14:44 Functions: 13 14 92.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * heapam_xlog.c
       4             :  *    WAL replay logic for heap access method.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/heap/heapam_xlog.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/bufmask.h"
      18             : #include "access/heapam.h"
      19             : #include "access/visibilitymap.h"
      20             : #include "access/xlog.h"
      21             : #include "access/xlogutils.h"
      22             : #include "storage/freespace.h"
      23             : #include "storage/standby.h"
      24             : 
      25             : 
      26             : /*
      27             :  * Replay XLOG_HEAP2_PRUNE_* records.
      28             :  */
      29             : static void
      30       17814 : heap_xlog_prune_freeze(XLogReaderState *record)
      31             : {
      32       17814 :     XLogRecPtr  lsn = record->EndRecPtr;
      33       17814 :     char       *maindataptr = XLogRecGetData(record);
      34             :     xl_heap_prune xlrec;
      35             :     Buffer      buffer;
      36             :     RelFileLocator rlocator;
      37             :     BlockNumber blkno;
      38             :     XLogRedoAction action;
      39             : 
      40       17814 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
      41       17814 :     memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
      42       17814 :     maindataptr += SizeOfHeapPrune;
      43             : 
      44             :     /*
      45             :      * We will take an ordinary exclusive lock or a cleanup lock depending on
      46             :      * whether the XLHP_CLEANUP_LOCK flag is set.  With an ordinary exclusive
      47             :      * lock, we better not be doing anything that requires moving existing
      48             :      * tuple data.
      49             :      */
      50             :     Assert((xlrec.flags & XLHP_CLEANUP_LOCK) != 0 ||
      51             :            (xlrec.flags & (XLHP_HAS_REDIRECTIONS | XLHP_HAS_DEAD_ITEMS)) == 0);
      52             : 
      53             :     /*
      54             :      * We are about to remove and/or freeze tuples.  In Hot Standby mode,
      55             :      * ensure that there are no queries running for which the removed tuples
      56             :      * are still visible or which still consider the frozen xids as running.
      57             :      * The conflict horizon XID comes after xl_heap_prune.
      58             :      */
      59       17814 :     if ((xlrec.flags & XLHP_HAS_CONFLICT_HORIZON) != 0)
      60             :     {
      61             :         TransactionId snapshot_conflict_horizon;
      62             : 
      63             :         /* memcpy() because snapshot_conflict_horizon is stored unaligned */
      64       14298 :         memcpy(&snapshot_conflict_horizon, maindataptr, sizeof(TransactionId));
      65       14298 :         maindataptr += sizeof(TransactionId);
      66             : 
      67       14298 :         if (InHotStandby)
      68       13828 :             ResolveRecoveryConflictWithSnapshot(snapshot_conflict_horizon,
      69       13828 :                                                 (xlrec.flags & XLHP_IS_CATALOG_REL) != 0,
      70             :                                                 rlocator);
      71             :     }
      72             : 
      73             :     /*
      74             :      * If we have a full-page image, restore it and we're done.
      75             :      */
      76       17814 :     action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL,
      77       17814 :                                            (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
      78             :                                            &buffer);
      79       17814 :     if (action == BLK_NEEDS_REDO)
      80             :     {
      81       15564 :         Page        page = (Page) BufferGetPage(buffer);
      82             :         OffsetNumber *redirected;
      83             :         OffsetNumber *nowdead;
      84             :         OffsetNumber *nowunused;
      85             :         int         nredirected;
      86             :         int         ndead;
      87             :         int         nunused;
      88             :         int         nplans;
      89             :         Size        datalen;
      90             :         xlhp_freeze_plan *plans;
      91             :         OffsetNumber *frz_offsets;
      92       15564 :         char       *dataptr = XLogRecGetBlockData(record, 0, &datalen);
      93             : 
      94       15564 :         heap_xlog_deserialize_prune_and_freeze(dataptr, xlrec.flags,
      95             :                                                &nplans, &plans, &frz_offsets,
      96             :                                                &nredirected, &redirected,
      97             :                                                &ndead, &nowdead,
      98             :                                                &nunused, &nowunused);
      99             : 
     100             :         /*
     101             :          * Update all line pointers per the record, and repair fragmentation
     102             :          * if needed.
     103             :          */
     104       15564 :         if (nredirected > 0 || ndead > 0 || nunused > 0)
     105       14612 :             heap_page_prune_execute(buffer,
     106       14612 :                                     (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
     107             :                                     redirected, nredirected,
     108             :                                     nowdead, ndead,
     109             :                                     nowunused, nunused);
     110             : 
     111             :         /* Freeze tuples */
     112       16706 :         for (int p = 0; p < nplans; p++)
     113             :         {
     114             :             HeapTupleFreeze frz;
     115             : 
     116             :             /*
     117             :              * Convert freeze plan representation from WAL record into
     118             :              * per-tuple format used by heap_execute_freeze_tuple
     119             :              */
     120        1142 :             frz.xmax = plans[p].xmax;
     121        1142 :             frz.t_infomask2 = plans[p].t_infomask2;
     122        1142 :             frz.t_infomask = plans[p].t_infomask;
     123        1142 :             frz.frzflags = plans[p].frzflags;
     124        1142 :             frz.offset = InvalidOffsetNumber;   /* unused, but be tidy */
     125             : 
     126      100760 :             for (int i = 0; i < plans[p].ntuples; i++)
     127             :             {
     128       99618 :                 OffsetNumber offset = *(frz_offsets++);
     129             :                 ItemId      lp;
     130             :                 HeapTupleHeader tuple;
     131             : 
     132       99618 :                 lp = PageGetItemId(page, offset);
     133       99618 :                 tuple = (HeapTupleHeader) PageGetItem(page, lp);
     134       99618 :                 heap_execute_freeze_tuple(tuple, &frz);
     135             :             }
     136             :         }
     137             : 
     138             :         /* There should be no more data */
     139             :         Assert((char *) frz_offsets == dataptr + datalen);
     140             : 
     141             :         /*
     142             :          * Note: we don't worry about updating the page's prunability hints.
     143             :          * At worst this will cause an extra prune cycle to occur soon.
     144             :          */
     145             : 
     146       15564 :         PageSetLSN(page, lsn);
     147       15564 :         MarkBufferDirty(buffer);
     148             :     }
     149             : 
     150             :     /*
     151             :      * If we released any space or line pointers, update the free space map.
     152             :      *
     153             :      * Do this regardless of a full-page image being applied, since the FSM
     154             :      * data is not in the page anyway.
     155             :      */
     156       17814 :     if (BufferIsValid(buffer))
     157             :     {
     158       17814 :         if (xlrec.flags & (XLHP_HAS_REDIRECTIONS |
     159             :                            XLHP_HAS_DEAD_ITEMS |
     160             :                            XLHP_HAS_NOW_UNUSED_ITEMS))
     161             :         {
     162       16860 :             Size        freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
     163             : 
     164       16860 :             UnlockReleaseBuffer(buffer);
     165             : 
     166       16860 :             XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
     167             :         }
     168             :         else
     169         954 :             UnlockReleaseBuffer(buffer);
     170             :     }
     171       17814 : }
     172             : 
     173             : /*
     174             :  * Replay XLOG_HEAP2_VISIBLE records.
     175             :  *
     176             :  * The critical integrity requirement here is that we must never end up with
     177             :  * a situation where the visibility map bit is set, and the page-level
     178             :  * PD_ALL_VISIBLE bit is clear.  If that were to occur, then a subsequent
     179             :  * page modification would fail to clear the visibility map bit.
     180             :  */
     181             : static void
     182        7622 : heap_xlog_visible(XLogReaderState *record)
     183             : {
     184        7622 :     XLogRecPtr  lsn = record->EndRecPtr;
     185        7622 :     xl_heap_visible *xlrec = (xl_heap_visible *) XLogRecGetData(record);
     186        7622 :     Buffer      vmbuffer = InvalidBuffer;
     187             :     Buffer      buffer;
     188             :     Page        page;
     189             :     RelFileLocator rlocator;
     190             :     BlockNumber blkno;
     191             :     XLogRedoAction action;
     192             : 
     193             :     Assert((xlrec->flags & VISIBILITYMAP_XLOG_VALID_BITS) == xlrec->flags);
     194             : 
     195        7622 :     XLogRecGetBlockTag(record, 1, &rlocator, NULL, &blkno);
     196             : 
     197             :     /*
     198             :      * If there are any Hot Standby transactions running that have an xmin
     199             :      * horizon old enough that this page isn't all-visible for them, they
     200             :      * might incorrectly decide that an index-only scan can skip a heap fetch.
     201             :      *
     202             :      * NB: It might be better to throw some kind of "soft" conflict here that
     203             :      * forces any index-only scan that is in flight to perform heap fetches,
     204             :      * rather than killing the transaction outright.
     205             :      */
     206        7622 :     if (InHotStandby)
     207        7274 :         ResolveRecoveryConflictWithSnapshot(xlrec->snapshotConflictHorizon,
     208        7274 :                                             xlrec->flags & VISIBILITYMAP_XLOG_CATALOG_REL,
     209             :                                             rlocator);
     210             : 
     211             :     /*
     212             :      * Read the heap page, if it still exists. If the heap file has dropped or
     213             :      * truncated later in recovery, we don't need to update the page, but we'd
     214             :      * better still update the visibility map.
     215             :      */
     216        7622 :     action = XLogReadBufferForRedo(record, 1, &buffer);
     217        7622 :     if (action == BLK_NEEDS_REDO)
     218             :     {
     219             :         /*
     220             :          * We don't bump the LSN of the heap page when setting the visibility
     221             :          * map bit (unless checksums or wal_hint_bits is enabled, in which
     222             :          * case we must). This exposes us to torn page hazards, but since
     223             :          * we're not inspecting the existing page contents in any way, we
     224             :          * don't care.
     225             :          */
     226        5558 :         page = BufferGetPage(buffer);
     227             : 
     228        5558 :         PageSetAllVisible(page);
     229             : 
     230        5558 :         if (XLogHintBitIsNeeded())
     231        5558 :             PageSetLSN(page, lsn);
     232             : 
     233        5558 :         MarkBufferDirty(buffer);
     234             :     }
     235             :     else if (action == BLK_RESTORED)
     236             :     {
     237             :         /*
     238             :          * If heap block was backed up, we already restored it and there's
     239             :          * nothing more to do. (This can only happen with checksums or
     240             :          * wal_log_hints enabled.)
     241             :          */
     242             :     }
     243             : 
     244        7622 :     if (BufferIsValid(buffer))
     245             :     {
     246        7622 :         Size        space = PageGetFreeSpace(BufferGetPage(buffer));
     247             : 
     248        7622 :         UnlockReleaseBuffer(buffer);
     249             : 
     250             :         /*
     251             :          * Since FSM is not WAL-logged and only updated heuristically, it
     252             :          * easily becomes stale in standbys.  If the standby is later promoted
     253             :          * and runs VACUUM, it will skip updating individual free space
     254             :          * figures for pages that became all-visible (or all-frozen, depending
     255             :          * on the vacuum mode,) which is troublesome when FreeSpaceMapVacuum
     256             :          * propagates too optimistic free space values to upper FSM layers;
     257             :          * later inserters try to use such pages only to find out that they
     258             :          * are unusable.  This can cause long stalls when there are many such
     259             :          * pages.
     260             :          *
     261             :          * Forestall those problems by updating FSM's idea about a page that
     262             :          * is becoming all-visible or all-frozen.
     263             :          *
     264             :          * Do this regardless of a full-page image being applied, since the
     265             :          * FSM data is not in the page anyway.
     266             :          */
     267        7622 :         if (xlrec->flags & VISIBILITYMAP_VALID_BITS)
     268        7622 :             XLogRecordPageWithFreeSpace(rlocator, blkno, space);
     269             :     }
     270             : 
     271             :     /*
     272             :      * Even if we skipped the heap page update due to the LSN interlock, it's
     273             :      * still safe to update the visibility map.  Any WAL record that clears
     274             :      * the visibility map bit does so before checking the page LSN, so any
     275             :      * bits that need to be cleared will still be cleared.
     276             :      */
     277        7622 :     if (XLogReadBufferForRedoExtended(record, 0, RBM_ZERO_ON_ERROR, false,
     278             :                                       &vmbuffer) == BLK_NEEDS_REDO)
     279             :     {
     280        7188 :         Page        vmpage = BufferGetPage(vmbuffer);
     281             :         Relation    reln;
     282             :         uint8       vmbits;
     283             : 
     284             :         /* initialize the page if it was read as zeros */
     285        7188 :         if (PageIsNew(vmpage))
     286           0 :             PageInit(vmpage, BLCKSZ, 0);
     287             : 
     288             :         /* remove VISIBILITYMAP_XLOG_* */
     289        7188 :         vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
     290             : 
     291             :         /*
     292             :          * XLogReadBufferForRedoExtended locked the buffer. But
     293             :          * visibilitymap_set will handle locking itself.
     294             :          */
     295        7188 :         LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
     296             : 
     297        7188 :         reln = CreateFakeRelcacheEntry(rlocator);
     298        7188 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     299             : 
     300        7188 :         visibilitymap_set(reln, blkno, InvalidBuffer, lsn, vmbuffer,
     301             :                           xlrec->snapshotConflictHorizon, vmbits);
     302             : 
     303        7188 :         ReleaseBuffer(vmbuffer);
     304        7188 :         FreeFakeRelcacheEntry(reln);
     305             :     }
     306         434 :     else if (BufferIsValid(vmbuffer))
     307         434 :         UnlockReleaseBuffer(vmbuffer);
     308        7622 : }
     309             : 
     310             : /*
     311             :  * Given an "infobits" field from an XLog record, set the correct bits in the
     312             :  * given infomask and infomask2 for the tuple touched by the record.
     313             :  *
     314             :  * (This is the reverse of compute_infobits).
     315             :  */
     316             : static void
     317      870540 : fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
     318             : {
     319      870540 :     *infomask &= ~(HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY |
     320             :                    HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK);
     321      870540 :     *infomask2 &= ~HEAP_KEYS_UPDATED;
     322             : 
     323      870540 :     if (infobits & XLHL_XMAX_IS_MULTI)
     324           4 :         *infomask |= HEAP_XMAX_IS_MULTI;
     325      870540 :     if (infobits & XLHL_XMAX_LOCK_ONLY)
     326      108608 :         *infomask |= HEAP_XMAX_LOCK_ONLY;
     327      870540 :     if (infobits & XLHL_XMAX_EXCL_LOCK)
     328      107814 :         *infomask |= HEAP_XMAX_EXCL_LOCK;
     329             :     /* note HEAP_XMAX_SHR_LOCK isn't considered here */
     330      870540 :     if (infobits & XLHL_XMAX_KEYSHR_LOCK)
     331         816 :         *infomask |= HEAP_XMAX_KEYSHR_LOCK;
     332             : 
     333      870540 :     if (infobits & XLHL_KEYS_UPDATED)
     334      581766 :         *infomask2 |= HEAP_KEYS_UPDATED;
     335      870540 : }
     336             : 
     337             : /*
     338             :  * Replay XLOG_HEAP_DELETE records.
     339             :  */
     340             : static void
     341      582660 : heap_xlog_delete(XLogReaderState *record)
     342             : {
     343      582660 :     XLogRecPtr  lsn = record->EndRecPtr;
     344      582660 :     xl_heap_delete *xlrec = (xl_heap_delete *) XLogRecGetData(record);
     345             :     Buffer      buffer;
     346             :     Page        page;
     347      582660 :     ItemId      lp = NULL;
     348             :     HeapTupleHeader htup;
     349             :     BlockNumber blkno;
     350             :     RelFileLocator target_locator;
     351             :     ItemPointerData target_tid;
     352             : 
     353      582660 :     XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
     354      582660 :     ItemPointerSetBlockNumber(&target_tid, blkno);
     355      582660 :     ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
     356             : 
     357             :     /*
     358             :      * The visibility map may need to be fixed even if the heap page is
     359             :      * already up-to-date.
     360             :      */
     361      582660 :     if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
     362             :     {
     363         344 :         Relation    reln = CreateFakeRelcacheEntry(target_locator);
     364         344 :         Buffer      vmbuffer = InvalidBuffer;
     365             : 
     366         344 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     367         344 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     368         344 :         ReleaseBuffer(vmbuffer);
     369         344 :         FreeFakeRelcacheEntry(reln);
     370             :     }
     371             : 
     372      582660 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     373             :     {
     374      579064 :         page = BufferGetPage(buffer);
     375             : 
     376      579064 :         if (PageGetMaxOffsetNumber(page) >= xlrec->offnum)
     377      579064 :             lp = PageGetItemId(page, xlrec->offnum);
     378             : 
     379      579064 :         if (PageGetMaxOffsetNumber(page) < xlrec->offnum || !ItemIdIsNormal(lp))
     380           0 :             elog(PANIC, "invalid lp");
     381             : 
     382      579064 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     383             : 
     384      579064 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
     385      579064 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
     386      579064 :         HeapTupleHeaderClearHotUpdated(htup);
     387      579064 :         fix_infomask_from_infobits(xlrec->infobits_set,
     388             :                                    &htup->t_infomask, &htup->t_infomask2);
     389      579064 :         if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
     390      579064 :             HeapTupleHeaderSetXmax(htup, xlrec->xmax);
     391             :         else
     392           0 :             HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
     393      579064 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
     394             : 
     395             :         /* Mark the page as a candidate for pruning */
     396      579064 :         PageSetPrunable(page, XLogRecGetXid(record));
     397             : 
     398      579064 :         if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
     399           6 :             PageClearAllVisible(page);
     400             : 
     401             :         /* Make sure t_ctid is set correctly */
     402      579064 :         if (xlrec->flags & XLH_DELETE_IS_PARTITION_MOVE)
     403         270 :             HeapTupleHeaderSetMovedPartitions(htup);
     404             :         else
     405      578794 :             htup->t_ctid = target_tid;
     406      579064 :         PageSetLSN(page, lsn);
     407      579064 :         MarkBufferDirty(buffer);
     408             :     }
     409      582660 :     if (BufferIsValid(buffer))
     410      582660 :         UnlockReleaseBuffer(buffer);
     411      582660 : }
     412             : 
     413             : /*
     414             :  * Replay XLOG_HEAP_INSERT records.
     415             :  */
     416             : static void
     417     2495992 : heap_xlog_insert(XLogReaderState *record)
     418             : {
     419     2495992 :     XLogRecPtr  lsn = record->EndRecPtr;
     420     2495992 :     xl_heap_insert *xlrec = (xl_heap_insert *) XLogRecGetData(record);
     421             :     Buffer      buffer;
     422             :     Page        page;
     423             :     union
     424             :     {
     425             :         HeapTupleHeaderData hdr;
     426             :         char        data[MaxHeapTupleSize];
     427             :     }           tbuf;
     428             :     HeapTupleHeader htup;
     429             :     xl_heap_header xlhdr;
     430             :     uint32      newlen;
     431     2495992 :     Size        freespace = 0;
     432             :     RelFileLocator target_locator;
     433             :     BlockNumber blkno;
     434             :     ItemPointerData target_tid;
     435             :     XLogRedoAction action;
     436             : 
     437     2495992 :     XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
     438     2495992 :     ItemPointerSetBlockNumber(&target_tid, blkno);
     439     2495992 :     ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
     440             : 
     441             :     /*
     442             :      * The visibility map may need to be fixed even if the heap page is
     443             :      * already up-to-date.
     444             :      */
     445     2495992 :     if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     446             :     {
     447        1186 :         Relation    reln = CreateFakeRelcacheEntry(target_locator);
     448        1186 :         Buffer      vmbuffer = InvalidBuffer;
     449             : 
     450        1186 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     451        1186 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     452        1186 :         ReleaseBuffer(vmbuffer);
     453        1186 :         FreeFakeRelcacheEntry(reln);
     454             :     }
     455             : 
     456             :     /*
     457             :      * If we inserted the first and only tuple on the page, re-initialize the
     458             :      * page from scratch.
     459             :      */
     460     2495992 :     if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
     461             :     {
     462       31550 :         buffer = XLogInitBufferForRedo(record, 0);
     463       31550 :         page = BufferGetPage(buffer);
     464       31550 :         PageInit(page, BufferGetPageSize(buffer), 0);
     465       31550 :         action = BLK_NEEDS_REDO;
     466             :     }
     467             :     else
     468     2464442 :         action = XLogReadBufferForRedo(record, 0, &buffer);
     469     2495992 :     if (action == BLK_NEEDS_REDO)
     470             :     {
     471             :         Size        datalen;
     472             :         char       *data;
     473             : 
     474     2491558 :         page = BufferGetPage(buffer);
     475             : 
     476     2491558 :         if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
     477           0 :             elog(PANIC, "invalid max offset number");
     478             : 
     479     2491558 :         data = XLogRecGetBlockData(record, 0, &datalen);
     480             : 
     481     2491558 :         newlen = datalen - SizeOfHeapHeader;
     482             :         Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
     483     2491558 :         memcpy((char *) &xlhdr, data, SizeOfHeapHeader);
     484     2491558 :         data += SizeOfHeapHeader;
     485             : 
     486     2491558 :         htup = &tbuf.hdr;
     487     2491558 :         MemSet((char *) htup, 0, SizeofHeapTupleHeader);
     488             :         /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
     489     2491558 :         memcpy((char *) htup + SizeofHeapTupleHeader,
     490             :                data,
     491             :                newlen);
     492     2491558 :         newlen += SizeofHeapTupleHeader;
     493     2491558 :         htup->t_infomask2 = xlhdr.t_infomask2;
     494     2491558 :         htup->t_infomask = xlhdr.t_infomask;
     495     2491558 :         htup->t_hoff = xlhdr.t_hoff;
     496     2491558 :         HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     497     2491558 :         HeapTupleHeaderSetCmin(htup, FirstCommandId);
     498     2491558 :         htup->t_ctid = target_tid;
     499             : 
     500     2491558 :         if (PageAddItem(page, (Item) htup, newlen, xlrec->offnum,
     501             :                         true, true) == InvalidOffsetNumber)
     502           0 :             elog(PANIC, "failed to add tuple");
     503             : 
     504     2491558 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     505             : 
     506     2491558 :         PageSetLSN(page, lsn);
     507             : 
     508     2491558 :         if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     509         376 :             PageClearAllVisible(page);
     510             : 
     511             :         /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
     512     2491558 :         if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
     513           0 :             PageSetAllVisible(page);
     514             : 
     515     2491558 :         MarkBufferDirty(buffer);
     516             :     }
     517     2495992 :     if (BufferIsValid(buffer))
     518     2495992 :         UnlockReleaseBuffer(buffer);
     519             : 
     520             :     /*
     521             :      * If the page is running low on free space, update the FSM as well.
     522             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     523             :      * better than that without knowing the fill-factor for the table.
     524             :      *
     525             :      * XXX: Don't do this if the page was restored from full page image. We
     526             :      * don't bother to update the FSM in that case, it doesn't need to be
     527             :      * totally accurate anyway.
     528             :      */
     529     2495992 :     if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
     530      490106 :         XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
     531     2495992 : }
     532             : 
     533             : /*
     534             :  * Replay XLOG_HEAP2_MULTI_INSERT records.
     535             :  */
     536             : static void
     537      106728 : heap_xlog_multi_insert(XLogReaderState *record)
     538             : {
     539      106728 :     XLogRecPtr  lsn = record->EndRecPtr;
     540             :     xl_heap_multi_insert *xlrec;
     541             :     RelFileLocator rlocator;
     542             :     BlockNumber blkno;
     543             :     Buffer      buffer;
     544             :     Page        page;
     545             :     union
     546             :     {
     547             :         HeapTupleHeaderData hdr;
     548             :         char        data[MaxHeapTupleSize];
     549             :     }           tbuf;
     550             :     HeapTupleHeader htup;
     551             :     uint32      newlen;
     552      106728 :     Size        freespace = 0;
     553             :     int         i;
     554      106728 :     bool        isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
     555             :     XLogRedoAction action;
     556             : 
     557             :     /*
     558             :      * Insertion doesn't overwrite MVCC data, so no conflict processing is
     559             :      * required.
     560             :      */
     561      106728 :     xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
     562             : 
     563      106728 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
     564             : 
     565             :     /* check that the mutually exclusive flags are not both set */
     566             :     Assert(!((xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) &&
     567             :              (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)));
     568             : 
     569             :     /*
     570             :      * The visibility map may need to be fixed even if the heap page is
     571             :      * already up-to-date.
     572             :      */
     573      106728 :     if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     574             :     {
     575        1002 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     576        1002 :         Buffer      vmbuffer = InvalidBuffer;
     577             : 
     578        1002 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     579        1002 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     580        1002 :         ReleaseBuffer(vmbuffer);
     581        1002 :         FreeFakeRelcacheEntry(reln);
     582             :     }
     583             : 
     584      106728 :     if (isinit)
     585             :     {
     586        4112 :         buffer = XLogInitBufferForRedo(record, 0);
     587        4112 :         page = BufferGetPage(buffer);
     588        4112 :         PageInit(page, BufferGetPageSize(buffer), 0);
     589        4112 :         action = BLK_NEEDS_REDO;
     590             :     }
     591             :     else
     592      102616 :         action = XLogReadBufferForRedo(record, 0, &buffer);
     593      106728 :     if (action == BLK_NEEDS_REDO)
     594             :     {
     595             :         char       *tupdata;
     596             :         char       *endptr;
     597             :         Size        len;
     598             : 
     599             :         /* Tuples are stored as block data */
     600      104778 :         tupdata = XLogRecGetBlockData(record, 0, &len);
     601      104778 :         endptr = tupdata + len;
     602             : 
     603      104778 :         page = (Page) BufferGetPage(buffer);
     604             : 
     605      505984 :         for (i = 0; i < xlrec->ntuples; i++)
     606             :         {
     607             :             OffsetNumber offnum;
     608             :             xl_multi_insert_tuple *xlhdr;
     609             : 
     610             :             /*
     611             :              * If we're reinitializing the page, the tuples are stored in
     612             :              * order from FirstOffsetNumber. Otherwise there's an array of
     613             :              * offsets in the WAL record, and the tuples come after that.
     614             :              */
     615      401206 :             if (isinit)
     616      200080 :                 offnum = FirstOffsetNumber + i;
     617             :             else
     618      201126 :                 offnum = xlrec->offsets[i];
     619      401206 :             if (PageGetMaxOffsetNumber(page) + 1 < offnum)
     620           0 :                 elog(PANIC, "invalid max offset number");
     621             : 
     622      401206 :             xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(tupdata);
     623      401206 :             tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
     624             : 
     625      401206 :             newlen = xlhdr->datalen;
     626             :             Assert(newlen <= MaxHeapTupleSize);
     627      401206 :             htup = &tbuf.hdr;
     628      401206 :             MemSet((char *) htup, 0, SizeofHeapTupleHeader);
     629             :             /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
     630      401206 :             memcpy((char *) htup + SizeofHeapTupleHeader,
     631             :                    (char *) tupdata,
     632             :                    newlen);
     633      401206 :             tupdata += newlen;
     634             : 
     635      401206 :             newlen += SizeofHeapTupleHeader;
     636      401206 :             htup->t_infomask2 = xlhdr->t_infomask2;
     637      401206 :             htup->t_infomask = xlhdr->t_infomask;
     638      401206 :             htup->t_hoff = xlhdr->t_hoff;
     639      401206 :             HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     640      401206 :             HeapTupleHeaderSetCmin(htup, FirstCommandId);
     641      401206 :             ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
     642      401206 :             ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
     643             : 
     644      401206 :             offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
     645      401206 :             if (offnum == InvalidOffsetNumber)
     646           0 :                 elog(PANIC, "failed to add tuple");
     647             :         }
     648      104778 :         if (tupdata != endptr)
     649           0 :             elog(PANIC, "total tuple length mismatch");
     650             : 
     651      104778 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     652             : 
     653      104778 :         PageSetLSN(page, lsn);
     654             : 
     655      104778 :         if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     656         164 :             PageClearAllVisible(page);
     657             : 
     658             :         /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
     659      104778 :         if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
     660           8 :             PageSetAllVisible(page);
     661             : 
     662      104778 :         MarkBufferDirty(buffer);
     663             :     }
     664      106728 :     if (BufferIsValid(buffer))
     665      106728 :         UnlockReleaseBuffer(buffer);
     666             : 
     667             :     /*
     668             :      * If the page is running low on free space, update the FSM as well.
     669             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     670             :      * better than that without knowing the fill-factor for the table.
     671             :      *
     672             :      * XXX: Don't do this if the page was restored from full page image. We
     673             :      * don't bother to update the FSM in that case, it doesn't need to be
     674             :      * totally accurate anyway.
     675             :      */
     676      106728 :     if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
     677       26698 :         XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
     678      106728 : }
     679             : 
     680             : /*
     681             :  * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
     682             :  */
     683             : static void
     684      183374 : heap_xlog_update(XLogReaderState *record, bool hot_update)
     685             : {
     686      183374 :     XLogRecPtr  lsn = record->EndRecPtr;
     687      183374 :     xl_heap_update *xlrec = (xl_heap_update *) XLogRecGetData(record);
     688             :     RelFileLocator rlocator;
     689             :     BlockNumber oldblk;
     690             :     BlockNumber newblk;
     691             :     ItemPointerData newtid;
     692             :     Buffer      obuffer,
     693             :                 nbuffer;
     694             :     Page        page;
     695             :     OffsetNumber offnum;
     696      183374 :     ItemId      lp = NULL;
     697             :     HeapTupleData oldtup;
     698             :     HeapTupleHeader htup;
     699      183374 :     uint16      prefixlen = 0,
     700      183374 :                 suffixlen = 0;
     701             :     char       *newp;
     702             :     union
     703             :     {
     704             :         HeapTupleHeaderData hdr;
     705             :         char        data[MaxHeapTupleSize];
     706             :     }           tbuf;
     707             :     xl_heap_header xlhdr;
     708             :     uint32      newlen;
     709      183374 :     Size        freespace = 0;
     710             :     XLogRedoAction oldaction;
     711             :     XLogRedoAction newaction;
     712             : 
     713             :     /* initialize to keep the compiler quiet */
     714      183374 :     oldtup.t_data = NULL;
     715      183374 :     oldtup.t_len = 0;
     716             : 
     717      183374 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &newblk);
     718      183374 :     if (XLogRecGetBlockTagExtended(record, 1, NULL, NULL, &oldblk, NULL))
     719             :     {
     720             :         /* HOT updates are never done across pages */
     721             :         Assert(!hot_update);
     722             :     }
     723             :     else
     724       76254 :         oldblk = newblk;
     725             : 
     726      183374 :     ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
     727             : 
     728             :     /*
     729             :      * The visibility map may need to be fixed even if the heap page is
     730             :      * already up-to-date.
     731             :      */
     732      183374 :     if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
     733             :     {
     734         340 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     735         340 :         Buffer      vmbuffer = InvalidBuffer;
     736             : 
     737         340 :         visibilitymap_pin(reln, oldblk, &vmbuffer);
     738         340 :         visibilitymap_clear(reln, oldblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
     739         340 :         ReleaseBuffer(vmbuffer);
     740         340 :         FreeFakeRelcacheEntry(reln);
     741             :     }
     742             : 
     743             :     /*
     744             :      * In normal operation, it is important to lock the two pages in
     745             :      * page-number order, to avoid possible deadlocks against other update
     746             :      * operations going the other way.  However, during WAL replay there can
     747             :      * be no other update happening, so we don't need to worry about that. But
     748             :      * we *do* need to worry that we don't expose an inconsistent state to Hot
     749             :      * Standby queries --- so the original page can't be unlocked before we've
     750             :      * added the new tuple to the new page.
     751             :      */
     752             : 
     753             :     /* Deal with old tuple version */
     754      183374 :     oldaction = XLogReadBufferForRedo(record, (oldblk == newblk) ? 0 : 1,
     755             :                                       &obuffer);
     756      183374 :     if (oldaction == BLK_NEEDS_REDO)
     757             :     {
     758      182868 :         page = BufferGetPage(obuffer);
     759      182868 :         offnum = xlrec->old_offnum;
     760      182868 :         if (PageGetMaxOffsetNumber(page) >= offnum)
     761      182868 :             lp = PageGetItemId(page, offnum);
     762             : 
     763      182868 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
     764           0 :             elog(PANIC, "invalid lp");
     765             : 
     766      182868 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     767             : 
     768      182868 :         oldtup.t_data = htup;
     769      182868 :         oldtup.t_len = ItemIdGetLength(lp);
     770             : 
     771      182868 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
     772      182868 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
     773      182868 :         if (hot_update)
     774       70132 :             HeapTupleHeaderSetHotUpdated(htup);
     775             :         else
     776      112736 :             HeapTupleHeaderClearHotUpdated(htup);
     777      182868 :         fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
     778             :                                    &htup->t_infomask2);
     779      182868 :         HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
     780      182868 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
     781             :         /* Set forward chain link in t_ctid */
     782      182868 :         htup->t_ctid = newtid;
     783             : 
     784             :         /* Mark the page as a candidate for pruning */
     785      182868 :         PageSetPrunable(page, XLogRecGetXid(record));
     786             : 
     787      182868 :         if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
     788         332 :             PageClearAllVisible(page);
     789             : 
     790      182868 :         PageSetLSN(page, lsn);
     791      182868 :         MarkBufferDirty(obuffer);
     792             :     }
     793             : 
     794             :     /*
     795             :      * Read the page the new tuple goes into, if different from old.
     796             :      */
     797      183374 :     if (oldblk == newblk)
     798             :     {
     799       76254 :         nbuffer = obuffer;
     800       76254 :         newaction = oldaction;
     801             :     }
     802      107120 :     else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
     803             :     {
     804        1210 :         nbuffer = XLogInitBufferForRedo(record, 0);
     805        1210 :         page = (Page) BufferGetPage(nbuffer);
     806        1210 :         PageInit(page, BufferGetPageSize(nbuffer), 0);
     807        1210 :         newaction = BLK_NEEDS_REDO;
     808             :     }
     809             :     else
     810      105910 :         newaction = XLogReadBufferForRedo(record, 0, &nbuffer);
     811             : 
     812             :     /*
     813             :      * The visibility map may need to be fixed even if the heap page is
     814             :      * already up-to-date.
     815             :      */
     816      183374 :     if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
     817             :     {
     818         130 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     819         130 :         Buffer      vmbuffer = InvalidBuffer;
     820             : 
     821         130 :         visibilitymap_pin(reln, newblk, &vmbuffer);
     822         130 :         visibilitymap_clear(reln, newblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
     823         130 :         ReleaseBuffer(vmbuffer);
     824         130 :         FreeFakeRelcacheEntry(reln);
     825             :     }
     826             : 
     827             :     /* Deal with new tuple */
     828      183374 :     if (newaction == BLK_NEEDS_REDO)
     829             :     {
     830             :         char       *recdata;
     831             :         char       *recdata_end;
     832             :         Size        datalen;
     833             :         Size        tuplen;
     834             : 
     835      182656 :         recdata = XLogRecGetBlockData(record, 0, &datalen);
     836      182656 :         recdata_end = recdata + datalen;
     837             : 
     838      182656 :         page = BufferGetPage(nbuffer);
     839             : 
     840      182656 :         offnum = xlrec->new_offnum;
     841      182656 :         if (PageGetMaxOffsetNumber(page) + 1 < offnum)
     842           0 :             elog(PANIC, "invalid max offset number");
     843             : 
     844      182656 :         if (xlrec->flags & XLH_UPDATE_PREFIX_FROM_OLD)
     845             :         {
     846             :             Assert(newblk == oldblk);
     847       28604 :             memcpy(&prefixlen, recdata, sizeof(uint16));
     848       28604 :             recdata += sizeof(uint16);
     849             :         }
     850      182656 :         if (xlrec->flags & XLH_UPDATE_SUFFIX_FROM_OLD)
     851             :         {
     852             :             Assert(newblk == oldblk);
     853       65630 :             memcpy(&suffixlen, recdata, sizeof(uint16));
     854       65630 :             recdata += sizeof(uint16);
     855             :         }
     856             : 
     857      182656 :         memcpy((char *) &xlhdr, recdata, SizeOfHeapHeader);
     858      182656 :         recdata += SizeOfHeapHeader;
     859             : 
     860      182656 :         tuplen = recdata_end - recdata;
     861             :         Assert(tuplen <= MaxHeapTupleSize);
     862             : 
     863      182656 :         htup = &tbuf.hdr;
     864      182656 :         MemSet((char *) htup, 0, SizeofHeapTupleHeader);
     865             : 
     866             :         /*
     867             :          * Reconstruct the new tuple using the prefix and/or suffix from the
     868             :          * old tuple, and the data stored in the WAL record.
     869             :          */
     870      182656 :         newp = (char *) htup + SizeofHeapTupleHeader;
     871      182656 :         if (prefixlen > 0)
     872             :         {
     873             :             int         len;
     874             : 
     875             :             /* copy bitmap [+ padding] [+ oid] from WAL record */
     876       28604 :             len = xlhdr.t_hoff - SizeofHeapTupleHeader;
     877       28604 :             memcpy(newp, recdata, len);
     878       28604 :             recdata += len;
     879       28604 :             newp += len;
     880             : 
     881             :             /* copy prefix from old tuple */
     882       28604 :             memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
     883       28604 :             newp += prefixlen;
     884             : 
     885             :             /* copy new tuple data from WAL record */
     886       28604 :             len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
     887       28604 :             memcpy(newp, recdata, len);
     888       28604 :             recdata += len;
     889       28604 :             newp += len;
     890             :         }
     891             :         else
     892             :         {
     893             :             /*
     894             :              * copy bitmap [+ padding] [+ oid] + data from record, all in one
     895             :              * go
     896             :              */
     897      154052 :             memcpy(newp, recdata, tuplen);
     898      154052 :             recdata += tuplen;
     899      154052 :             newp += tuplen;
     900             :         }
     901             :         Assert(recdata == recdata_end);
     902             : 
     903             :         /* copy suffix from old tuple */
     904      182656 :         if (suffixlen > 0)
     905       65630 :             memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
     906             : 
     907      182656 :         newlen = SizeofHeapTupleHeader + tuplen + prefixlen + suffixlen;
     908      182656 :         htup->t_infomask2 = xlhdr.t_infomask2;
     909      182656 :         htup->t_infomask = xlhdr.t_infomask;
     910      182656 :         htup->t_hoff = xlhdr.t_hoff;
     911             : 
     912      182656 :         HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     913      182656 :         HeapTupleHeaderSetCmin(htup, FirstCommandId);
     914      182656 :         HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
     915             :         /* Make sure there is no forward chain link in t_ctid */
     916      182656 :         htup->t_ctid = newtid;
     917             : 
     918      182656 :         offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
     919      182656 :         if (offnum == InvalidOffsetNumber)
     920           0 :             elog(PANIC, "failed to add tuple");
     921             : 
     922      182656 :         if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
     923          56 :             PageClearAllVisible(page);
     924             : 
     925      182656 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     926             : 
     927      182656 :         PageSetLSN(page, lsn);
     928      182656 :         MarkBufferDirty(nbuffer);
     929             :     }
     930             : 
     931      183374 :     if (BufferIsValid(nbuffer) && nbuffer != obuffer)
     932      107120 :         UnlockReleaseBuffer(nbuffer);
     933      183374 :     if (BufferIsValid(obuffer))
     934      183374 :         UnlockReleaseBuffer(obuffer);
     935             : 
     936             :     /*
     937             :      * If the new page is running low on free space, update the FSM as well.
     938             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     939             :      * better than that without knowing the fill-factor for the table.
     940             :      *
     941             :      * However, don't update the FSM on HOT updates, because after crash
     942             :      * recovery, either the old or the new tuple will certainly be dead and
     943             :      * prunable. After pruning, the page will have roughly as much free space
     944             :      * as it did before the update, assuming the new tuple is about the same
     945             :      * size as the old one.
     946             :      *
     947             :      * XXX: Don't do this if the page was restored from full page image. We
     948             :      * don't bother to update the FSM in that case, it doesn't need to be
     949             :      * totally accurate anyway.
     950             :      */
     951      183374 :     if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
     952       22744 :         XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
     953      183374 : }
     954             : 
     955             : /*
     956             :  * Replay XLOG_HEAP_CONFIRM records.
     957             :  */
     958             : static void
     959         154 : heap_xlog_confirm(XLogReaderState *record)
     960             : {
     961         154 :     XLogRecPtr  lsn = record->EndRecPtr;
     962         154 :     xl_heap_confirm *xlrec = (xl_heap_confirm *) XLogRecGetData(record);
     963             :     Buffer      buffer;
     964             :     Page        page;
     965             :     OffsetNumber offnum;
     966         154 :     ItemId      lp = NULL;
     967             :     HeapTupleHeader htup;
     968             : 
     969         154 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     970             :     {
     971         154 :         page = BufferGetPage(buffer);
     972             : 
     973         154 :         offnum = xlrec->offnum;
     974         154 :         if (PageGetMaxOffsetNumber(page) >= offnum)
     975         154 :             lp = PageGetItemId(page, offnum);
     976             : 
     977         154 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
     978           0 :             elog(PANIC, "invalid lp");
     979             : 
     980         154 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     981             : 
     982             :         /*
     983             :          * Confirm tuple as actually inserted
     984             :          */
     985         154 :         ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
     986             : 
     987         154 :         PageSetLSN(page, lsn);
     988         154 :         MarkBufferDirty(buffer);
     989             :     }
     990         154 :     if (BufferIsValid(buffer))
     991         154 :         UnlockReleaseBuffer(buffer);
     992         154 : }
     993             : 
     994             : /*
     995             :  * Replay XLOG_HEAP_LOCK records.
     996             :  */
     997             : static void
     998      108920 : heap_xlog_lock(XLogReaderState *record)
     999             : {
    1000      108920 :     XLogRecPtr  lsn = record->EndRecPtr;
    1001      108920 :     xl_heap_lock *xlrec = (xl_heap_lock *) XLogRecGetData(record);
    1002             :     Buffer      buffer;
    1003             :     Page        page;
    1004             :     OffsetNumber offnum;
    1005      108920 :     ItemId      lp = NULL;
    1006             :     HeapTupleHeader htup;
    1007             : 
    1008             :     /*
    1009             :      * The visibility map may need to be fixed even if the heap page is
    1010             :      * already up-to-date.
    1011             :      */
    1012      108920 :     if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
    1013             :     {
    1014             :         RelFileLocator rlocator;
    1015          54 :         Buffer      vmbuffer = InvalidBuffer;
    1016             :         BlockNumber block;
    1017             :         Relation    reln;
    1018             : 
    1019          54 :         XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
    1020          54 :         reln = CreateFakeRelcacheEntry(rlocator);
    1021             : 
    1022          54 :         visibilitymap_pin(reln, block, &vmbuffer);
    1023          54 :         visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
    1024             : 
    1025          54 :         ReleaseBuffer(vmbuffer);
    1026          54 :         FreeFakeRelcacheEntry(reln);
    1027             :     }
    1028             : 
    1029      108920 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1030             :     {
    1031      108608 :         page = (Page) BufferGetPage(buffer);
    1032             : 
    1033      108608 :         offnum = xlrec->offnum;
    1034      108608 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1035      108608 :             lp = PageGetItemId(page, offnum);
    1036             : 
    1037      108608 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1038           0 :             elog(PANIC, "invalid lp");
    1039             : 
    1040      108608 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1041             : 
    1042      108608 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
    1043      108608 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
    1044      108608 :         fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
    1045             :                                    &htup->t_infomask2);
    1046             : 
    1047             :         /*
    1048             :          * Clear relevant update flags, but only if the modified infomask says
    1049             :          * there's no update.
    1050             :          */
    1051      108608 :         if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask))
    1052             :         {
    1053      108608 :             HeapTupleHeaderClearHotUpdated(htup);
    1054             :             /* Make sure there is no forward chain link in t_ctid */
    1055      108608 :             ItemPointerSet(&htup->t_ctid,
    1056             :                            BufferGetBlockNumber(buffer),
    1057             :                            offnum);
    1058             :         }
    1059      108608 :         HeapTupleHeaderSetXmax(htup, xlrec->xmax);
    1060      108608 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
    1061      108608 :         PageSetLSN(page, lsn);
    1062      108608 :         MarkBufferDirty(buffer);
    1063             :     }
    1064      108920 :     if (BufferIsValid(buffer))
    1065      108920 :         UnlockReleaseBuffer(buffer);
    1066      108920 : }
    1067             : 
    1068             : /*
    1069             :  * Replay XLOG_HEAP2_LOCK_UPDATED records.
    1070             :  */
    1071             : static void
    1072           0 : heap_xlog_lock_updated(XLogReaderState *record)
    1073             : {
    1074           0 :     XLogRecPtr  lsn = record->EndRecPtr;
    1075             :     xl_heap_lock_updated *xlrec;
    1076             :     Buffer      buffer;
    1077             :     Page        page;
    1078             :     OffsetNumber offnum;
    1079           0 :     ItemId      lp = NULL;
    1080             :     HeapTupleHeader htup;
    1081             : 
    1082           0 :     xlrec = (xl_heap_lock_updated *) XLogRecGetData(record);
    1083             : 
    1084             :     /*
    1085             :      * The visibility map may need to be fixed even if the heap page is
    1086             :      * already up-to-date.
    1087             :      */
    1088           0 :     if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
    1089             :     {
    1090             :         RelFileLocator rlocator;
    1091           0 :         Buffer      vmbuffer = InvalidBuffer;
    1092             :         BlockNumber block;
    1093             :         Relation    reln;
    1094             : 
    1095           0 :         XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
    1096           0 :         reln = CreateFakeRelcacheEntry(rlocator);
    1097             : 
    1098           0 :         visibilitymap_pin(reln, block, &vmbuffer);
    1099           0 :         visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
    1100             : 
    1101           0 :         ReleaseBuffer(vmbuffer);
    1102           0 :         FreeFakeRelcacheEntry(reln);
    1103             :     }
    1104             : 
    1105           0 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1106             :     {
    1107           0 :         page = BufferGetPage(buffer);
    1108             : 
    1109           0 :         offnum = xlrec->offnum;
    1110           0 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1111           0 :             lp = PageGetItemId(page, offnum);
    1112             : 
    1113           0 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1114           0 :             elog(PANIC, "invalid lp");
    1115             : 
    1116           0 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1117             : 
    1118           0 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
    1119           0 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
    1120           0 :         fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
    1121             :                                    &htup->t_infomask2);
    1122           0 :         HeapTupleHeaderSetXmax(htup, xlrec->xmax);
    1123             : 
    1124           0 :         PageSetLSN(page, lsn);
    1125           0 :         MarkBufferDirty(buffer);
    1126             :     }
    1127           0 :     if (BufferIsValid(buffer))
    1128           0 :         UnlockReleaseBuffer(buffer);
    1129           0 : }
    1130             : 
    1131             : /*
    1132             :  * Replay XLOG_HEAP_INPLACE records.
    1133             :  */
    1134             : static void
    1135       13956 : heap_xlog_inplace(XLogReaderState *record)
    1136             : {
    1137       13956 :     XLogRecPtr  lsn = record->EndRecPtr;
    1138       13956 :     xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record);
    1139             :     Buffer      buffer;
    1140             :     Page        page;
    1141             :     OffsetNumber offnum;
    1142       13956 :     ItemId      lp = NULL;
    1143             :     HeapTupleHeader htup;
    1144             :     uint32      oldlen;
    1145             :     Size        newlen;
    1146             : 
    1147       13956 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1148             :     {
    1149       13668 :         char       *newtup = XLogRecGetBlockData(record, 0, &newlen);
    1150             : 
    1151       13668 :         page = BufferGetPage(buffer);
    1152             : 
    1153       13668 :         offnum = xlrec->offnum;
    1154       13668 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1155       13668 :             lp = PageGetItemId(page, offnum);
    1156             : 
    1157       13668 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1158           0 :             elog(PANIC, "invalid lp");
    1159             : 
    1160       13668 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1161             : 
    1162       13668 :         oldlen = ItemIdGetLength(lp) - htup->t_hoff;
    1163       13668 :         if (oldlen != newlen)
    1164           0 :             elog(PANIC, "wrong tuple length");
    1165             : 
    1166       13668 :         memcpy((char *) htup + htup->t_hoff, newtup, newlen);
    1167             : 
    1168       13668 :         PageSetLSN(page, lsn);
    1169       13668 :         MarkBufferDirty(buffer);
    1170             :     }
    1171       13956 :     if (BufferIsValid(buffer))
    1172       13956 :         UnlockReleaseBuffer(buffer);
    1173             : 
    1174       13956 :     ProcessCommittedInvalidationMessages(xlrec->msgs,
    1175             :                                          xlrec->nmsgs,
    1176       13956 :                                          xlrec->relcacheInitFileInval,
    1177             :                                          xlrec->dbId,
    1178             :                                          xlrec->tsId);
    1179       13956 : }
    1180             : 
    1181             : void
    1182     3385060 : heap_redo(XLogReaderState *record)
    1183             : {
    1184     3385060 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    1185             : 
    1186             :     /*
    1187             :      * These operations don't overwrite MVCC data so no conflict processing is
    1188             :      * required. The ones in heap2 rmgr do.
    1189             :      */
    1190             : 
    1191     3385060 :     switch (info & XLOG_HEAP_OPMASK)
    1192             :     {
    1193     2495992 :         case XLOG_HEAP_INSERT:
    1194     2495992 :             heap_xlog_insert(record);
    1195     2495992 :             break;
    1196      582660 :         case XLOG_HEAP_DELETE:
    1197      582660 :             heap_xlog_delete(record);
    1198      582660 :             break;
    1199      112792 :         case XLOG_HEAP_UPDATE:
    1200      112792 :             heap_xlog_update(record, false);
    1201      112792 :             break;
    1202           4 :         case XLOG_HEAP_TRUNCATE:
    1203             : 
    1204             :             /*
    1205             :              * TRUNCATE is a no-op because the actions are already logged as
    1206             :              * SMGR WAL records.  TRUNCATE WAL record only exists for logical
    1207             :              * decoding.
    1208             :              */
    1209           4 :             break;
    1210       70582 :         case XLOG_HEAP_HOT_UPDATE:
    1211       70582 :             heap_xlog_update(record, true);
    1212       70582 :             break;
    1213         154 :         case XLOG_HEAP_CONFIRM:
    1214         154 :             heap_xlog_confirm(record);
    1215         154 :             break;
    1216      108920 :         case XLOG_HEAP_LOCK:
    1217      108920 :             heap_xlog_lock(record);
    1218      108920 :             break;
    1219       13956 :         case XLOG_HEAP_INPLACE:
    1220       13956 :             heap_xlog_inplace(record);
    1221       13956 :             break;
    1222           0 :         default:
    1223           0 :             elog(PANIC, "heap_redo: unknown op code %u", info);
    1224             :     }
    1225     3385060 : }
    1226             : 
    1227             : void
    1228      133916 : heap2_redo(XLogReaderState *record)
    1229             : {
    1230      133916 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    1231             : 
    1232      133916 :     switch (info & XLOG_HEAP_OPMASK)
    1233             :     {
    1234       17814 :         case XLOG_HEAP2_PRUNE_ON_ACCESS:
    1235             :         case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
    1236             :         case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
    1237       17814 :             heap_xlog_prune_freeze(record);
    1238       17814 :             break;
    1239        7622 :         case XLOG_HEAP2_VISIBLE:
    1240        7622 :             heap_xlog_visible(record);
    1241        7622 :             break;
    1242      106728 :         case XLOG_HEAP2_MULTI_INSERT:
    1243      106728 :             heap_xlog_multi_insert(record);
    1244      106728 :             break;
    1245           0 :         case XLOG_HEAP2_LOCK_UPDATED:
    1246           0 :             heap_xlog_lock_updated(record);
    1247           0 :             break;
    1248        1752 :         case XLOG_HEAP2_NEW_CID:
    1249             : 
    1250             :             /*
    1251             :              * Nothing to do on a real replay, only used during logical
    1252             :              * decoding.
    1253             :              */
    1254        1752 :             break;
    1255           0 :         case XLOG_HEAP2_REWRITE:
    1256           0 :             heap_xlog_logical_rewrite(record);
    1257           0 :             break;
    1258           0 :         default:
    1259           0 :             elog(PANIC, "heap2_redo: unknown op code %u", info);
    1260             :     }
    1261      133916 : }
    1262             : 
    1263             : /*
    1264             :  * Mask a heap page before performing consistency checks on it.
    1265             :  */
    1266             : void
    1267     5593860 : heap_mask(char *pagedata, BlockNumber blkno)
    1268             : {
    1269     5593860 :     Page        page = (Page) pagedata;
    1270             :     OffsetNumber off;
    1271             : 
    1272     5593860 :     mask_page_lsn_and_checksum(page);
    1273             : 
    1274     5593860 :     mask_page_hint_bits(page);
    1275     5593860 :     mask_unused_space(page);
    1276             : 
    1277   458054072 :     for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
    1278             :     {
    1279   452460212 :         ItemId      iid = PageGetItemId(page, off);
    1280             :         char       *page_item;
    1281             : 
    1282   452460212 :         page_item = (char *) (page + ItemIdGetOffset(iid));
    1283             : 
    1284   452460212 :         if (ItemIdIsNormal(iid))
    1285             :         {
    1286   428691908 :             HeapTupleHeader page_htup = (HeapTupleHeader) page_item;
    1287             : 
    1288             :             /*
    1289             :              * If xmin of a tuple is not yet frozen, we should ignore
    1290             :              * differences in hint bits, since they can be set without
    1291             :              * emitting WAL.
    1292             :              */
    1293   428691908 :             if (!HeapTupleHeaderXminFrozen(page_htup))
    1294   417706776 :                 page_htup->t_infomask &= ~HEAP_XACT_MASK;
    1295             :             else
    1296             :             {
    1297             :                 /* Still we need to mask xmax hint bits. */
    1298    10985132 :                 page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
    1299    10985132 :                 page_htup->t_infomask &= ~HEAP_XMAX_COMMITTED;
    1300             :             }
    1301             : 
    1302             :             /*
    1303             :              * During replay, we set Command Id to FirstCommandId. Hence, mask
    1304             :              * it. See heap_xlog_insert() for details.
    1305             :              */
    1306   428691908 :             page_htup->t_choice.t_heap.t_field3.t_cid = MASK_MARKER;
    1307             : 
    1308             :             /*
    1309             :              * For a speculative tuple, heap_insert() does not set ctid in the
    1310             :              * caller-passed heap tuple itself, leaving the ctid field to
    1311             :              * contain a speculative token value - a per-backend monotonically
    1312             :              * increasing identifier. Besides, it does not WAL-log ctid under
    1313             :              * any circumstances.
    1314             :              *
    1315             :              * During redo, heap_xlog_insert() sets t_ctid to current block
    1316             :              * number and self offset number. It doesn't care about any
    1317             :              * speculative insertions on the primary. Hence, we set t_ctid to
    1318             :              * current block number and self offset number to ignore any
    1319             :              * inconsistency.
    1320             :              */
    1321   428691908 :             if (HeapTupleHeaderIsSpeculative(page_htup))
    1322         156 :                 ItemPointerSet(&page_htup->t_ctid, blkno, off);
    1323             : 
    1324             :             /*
    1325             :              * NB: Not ignoring ctid changes due to the tuple having moved
    1326             :              * (i.e. HeapTupleHeaderIndicatesMovedPartitions), because that's
    1327             :              * important information that needs to be in-sync between primary
    1328             :              * and standby, and thus is WAL logged.
    1329             :              */
    1330             :         }
    1331             : 
    1332             :         /*
    1333             :          * Ignore any padding bytes after the tuple, when the length of the
    1334             :          * item is not MAXALIGNed.
    1335             :          */
    1336   452460212 :         if (ItemIdHasStorage(iid))
    1337             :         {
    1338   428691908 :             int         len = ItemIdGetLength(iid);
    1339   428691908 :             int         padlen = MAXALIGN(len) - len;
    1340             : 
    1341   428691908 :             if (padlen > 0)
    1342   216400756 :                 memset(page_item + len, MASK_MARKER, padlen);
    1343             :         }
    1344             :     }
    1345     5593860 : }

Generated by: LCOV version 1.14