LCOV - code coverage report
Current view: top level - src/backend/access/heap - heapam_xlog.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 484 538 90.0 %
Date: 2025-07-07 21:17:16 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-2025, 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       25280 : heap_xlog_prune_freeze(XLogReaderState *record)
      31             : {
      32       25280 :     XLogRecPtr  lsn = record->EndRecPtr;
      33       25280 :     char       *maindataptr = XLogRecGetData(record);
      34             :     xl_heap_prune xlrec;
      35             :     Buffer      buffer;
      36             :     RelFileLocator rlocator;
      37             :     BlockNumber blkno;
      38             :     XLogRedoAction action;
      39             : 
      40       25280 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
      41       25280 :     memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
      42       25280 :     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       25280 :     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       19010 :         memcpy(&snapshot_conflict_horizon, maindataptr, sizeof(TransactionId));
      65       19010 :         maindataptr += sizeof(TransactionId);
      66             : 
      67       19010 :         if (InHotStandby)
      68       18532 :             ResolveRecoveryConflictWithSnapshot(snapshot_conflict_horizon,
      69       18532 :                                                 (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       25280 :     action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL,
      77       25280 :                                            (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
      78             :                                            &buffer);
      79       25280 :     if (action == BLK_NEEDS_REDO)
      80             :     {
      81       17308 :         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       17308 :         char       *dataptr = XLogRecGetBlockData(record, 0, &datalen);
      93             : 
      94       17308 :         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       17308 :         if (nredirected > 0 || ndead > 0 || nunused > 0)
     105       16252 :             heap_page_prune_execute(buffer,
     106       16252 :                                     (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
     107             :                                     redirected, nredirected,
     108             :                                     nowdead, ndead,
     109             :                                     nowunused, nunused);
     110             : 
     111             :         /* Freeze tuples */
     112       19328 :         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        2020 :             frz.xmax = plans[p].xmax;
     121        2020 :             frz.t_infomask2 = plans[p].t_infomask2;
     122        2020 :             frz.t_infomask = plans[p].t_infomask;
     123        2020 :             frz.frzflags = plans[p].frzflags;
     124        2020 :             frz.offset = InvalidOffsetNumber;   /* unused, but be tidy */
     125             : 
     126       97902 :             for (int i = 0; i < plans[p].ntuples; i++)
     127             :             {
     128       95882 :                 OffsetNumber offset = *(frz_offsets++);
     129             :                 ItemId      lp;
     130             :                 HeapTupleHeader tuple;
     131             : 
     132       95882 :                 lp = PageGetItemId(page, offset);
     133       95882 :                 tuple = (HeapTupleHeader) PageGetItem(page, lp);
     134       95882 :                 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       17308 :         PageSetLSN(page, lsn);
     147       17308 :         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       25280 :     if (BufferIsValid(buffer))
     157             :     {
     158       25280 :         if (xlrec.flags & (XLHP_HAS_REDIRECTIONS |
     159             :                            XLHP_HAS_DEAD_ITEMS |
     160             :                            XLHP_HAS_NOW_UNUSED_ITEMS))
     161             :         {
     162       21672 :             Size        freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
     163             : 
     164       21672 :             UnlockReleaseBuffer(buffer);
     165             : 
     166       21672 :             XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
     167             :         }
     168             :         else
     169        3608 :             UnlockReleaseBuffer(buffer);
     170             :     }
     171       25280 : }
     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       14610 : heap_xlog_visible(XLogReaderState *record)
     183             : {
     184       14610 :     XLogRecPtr  lsn = record->EndRecPtr;
     185       14610 :     xl_heap_visible *xlrec = (xl_heap_visible *) XLogRecGetData(record);
     186       14610 :     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       14610 :     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       14610 :     if (InHotStandby)
     207       14262 :         ResolveRecoveryConflictWithSnapshot(xlrec->snapshotConflictHorizon,
     208       14262 :                                             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       14610 :     action = XLogReadBufferForRedo(record, 1, &buffer);
     217       14610 :     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       10950 :         page = BufferGetPage(buffer);
     227             : 
     228       10950 :         PageSetAllVisible(page);
     229             : 
     230       10950 :         if (XLogHintBitIsNeeded())
     231       10950 :             PageSetLSN(page, lsn);
     232             : 
     233       10950 :         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       14610 :     if (BufferIsValid(buffer))
     245             :     {
     246       14610 :         Size        space = PageGetFreeSpace(BufferGetPage(buffer));
     247             : 
     248       14610 :         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       14610 :         if (xlrec->flags & VISIBILITYMAP_VALID_BITS)
     268       14610 :             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       14610 :     if (XLogReadBufferForRedoExtended(record, 0, RBM_ZERO_ON_ERROR, false,
     278             :                                       &vmbuffer) == BLK_NEEDS_REDO)
     279             :     {
     280       14072 :         Page        vmpage = BufferGetPage(vmbuffer);
     281             :         Relation    reln;
     282             :         uint8       vmbits;
     283             : 
     284             :         /* initialize the page if it was read as zeros */
     285       14072 :         if (PageIsNew(vmpage))
     286           0 :             PageInit(vmpage, BLCKSZ, 0);
     287             : 
     288             :         /* remove VISIBILITYMAP_XLOG_* */
     289       14072 :         vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
     290             : 
     291             :         /*
     292             :          * XLogReadBufferForRedoExtended locked the buffer. But
     293             :          * visibilitymap_set will handle locking itself.
     294             :          */
     295       14072 :         LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
     296             : 
     297       14072 :         reln = CreateFakeRelcacheEntry(rlocator);
     298       14072 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     299             : 
     300       14072 :         visibilitymap_set(reln, blkno, InvalidBuffer, lsn, vmbuffer,
     301             :                           xlrec->snapshotConflictHorizon, vmbits);
     302             : 
     303       14072 :         ReleaseBuffer(vmbuffer);
     304       14072 :         FreeFakeRelcacheEntry(reln);
     305             :     }
     306         538 :     else if (BufferIsValid(vmbuffer))
     307         538 :         UnlockReleaseBuffer(vmbuffer);
     308       14610 : }
     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      889968 : fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
     318             : {
     319      889968 :     *infomask &= ~(HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY |
     320             :                    HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK);
     321      889968 :     *infomask2 &= ~HEAP_KEYS_UPDATED;
     322             : 
     323      889968 :     if (infobits & XLHL_XMAX_IS_MULTI)
     324           4 :         *infomask |= HEAP_XMAX_IS_MULTI;
     325      889968 :     if (infobits & XLHL_XMAX_LOCK_ONLY)
     326      110004 :         *infomask |= HEAP_XMAX_LOCK_ONLY;
     327      889968 :     if (infobits & XLHL_XMAX_EXCL_LOCK)
     328      109206 :         *infomask |= HEAP_XMAX_EXCL_LOCK;
     329             :     /* note HEAP_XMAX_SHR_LOCK isn't considered here */
     330      889968 :     if (infobits & XLHL_XMAX_KEYSHR_LOCK)
     331         820 :         *infomask |= HEAP_XMAX_KEYSHR_LOCK;
     332             : 
     333      889968 :     if (infobits & XLHL_KEYS_UPDATED)
     334      596754 :         *infomask2 |= HEAP_KEYS_UPDATED;
     335      889968 : }
     336             : 
     337             : /*
     338             :  * Replay XLOG_HEAP_DELETE records.
     339             :  */
     340             : static void
     341      597944 : heap_xlog_delete(XLogReaderState *record)
     342             : {
     343      597944 :     XLogRecPtr  lsn = record->EndRecPtr;
     344      597944 :     xl_heap_delete *xlrec = (xl_heap_delete *) XLogRecGetData(record);
     345             :     Buffer      buffer;
     346             :     Page        page;
     347      597944 :     ItemId      lp = NULL;
     348             :     HeapTupleHeader htup;
     349             :     BlockNumber blkno;
     350             :     RelFileLocator target_locator;
     351             :     ItemPointerData target_tid;
     352             : 
     353      597944 :     XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
     354      597944 :     ItemPointerSetBlockNumber(&target_tid, blkno);
     355      597944 :     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      597944 :     if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
     362             :     {
     363          52 :         Relation    reln = CreateFakeRelcacheEntry(target_locator);
     364          52 :         Buffer      vmbuffer = InvalidBuffer;
     365             : 
     366          52 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     367          52 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     368          52 :         ReleaseBuffer(vmbuffer);
     369          52 :         FreeFakeRelcacheEntry(reln);
     370             :     }
     371             : 
     372      597944 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     373             :     {
     374      593988 :         page = BufferGetPage(buffer);
     375             : 
     376      593988 :         if (PageGetMaxOffsetNumber(page) >= xlrec->offnum)
     377      593988 :             lp = PageGetItemId(page, xlrec->offnum);
     378             : 
     379      593988 :         if (PageGetMaxOffsetNumber(page) < xlrec->offnum || !ItemIdIsNormal(lp))
     380           0 :             elog(PANIC, "invalid lp");
     381             : 
     382      593988 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     383             : 
     384      593988 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
     385      593988 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
     386      593988 :         HeapTupleHeaderClearHotUpdated(htup);
     387      593988 :         fix_infomask_from_infobits(xlrec->infobits_set,
     388             :                                    &htup->t_infomask, &htup->t_infomask2);
     389      593988 :         if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
     390      593988 :             HeapTupleHeaderSetXmax(htup, xlrec->xmax);
     391             :         else
     392           0 :             HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
     393      593988 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
     394             : 
     395             :         /* Mark the page as a candidate for pruning */
     396      593988 :         PageSetPrunable(page, XLogRecGetXid(record));
     397             : 
     398      593988 :         if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
     399          28 :             PageClearAllVisible(page);
     400             : 
     401             :         /* Make sure t_ctid is set correctly */
     402      593988 :         if (xlrec->flags & XLH_DELETE_IS_PARTITION_MOVE)
     403         288 :             HeapTupleHeaderSetMovedPartitions(htup);
     404             :         else
     405      593700 :             htup->t_ctid = target_tid;
     406      593988 :         PageSetLSN(page, lsn);
     407      593988 :         MarkBufferDirty(buffer);
     408             :     }
     409      597944 :     if (BufferIsValid(buffer))
     410      597944 :         UnlockReleaseBuffer(buffer);
     411      597944 : }
     412             : 
     413             : /*
     414             :  * Replay XLOG_HEAP_INSERT records.
     415             :  */
     416             : static void
     417     2562502 : heap_xlog_insert(XLogReaderState *record)
     418             : {
     419     2562502 :     XLogRecPtr  lsn = record->EndRecPtr;
     420     2562502 :     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     2562502 :     Size        freespace = 0;
     432             :     RelFileLocator target_locator;
     433             :     BlockNumber blkno;
     434             :     ItemPointerData target_tid;
     435             :     XLogRedoAction action;
     436             : 
     437     2562502 :     XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
     438     2562502 :     ItemPointerSetBlockNumber(&target_tid, blkno);
     439     2562502 :     ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
     440             : 
     441             :     /* No freezing in the heap_insert() code path */
     442             :     Assert(!(xlrec->flags & XLH_INSERT_ALL_FROZEN_SET));
     443             : 
     444             :     /*
     445             :      * The visibility map may need to be fixed even if the heap page is
     446             :      * already up-to-date.
     447             :      */
     448     2562502 :     if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     449             :     {
     450        1730 :         Relation    reln = CreateFakeRelcacheEntry(target_locator);
     451        1730 :         Buffer      vmbuffer = InvalidBuffer;
     452             : 
     453        1730 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     454        1730 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     455        1730 :         ReleaseBuffer(vmbuffer);
     456        1730 :         FreeFakeRelcacheEntry(reln);
     457             :     }
     458             : 
     459             :     /*
     460             :      * If we inserted the first and only tuple on the page, re-initialize the
     461             :      * page from scratch.
     462             :      */
     463     2562502 :     if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
     464             :     {
     465       34156 :         buffer = XLogInitBufferForRedo(record, 0);
     466       34156 :         page = BufferGetPage(buffer);
     467       34156 :         PageInit(page, BufferGetPageSize(buffer), 0);
     468       34156 :         action = BLK_NEEDS_REDO;
     469             :     }
     470             :     else
     471     2528346 :         action = XLogReadBufferForRedo(record, 0, &buffer);
     472     2562502 :     if (action == BLK_NEEDS_REDO)
     473             :     {
     474             :         Size        datalen;
     475             :         char       *data;
     476             : 
     477     2557134 :         page = BufferGetPage(buffer);
     478             : 
     479     2557134 :         if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
     480           0 :             elog(PANIC, "invalid max offset number");
     481             : 
     482     2557134 :         data = XLogRecGetBlockData(record, 0, &datalen);
     483             : 
     484     2557134 :         newlen = datalen - SizeOfHeapHeader;
     485             :         Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
     486     2557134 :         memcpy(&xlhdr, data, SizeOfHeapHeader);
     487     2557134 :         data += SizeOfHeapHeader;
     488             : 
     489     2557134 :         htup = &tbuf.hdr;
     490     2557134 :         MemSet(htup, 0, SizeofHeapTupleHeader);
     491             :         /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
     492     2557134 :         memcpy((char *) htup + SizeofHeapTupleHeader,
     493             :                data,
     494             :                newlen);
     495     2557134 :         newlen += SizeofHeapTupleHeader;
     496     2557134 :         htup->t_infomask2 = xlhdr.t_infomask2;
     497     2557134 :         htup->t_infomask = xlhdr.t_infomask;
     498     2557134 :         htup->t_hoff = xlhdr.t_hoff;
     499     2557134 :         HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     500     2557134 :         HeapTupleHeaderSetCmin(htup, FirstCommandId);
     501     2557134 :         htup->t_ctid = target_tid;
     502             : 
     503     2557134 :         if (PageAddItem(page, (Item) htup, newlen, xlrec->offnum,
     504             :                         true, true) == InvalidOffsetNumber)
     505           0 :             elog(PANIC, "failed to add tuple");
     506             : 
     507     2557134 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     508             : 
     509     2557134 :         PageSetLSN(page, lsn);
     510             : 
     511     2557134 :         if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     512         524 :             PageClearAllVisible(page);
     513             : 
     514     2557134 :         MarkBufferDirty(buffer);
     515             :     }
     516     2562502 :     if (BufferIsValid(buffer))
     517     2562502 :         UnlockReleaseBuffer(buffer);
     518             : 
     519             :     /*
     520             :      * If the page is running low on free space, update the FSM as well.
     521             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     522             :      * better than that without knowing the fill-factor for the table.
     523             :      *
     524             :      * XXX: Don't do this if the page was restored from full page image. We
     525             :      * don't bother to update the FSM in that case, it doesn't need to be
     526             :      * totally accurate anyway.
     527             :      */
     528     2562502 :     if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
     529      503512 :         XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
     530     2562502 : }
     531             : 
     532             : /*
     533             :  * Replay XLOG_HEAP2_MULTI_INSERT records.
     534             :  */
     535             : static void
     536      113982 : heap_xlog_multi_insert(XLogReaderState *record)
     537             : {
     538      113982 :     XLogRecPtr  lsn = record->EndRecPtr;
     539             :     xl_heap_multi_insert *xlrec;
     540             :     RelFileLocator rlocator;
     541             :     BlockNumber blkno;
     542             :     Buffer      buffer;
     543             :     Page        page;
     544             :     union
     545             :     {
     546             :         HeapTupleHeaderData hdr;
     547             :         char        data[MaxHeapTupleSize];
     548             :     }           tbuf;
     549             :     HeapTupleHeader htup;
     550             :     uint32      newlen;
     551      113982 :     Size        freespace = 0;
     552             :     int         i;
     553      113982 :     bool        isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
     554             :     XLogRedoAction action;
     555             : 
     556             :     /*
     557             :      * Insertion doesn't overwrite MVCC data, so no conflict processing is
     558             :      * required.
     559             :      */
     560      113982 :     xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
     561             : 
     562      113982 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
     563             : 
     564             :     /* check that the mutually exclusive flags are not both set */
     565             :     Assert(!((xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) &&
     566             :              (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)));
     567             : 
     568             :     /*
     569             :      * The visibility map may need to be fixed even if the heap page is
     570             :      * already up-to-date.
     571             :      */
     572      113982 :     if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     573             :     {
     574        1868 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     575        1868 :         Buffer      vmbuffer = InvalidBuffer;
     576             : 
     577        1868 :         visibilitymap_pin(reln, blkno, &vmbuffer);
     578        1868 :         visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
     579        1868 :         ReleaseBuffer(vmbuffer);
     580        1868 :         FreeFakeRelcacheEntry(reln);
     581             :     }
     582             : 
     583      113982 :     if (isinit)
     584             :     {
     585        3580 :         buffer = XLogInitBufferForRedo(record, 0);
     586        3580 :         page = BufferGetPage(buffer);
     587        3580 :         PageInit(page, BufferGetPageSize(buffer), 0);
     588        3580 :         action = BLK_NEEDS_REDO;
     589             :     }
     590             :     else
     591      110402 :         action = XLogReadBufferForRedo(record, 0, &buffer);
     592      113982 :     if (action == BLK_NEEDS_REDO)
     593             :     {
     594             :         char       *tupdata;
     595             :         char       *endptr;
     596             :         Size        len;
     597             : 
     598             :         /* Tuples are stored as block data */
     599      110890 :         tupdata = XLogRecGetBlockData(record, 0, &len);
     600      110890 :         endptr = tupdata + len;
     601             : 
     602      110890 :         page = (Page) BufferGetPage(buffer);
     603             : 
     604      521328 :         for (i = 0; i < xlrec->ntuples; i++)
     605             :         {
     606             :             OffsetNumber offnum;
     607             :             xl_multi_insert_tuple *xlhdr;
     608             : 
     609             :             /*
     610             :              * If we're reinitializing the page, the tuples are stored in
     611             :              * order from FirstOffsetNumber. Otherwise there's an array of
     612             :              * offsets in the WAL record, and the tuples come after that.
     613             :              */
     614      410438 :             if (isinit)
     615      198590 :                 offnum = FirstOffsetNumber + i;
     616             :             else
     617      211848 :                 offnum = xlrec->offsets[i];
     618      410438 :             if (PageGetMaxOffsetNumber(page) + 1 < offnum)
     619           0 :                 elog(PANIC, "invalid max offset number");
     620             : 
     621      410438 :             xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(tupdata);
     622      410438 :             tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
     623             : 
     624      410438 :             newlen = xlhdr->datalen;
     625             :             Assert(newlen <= MaxHeapTupleSize);
     626      410438 :             htup = &tbuf.hdr;
     627      410438 :             MemSet(htup, 0, SizeofHeapTupleHeader);
     628             :             /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
     629      410438 :             memcpy((char *) htup + SizeofHeapTupleHeader,
     630             :                    tupdata,
     631             :                    newlen);
     632      410438 :             tupdata += newlen;
     633             : 
     634      410438 :             newlen += SizeofHeapTupleHeader;
     635      410438 :             htup->t_infomask2 = xlhdr->t_infomask2;
     636      410438 :             htup->t_infomask = xlhdr->t_infomask;
     637      410438 :             htup->t_hoff = xlhdr->t_hoff;
     638      410438 :             HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     639      410438 :             HeapTupleHeaderSetCmin(htup, FirstCommandId);
     640      410438 :             ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
     641      410438 :             ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
     642             : 
     643      410438 :             offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
     644      410438 :             if (offnum == InvalidOffsetNumber)
     645           0 :                 elog(PANIC, "failed to add tuple");
     646             :         }
     647      110890 :         if (tupdata != endptr)
     648           0 :             elog(PANIC, "total tuple length mismatch");
     649             : 
     650      110890 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     651             : 
     652      110890 :         PageSetLSN(page, lsn);
     653             : 
     654      110890 :         if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
     655         154 :             PageClearAllVisible(page);
     656             : 
     657             :         /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
     658      110890 :         if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
     659           8 :             PageSetAllVisible(page);
     660             : 
     661      110890 :         MarkBufferDirty(buffer);
     662             :     }
     663      113982 :     if (BufferIsValid(buffer))
     664      113982 :         UnlockReleaseBuffer(buffer);
     665             : 
     666             :     /*
     667             :      * If the page is running low on free space, update the FSM as well.
     668             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     669             :      * better than that without knowing the fill-factor for the table.
     670             :      *
     671             :      * XXX: Don't do this if the page was restored from full page image. We
     672             :      * don't bother to update the FSM in that case, it doesn't need to be
     673             :      * totally accurate anyway.
     674             :      */
     675      113982 :     if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
     676       31476 :         XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
     677      113982 : }
     678             : 
     679             : /*
     680             :  * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
     681             :  */
     682             : static void
     683      186552 : heap_xlog_update(XLogReaderState *record, bool hot_update)
     684             : {
     685      186552 :     XLogRecPtr  lsn = record->EndRecPtr;
     686      186552 :     xl_heap_update *xlrec = (xl_heap_update *) XLogRecGetData(record);
     687             :     RelFileLocator rlocator;
     688             :     BlockNumber oldblk;
     689             :     BlockNumber newblk;
     690             :     ItemPointerData newtid;
     691             :     Buffer      obuffer,
     692             :                 nbuffer;
     693             :     Page        page;
     694             :     OffsetNumber offnum;
     695      186552 :     ItemId      lp = NULL;
     696             :     HeapTupleData oldtup;
     697             :     HeapTupleHeader htup;
     698      186552 :     uint16      prefixlen = 0,
     699      186552 :                 suffixlen = 0;
     700             :     char       *newp;
     701             :     union
     702             :     {
     703             :         HeapTupleHeaderData hdr;
     704             :         char        data[MaxHeapTupleSize];
     705             :     }           tbuf;
     706             :     xl_heap_header xlhdr;
     707             :     uint32      newlen;
     708      186552 :     Size        freespace = 0;
     709             :     XLogRedoAction oldaction;
     710             :     XLogRedoAction newaction;
     711             : 
     712             :     /* initialize to keep the compiler quiet */
     713      186552 :     oldtup.t_data = NULL;
     714      186552 :     oldtup.t_len = 0;
     715             : 
     716      186552 :     XLogRecGetBlockTag(record, 0, &rlocator, NULL, &newblk);
     717      186552 :     if (XLogRecGetBlockTagExtended(record, 1, NULL, NULL, &oldblk, NULL))
     718             :     {
     719             :         /* HOT updates are never done across pages */
     720             :         Assert(!hot_update);
     721             :     }
     722             :     else
     723       77916 :         oldblk = newblk;
     724             : 
     725      186552 :     ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
     726             : 
     727             :     /*
     728             :      * The visibility map may need to be fixed even if the heap page is
     729             :      * already up-to-date.
     730             :      */
     731      186552 :     if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
     732             :     {
     733         416 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     734         416 :         Buffer      vmbuffer = InvalidBuffer;
     735             : 
     736         416 :         visibilitymap_pin(reln, oldblk, &vmbuffer);
     737         416 :         visibilitymap_clear(reln, oldblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
     738         416 :         ReleaseBuffer(vmbuffer);
     739         416 :         FreeFakeRelcacheEntry(reln);
     740             :     }
     741             : 
     742             :     /*
     743             :      * In normal operation, it is important to lock the two pages in
     744             :      * page-number order, to avoid possible deadlocks against other update
     745             :      * operations going the other way.  However, during WAL replay there can
     746             :      * be no other update happening, so we don't need to worry about that. But
     747             :      * we *do* need to worry that we don't expose an inconsistent state to Hot
     748             :      * Standby queries --- so the original page can't be unlocked before we've
     749             :      * added the new tuple to the new page.
     750             :      */
     751             : 
     752             :     /* Deal with old tuple version */
     753      186552 :     oldaction = XLogReadBufferForRedo(record, (oldblk == newblk) ? 0 : 1,
     754             :                                       &obuffer);
     755      186552 :     if (oldaction == BLK_NEEDS_REDO)
     756             :     {
     757      185976 :         page = BufferGetPage(obuffer);
     758      185976 :         offnum = xlrec->old_offnum;
     759      185976 :         if (PageGetMaxOffsetNumber(page) >= offnum)
     760      185976 :             lp = PageGetItemId(page, offnum);
     761             : 
     762      185976 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
     763           0 :             elog(PANIC, "invalid lp");
     764             : 
     765      185976 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     766             : 
     767      185976 :         oldtup.t_data = htup;
     768      185976 :         oldtup.t_len = ItemIdGetLength(lp);
     769             : 
     770      185976 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
     771      185976 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
     772      185976 :         if (hot_update)
     773       71788 :             HeapTupleHeaderSetHotUpdated(htup);
     774             :         else
     775      114188 :             HeapTupleHeaderClearHotUpdated(htup);
     776      185976 :         fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
     777             :                                    &htup->t_infomask2);
     778      185976 :         HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
     779      185976 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
     780             :         /* Set forward chain link in t_ctid */
     781      185976 :         htup->t_ctid = newtid;
     782             : 
     783             :         /* Mark the page as a candidate for pruning */
     784      185976 :         PageSetPrunable(page, XLogRecGetXid(record));
     785             : 
     786      185976 :         if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
     787         394 :             PageClearAllVisible(page);
     788             : 
     789      185976 :         PageSetLSN(page, lsn);
     790      185976 :         MarkBufferDirty(obuffer);
     791             :     }
     792             : 
     793             :     /*
     794             :      * Read the page the new tuple goes into, if different from old.
     795             :      */
     796      186552 :     if (oldblk == newblk)
     797             :     {
     798       77916 :         nbuffer = obuffer;
     799       77916 :         newaction = oldaction;
     800             :     }
     801      108636 :     else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
     802             :     {
     803        1188 :         nbuffer = XLogInitBufferForRedo(record, 0);
     804        1188 :         page = (Page) BufferGetPage(nbuffer);
     805        1188 :         PageInit(page, BufferGetPageSize(nbuffer), 0);
     806        1188 :         newaction = BLK_NEEDS_REDO;
     807             :     }
     808             :     else
     809      107448 :         newaction = XLogReadBufferForRedo(record, 0, &nbuffer);
     810             : 
     811             :     /*
     812             :      * The visibility map may need to be fixed even if the heap page is
     813             :      * already up-to-date.
     814             :      */
     815      186552 :     if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
     816             :     {
     817         330 :         Relation    reln = CreateFakeRelcacheEntry(rlocator);
     818         330 :         Buffer      vmbuffer = InvalidBuffer;
     819             : 
     820         330 :         visibilitymap_pin(reln, newblk, &vmbuffer);
     821         330 :         visibilitymap_clear(reln, newblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
     822         330 :         ReleaseBuffer(vmbuffer);
     823         330 :         FreeFakeRelcacheEntry(reln);
     824             :     }
     825             : 
     826             :     /* Deal with new tuple */
     827      186552 :     if (newaction == BLK_NEEDS_REDO)
     828             :     {
     829             :         char       *recdata;
     830             :         char       *recdata_end;
     831             :         Size        datalen;
     832             :         Size        tuplen;
     833             : 
     834      185548 :         recdata = XLogRecGetBlockData(record, 0, &datalen);
     835      185548 :         recdata_end = recdata + datalen;
     836             : 
     837      185548 :         page = BufferGetPage(nbuffer);
     838             : 
     839      185548 :         offnum = xlrec->new_offnum;
     840      185548 :         if (PageGetMaxOffsetNumber(page) + 1 < offnum)
     841           0 :             elog(PANIC, "invalid max offset number");
     842             : 
     843      185548 :         if (xlrec->flags & XLH_UPDATE_PREFIX_FROM_OLD)
     844             :         {
     845             :             Assert(newblk == oldblk);
     846       30148 :             memcpy(&prefixlen, recdata, sizeof(uint16));
     847       30148 :             recdata += sizeof(uint16);
     848             :         }
     849      185548 :         if (xlrec->flags & XLH_UPDATE_SUFFIX_FROM_OLD)
     850             :         {
     851             :             Assert(newblk == oldblk);
     852       67116 :             memcpy(&suffixlen, recdata, sizeof(uint16));
     853       67116 :             recdata += sizeof(uint16);
     854             :         }
     855             : 
     856      185548 :         memcpy(&xlhdr, recdata, SizeOfHeapHeader);
     857      185548 :         recdata += SizeOfHeapHeader;
     858             : 
     859      185548 :         tuplen = recdata_end - recdata;
     860             :         Assert(tuplen <= MaxHeapTupleSize);
     861             : 
     862      185548 :         htup = &tbuf.hdr;
     863      185548 :         MemSet(htup, 0, SizeofHeapTupleHeader);
     864             : 
     865             :         /*
     866             :          * Reconstruct the new tuple using the prefix and/or suffix from the
     867             :          * old tuple, and the data stored in the WAL record.
     868             :          */
     869      185548 :         newp = (char *) htup + SizeofHeapTupleHeader;
     870      185548 :         if (prefixlen > 0)
     871             :         {
     872             :             int         len;
     873             : 
     874             :             /* copy bitmap [+ padding] [+ oid] from WAL record */
     875       30148 :             len = xlhdr.t_hoff - SizeofHeapTupleHeader;
     876       30148 :             memcpy(newp, recdata, len);
     877       30148 :             recdata += len;
     878       30148 :             newp += len;
     879             : 
     880             :             /* copy prefix from old tuple */
     881       30148 :             memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
     882       30148 :             newp += prefixlen;
     883             : 
     884             :             /* copy new tuple data from WAL record */
     885       30148 :             len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
     886       30148 :             memcpy(newp, recdata, len);
     887       30148 :             recdata += len;
     888       30148 :             newp += len;
     889             :         }
     890             :         else
     891             :         {
     892             :             /*
     893             :              * copy bitmap [+ padding] [+ oid] + data from record, all in one
     894             :              * go
     895             :              */
     896      155400 :             memcpy(newp, recdata, tuplen);
     897      155400 :             recdata += tuplen;
     898      155400 :             newp += tuplen;
     899             :         }
     900             :         Assert(recdata == recdata_end);
     901             : 
     902             :         /* copy suffix from old tuple */
     903      185548 :         if (suffixlen > 0)
     904       67116 :             memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
     905             : 
     906      185548 :         newlen = SizeofHeapTupleHeader + tuplen + prefixlen + suffixlen;
     907      185548 :         htup->t_infomask2 = xlhdr.t_infomask2;
     908      185548 :         htup->t_infomask = xlhdr.t_infomask;
     909      185548 :         htup->t_hoff = xlhdr.t_hoff;
     910             : 
     911      185548 :         HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
     912      185548 :         HeapTupleHeaderSetCmin(htup, FirstCommandId);
     913      185548 :         HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
     914             :         /* Make sure there is no forward chain link in t_ctid */
     915      185548 :         htup->t_ctid = newtid;
     916             : 
     917      185548 :         offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
     918      185548 :         if (offnum == InvalidOffsetNumber)
     919           0 :             elog(PANIC, "failed to add tuple");
     920             : 
     921      185548 :         if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
     922         138 :             PageClearAllVisible(page);
     923             : 
     924      185548 :         freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
     925             : 
     926      185548 :         PageSetLSN(page, lsn);
     927      185548 :         MarkBufferDirty(nbuffer);
     928             :     }
     929             : 
     930      186552 :     if (BufferIsValid(nbuffer) && nbuffer != obuffer)
     931      108636 :         UnlockReleaseBuffer(nbuffer);
     932      186552 :     if (BufferIsValid(obuffer))
     933      186552 :         UnlockReleaseBuffer(obuffer);
     934             : 
     935             :     /*
     936             :      * If the new page is running low on free space, update the FSM as well.
     937             :      * Arbitrarily, our definition of "low" is less than 20%. We can't do much
     938             :      * better than that without knowing the fill-factor for the table.
     939             :      *
     940             :      * However, don't update the FSM on HOT updates, because after crash
     941             :      * recovery, either the old or the new tuple will certainly be dead and
     942             :      * prunable. After pruning, the page will have roughly as much free space
     943             :      * as it did before the update, assuming the new tuple is about the same
     944             :      * size as the old one.
     945             :      *
     946             :      * XXX: Don't do this if the page was restored from full page image. We
     947             :      * don't bother to update the FSM in that case, it doesn't need to be
     948             :      * totally accurate anyway.
     949             :      */
     950      186552 :     if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
     951       23270 :         XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
     952      186552 : }
     953             : 
     954             : /*
     955             :  * Replay XLOG_HEAP_CONFIRM records.
     956             :  */
     957             : static void
     958         154 : heap_xlog_confirm(XLogReaderState *record)
     959             : {
     960         154 :     XLogRecPtr  lsn = record->EndRecPtr;
     961         154 :     xl_heap_confirm *xlrec = (xl_heap_confirm *) XLogRecGetData(record);
     962             :     Buffer      buffer;
     963             :     Page        page;
     964             :     OffsetNumber offnum;
     965         154 :     ItemId      lp = NULL;
     966             :     HeapTupleHeader htup;
     967             : 
     968         154 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     969             :     {
     970         154 :         page = BufferGetPage(buffer);
     971             : 
     972         154 :         offnum = xlrec->offnum;
     973         154 :         if (PageGetMaxOffsetNumber(page) >= offnum)
     974         154 :             lp = PageGetItemId(page, offnum);
     975             : 
     976         154 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
     977           0 :             elog(PANIC, "invalid lp");
     978             : 
     979         154 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
     980             : 
     981             :         /*
     982             :          * Confirm tuple as actually inserted
     983             :          */
     984         154 :         ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
     985             : 
     986         154 :         PageSetLSN(page, lsn);
     987         154 :         MarkBufferDirty(buffer);
     988             :     }
     989         154 :     if (BufferIsValid(buffer))
     990         154 :         UnlockReleaseBuffer(buffer);
     991         154 : }
     992             : 
     993             : /*
     994             :  * Replay XLOG_HEAP_LOCK records.
     995             :  */
     996             : static void
     997      110406 : heap_xlog_lock(XLogReaderState *record)
     998             : {
     999      110406 :     XLogRecPtr  lsn = record->EndRecPtr;
    1000      110406 :     xl_heap_lock *xlrec = (xl_heap_lock *) XLogRecGetData(record);
    1001             :     Buffer      buffer;
    1002             :     Page        page;
    1003             :     OffsetNumber offnum;
    1004      110406 :     ItemId      lp = NULL;
    1005             :     HeapTupleHeader htup;
    1006             : 
    1007             :     /*
    1008             :      * The visibility map may need to be fixed even if the heap page is
    1009             :      * already up-to-date.
    1010             :      */
    1011      110406 :     if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
    1012             :     {
    1013             :         RelFileLocator rlocator;
    1014         104 :         Buffer      vmbuffer = InvalidBuffer;
    1015             :         BlockNumber block;
    1016             :         Relation    reln;
    1017             : 
    1018         104 :         XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
    1019         104 :         reln = CreateFakeRelcacheEntry(rlocator);
    1020             : 
    1021         104 :         visibilitymap_pin(reln, block, &vmbuffer);
    1022         104 :         visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
    1023             : 
    1024         104 :         ReleaseBuffer(vmbuffer);
    1025         104 :         FreeFakeRelcacheEntry(reln);
    1026             :     }
    1027             : 
    1028      110406 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1029             :     {
    1030      110004 :         page = (Page) BufferGetPage(buffer);
    1031             : 
    1032      110004 :         offnum = xlrec->offnum;
    1033      110004 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1034      110004 :             lp = PageGetItemId(page, offnum);
    1035             : 
    1036      110004 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1037           0 :             elog(PANIC, "invalid lp");
    1038             : 
    1039      110004 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1040             : 
    1041      110004 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
    1042      110004 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
    1043      110004 :         fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
    1044             :                                    &htup->t_infomask2);
    1045             : 
    1046             :         /*
    1047             :          * Clear relevant update flags, but only if the modified infomask says
    1048             :          * there's no update.
    1049             :          */
    1050      110004 :         if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask))
    1051             :         {
    1052      110004 :             HeapTupleHeaderClearHotUpdated(htup);
    1053             :             /* Make sure there is no forward chain link in t_ctid */
    1054      110004 :             ItemPointerSet(&htup->t_ctid,
    1055             :                            BufferGetBlockNumber(buffer),
    1056             :                            offnum);
    1057             :         }
    1058      110004 :         HeapTupleHeaderSetXmax(htup, xlrec->xmax);
    1059      110004 :         HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
    1060      110004 :         PageSetLSN(page, lsn);
    1061      110004 :         MarkBufferDirty(buffer);
    1062             :     }
    1063      110406 :     if (BufferIsValid(buffer))
    1064      110406 :         UnlockReleaseBuffer(buffer);
    1065      110406 : }
    1066             : 
    1067             : /*
    1068             :  * Replay XLOG_HEAP2_LOCK_UPDATED records.
    1069             :  */
    1070             : static void
    1071           0 : heap_xlog_lock_updated(XLogReaderState *record)
    1072             : {
    1073           0 :     XLogRecPtr  lsn = record->EndRecPtr;
    1074             :     xl_heap_lock_updated *xlrec;
    1075             :     Buffer      buffer;
    1076             :     Page        page;
    1077             :     OffsetNumber offnum;
    1078           0 :     ItemId      lp = NULL;
    1079             :     HeapTupleHeader htup;
    1080             : 
    1081           0 :     xlrec = (xl_heap_lock_updated *) XLogRecGetData(record);
    1082             : 
    1083             :     /*
    1084             :      * The visibility map may need to be fixed even if the heap page is
    1085             :      * already up-to-date.
    1086             :      */
    1087           0 :     if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
    1088             :     {
    1089             :         RelFileLocator rlocator;
    1090           0 :         Buffer      vmbuffer = InvalidBuffer;
    1091             :         BlockNumber block;
    1092             :         Relation    reln;
    1093             : 
    1094           0 :         XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
    1095           0 :         reln = CreateFakeRelcacheEntry(rlocator);
    1096             : 
    1097           0 :         visibilitymap_pin(reln, block, &vmbuffer);
    1098           0 :         visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
    1099             : 
    1100           0 :         ReleaseBuffer(vmbuffer);
    1101           0 :         FreeFakeRelcacheEntry(reln);
    1102             :     }
    1103             : 
    1104           0 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1105             :     {
    1106           0 :         page = BufferGetPage(buffer);
    1107             : 
    1108           0 :         offnum = xlrec->offnum;
    1109           0 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1110           0 :             lp = PageGetItemId(page, offnum);
    1111             : 
    1112           0 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1113           0 :             elog(PANIC, "invalid lp");
    1114             : 
    1115           0 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1116             : 
    1117           0 :         htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
    1118           0 :         htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
    1119           0 :         fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
    1120             :                                    &htup->t_infomask2);
    1121           0 :         HeapTupleHeaderSetXmax(htup, xlrec->xmax);
    1122             : 
    1123           0 :         PageSetLSN(page, lsn);
    1124           0 :         MarkBufferDirty(buffer);
    1125             :     }
    1126           0 :     if (BufferIsValid(buffer))
    1127           0 :         UnlockReleaseBuffer(buffer);
    1128           0 : }
    1129             : 
    1130             : /*
    1131             :  * Replay XLOG_HEAP_INPLACE records.
    1132             :  */
    1133             : static void
    1134       15008 : heap_xlog_inplace(XLogReaderState *record)
    1135             : {
    1136       15008 :     XLogRecPtr  lsn = record->EndRecPtr;
    1137       15008 :     xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record);
    1138             :     Buffer      buffer;
    1139             :     Page        page;
    1140             :     OffsetNumber offnum;
    1141       15008 :     ItemId      lp = NULL;
    1142             :     HeapTupleHeader htup;
    1143             :     uint32      oldlen;
    1144             :     Size        newlen;
    1145             : 
    1146       15008 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
    1147             :     {
    1148       14628 :         char       *newtup = XLogRecGetBlockData(record, 0, &newlen);
    1149             : 
    1150       14628 :         page = BufferGetPage(buffer);
    1151             : 
    1152       14628 :         offnum = xlrec->offnum;
    1153       14628 :         if (PageGetMaxOffsetNumber(page) >= offnum)
    1154       14628 :             lp = PageGetItemId(page, offnum);
    1155             : 
    1156       14628 :         if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
    1157           0 :             elog(PANIC, "invalid lp");
    1158             : 
    1159       14628 :         htup = (HeapTupleHeader) PageGetItem(page, lp);
    1160             : 
    1161       14628 :         oldlen = ItemIdGetLength(lp) - htup->t_hoff;
    1162       14628 :         if (oldlen != newlen)
    1163           0 :             elog(PANIC, "wrong tuple length");
    1164             : 
    1165       14628 :         memcpy((char *) htup + htup->t_hoff, newtup, newlen);
    1166             : 
    1167       14628 :         PageSetLSN(page, lsn);
    1168       14628 :         MarkBufferDirty(buffer);
    1169             :     }
    1170       15008 :     if (BufferIsValid(buffer))
    1171       15008 :         UnlockReleaseBuffer(buffer);
    1172             : 
    1173       15008 :     ProcessCommittedInvalidationMessages(xlrec->msgs,
    1174             :                                          xlrec->nmsgs,
    1175       15008 :                                          xlrec->relcacheInitFileInval,
    1176             :                                          xlrec->dbId,
    1177             :                                          xlrec->tsId);
    1178       15008 : }
    1179             : 
    1180             : void
    1181     3472570 : heap_redo(XLogReaderState *record)
    1182             : {
    1183     3472570 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    1184             : 
    1185             :     /*
    1186             :      * These operations don't overwrite MVCC data so no conflict processing is
    1187             :      * required. The ones in heap2 rmgr do.
    1188             :      */
    1189             : 
    1190     3472570 :     switch (info & XLOG_HEAP_OPMASK)
    1191             :     {
    1192     2562502 :         case XLOG_HEAP_INSERT:
    1193     2562502 :             heap_xlog_insert(record);
    1194     2562502 :             break;
    1195      597944 :         case XLOG_HEAP_DELETE:
    1196      597944 :             heap_xlog_delete(record);
    1197      597944 :             break;
    1198      114260 :         case XLOG_HEAP_UPDATE:
    1199      114260 :             heap_xlog_update(record, false);
    1200      114260 :             break;
    1201           4 :         case XLOG_HEAP_TRUNCATE:
    1202             : 
    1203             :             /*
    1204             :              * TRUNCATE is a no-op because the actions are already logged as
    1205             :              * SMGR WAL records.  TRUNCATE WAL record only exists for logical
    1206             :              * decoding.
    1207             :              */
    1208           4 :             break;
    1209       72292 :         case XLOG_HEAP_HOT_UPDATE:
    1210       72292 :             heap_xlog_update(record, true);
    1211       72292 :             break;
    1212         154 :         case XLOG_HEAP_CONFIRM:
    1213         154 :             heap_xlog_confirm(record);
    1214         154 :             break;
    1215      110406 :         case XLOG_HEAP_LOCK:
    1216      110406 :             heap_xlog_lock(record);
    1217      110406 :             break;
    1218       15008 :         case XLOG_HEAP_INPLACE:
    1219       15008 :             heap_xlog_inplace(record);
    1220       15008 :             break;
    1221           0 :         default:
    1222           0 :             elog(PANIC, "heap_redo: unknown op code %u", info);
    1223             :     }
    1224     3472570 : }
    1225             : 
    1226             : void
    1227      155848 : heap2_redo(XLogReaderState *record)
    1228             : {
    1229      155848 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    1230             : 
    1231      155848 :     switch (info & XLOG_HEAP_OPMASK)
    1232             :     {
    1233       25280 :         case XLOG_HEAP2_PRUNE_ON_ACCESS:
    1234             :         case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
    1235             :         case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
    1236       25280 :             heap_xlog_prune_freeze(record);
    1237       25280 :             break;
    1238       14610 :         case XLOG_HEAP2_VISIBLE:
    1239       14610 :             heap_xlog_visible(record);
    1240       14610 :             break;
    1241      113982 :         case XLOG_HEAP2_MULTI_INSERT:
    1242      113982 :             heap_xlog_multi_insert(record);
    1243      113982 :             break;
    1244           0 :         case XLOG_HEAP2_LOCK_UPDATED:
    1245           0 :             heap_xlog_lock_updated(record);
    1246           0 :             break;
    1247        1976 :         case XLOG_HEAP2_NEW_CID:
    1248             : 
    1249             :             /*
    1250             :              * Nothing to do on a real replay, only used during logical
    1251             :              * decoding.
    1252             :              */
    1253        1976 :             break;
    1254           0 :         case XLOG_HEAP2_REWRITE:
    1255           0 :             heap_xlog_logical_rewrite(record);
    1256           0 :             break;
    1257           0 :         default:
    1258           0 :             elog(PANIC, "heap2_redo: unknown op code %u", info);
    1259             :     }
    1260      155848 : }
    1261             : 
    1262             : /*
    1263             :  * Mask a heap page before performing consistency checks on it.
    1264             :  */
    1265             : void
    1266     5796044 : heap_mask(char *pagedata, BlockNumber blkno)
    1267             : {
    1268     5796044 :     Page        page = (Page) pagedata;
    1269             :     OffsetNumber off;
    1270             : 
    1271     5796044 :     mask_page_lsn_and_checksum(page);
    1272             : 
    1273     5796044 :     mask_page_hint_bits(page);
    1274     5796044 :     mask_unused_space(page);
    1275             : 
    1276   478632612 :     for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
    1277             :     {
    1278   472836568 :         ItemId      iid = PageGetItemId(page, off);
    1279             :         char       *page_item;
    1280             : 
    1281   472836568 :         page_item = (char *) (page + ItemIdGetOffset(iid));
    1282             : 
    1283   472836568 :         if (ItemIdIsNormal(iid))
    1284             :         {
    1285   443999208 :             HeapTupleHeader page_htup = (HeapTupleHeader) page_item;
    1286             : 
    1287             :             /*
    1288             :              * If xmin of a tuple is not yet frozen, we should ignore
    1289             :              * differences in hint bits, since they can be set without
    1290             :              * emitting WAL.
    1291             :              */
    1292   443999208 :             if (!HeapTupleHeaderXminFrozen(page_htup))
    1293   439847392 :                 page_htup->t_infomask &= ~HEAP_XACT_MASK;
    1294             :             else
    1295             :             {
    1296             :                 /* Still we need to mask xmax hint bits. */
    1297     4151816 :                 page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
    1298     4151816 :                 page_htup->t_infomask &= ~HEAP_XMAX_COMMITTED;
    1299             :             }
    1300             : 
    1301             :             /*
    1302             :              * During replay, we set Command Id to FirstCommandId. Hence, mask
    1303             :              * it. See heap_xlog_insert() for details.
    1304             :              */
    1305   443999208 :             page_htup->t_choice.t_heap.t_field3.t_cid = MASK_MARKER;
    1306             : 
    1307             :             /*
    1308             :              * For a speculative tuple, heap_insert() does not set ctid in the
    1309             :              * caller-passed heap tuple itself, leaving the ctid field to
    1310             :              * contain a speculative token value - a per-backend monotonically
    1311             :              * increasing identifier. Besides, it does not WAL-log ctid under
    1312             :              * any circumstances.
    1313             :              *
    1314             :              * During redo, heap_xlog_insert() sets t_ctid to current block
    1315             :              * number and self offset number. It doesn't care about any
    1316             :              * speculative insertions on the primary. Hence, we set t_ctid to
    1317             :              * current block number and self offset number to ignore any
    1318             :              * inconsistency.
    1319             :              */
    1320   443999208 :             if (HeapTupleHeaderIsSpeculative(page_htup))
    1321         156 :                 ItemPointerSet(&page_htup->t_ctid, blkno, off);
    1322             : 
    1323             :             /*
    1324             :              * NB: Not ignoring ctid changes due to the tuple having moved
    1325             :              * (i.e. HeapTupleHeaderIndicatesMovedPartitions), because that's
    1326             :              * important information that needs to be in-sync between primary
    1327             :              * and standby, and thus is WAL logged.
    1328             :              */
    1329             :         }
    1330             : 
    1331             :         /*
    1332             :          * Ignore any padding bytes after the tuple, when the length of the
    1333             :          * item is not MAXALIGNed.
    1334             :          */
    1335   472836568 :         if (ItemIdHasStorage(iid))
    1336             :         {
    1337   443999208 :             int         len = ItemIdGetLength(iid);
    1338   443999208 :             int         padlen = MAXALIGN(len) - len;
    1339             : 
    1340   443999208 :             if (padlen > 0)
    1341   237877484 :                 memset(page_item + len, MASK_MARKER, padlen);
    1342             :         }
    1343             :     }
    1344     5796044 : }

Generated by: LCOV version 1.16