LCOV - code coverage report
Current view: top level - src/backend/access/transam - xlogutils.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 167 231 72.3 %
Date: 2020-06-05 20:07:15 Functions: 18 20 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * xlogutils.c
       4             :  *
       5             :  * PostgreSQL write-ahead log manager utility routines
       6             :  *
       7             :  * This file contains support routines that are used by XLOG replay functions.
       8             :  * None of this code is used during normal system operation.
       9             :  *
      10             :  *
      11             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
      12             :  * Portions Copyright (c) 1994, Regents of the University of California
      13             :  *
      14             :  * src/backend/access/transam/xlogutils.c
      15             :  *
      16             :  *-------------------------------------------------------------------------
      17             :  */
      18             : #include "postgres.h"
      19             : 
      20             : #include <unistd.h>
      21             : 
      22             : #include "access/timeline.h"
      23             : #include "access/xlog.h"
      24             : #include "access/xlog_internal.h"
      25             : #include "access/xlogutils.h"
      26             : #include "miscadmin.h"
      27             : #include "pgstat.h"
      28             : #include "storage/smgr.h"
      29             : #include "utils/guc.h"
      30             : #include "utils/hsearch.h"
      31             : #include "utils/rel.h"
      32             : 
      33             : 
      34             : /* GUC variable */
      35             : bool        ignore_invalid_pages = false;
      36             : 
      37             : /*
      38             :  * During XLOG replay, we may see XLOG records for incremental updates of
      39             :  * pages that no longer exist, because their relation was later dropped or
      40             :  * truncated.  (Note: this is only possible when full_page_writes = OFF,
      41             :  * since when it's ON, the first reference we see to a page should always
      42             :  * be a full-page rewrite not an incremental update.)  Rather than simply
      43             :  * ignoring such records, we make a note of the referenced page, and then
      44             :  * complain if we don't actually see a drop or truncate covering the page
      45             :  * later in replay.
      46             :  */
      47             : typedef struct xl_invalid_page_key
      48             : {
      49             :     RelFileNode node;           /* the relation */
      50             :     ForkNumber  forkno;         /* the fork number */
      51             :     BlockNumber blkno;          /* the page */
      52             : } xl_invalid_page_key;
      53             : 
      54             : typedef struct xl_invalid_page
      55             : {
      56             :     xl_invalid_page_key key;    /* hash key ... must be first */
      57             :     bool        present;        /* page existed but contained zeroes */
      58             : } xl_invalid_page;
      59             : 
      60             : static HTAB *invalid_page_tab = NULL;
      61             : 
      62             : 
      63             : /* Report a reference to an invalid page */
      64             : static void
      65           0 : report_invalid_page(int elevel, RelFileNode node, ForkNumber forkno,
      66             :                     BlockNumber blkno, bool present)
      67             : {
      68           0 :     char       *path = relpathperm(node, forkno);
      69             : 
      70           0 :     if (present)
      71           0 :         elog(elevel, "page %u of relation %s is uninitialized",
      72             :              blkno, path);
      73             :     else
      74           0 :         elog(elevel, "page %u of relation %s does not exist",
      75             :              blkno, path);
      76           0 :     pfree(path);
      77           0 : }
      78             : 
      79             : /* Log a reference to an invalid page */
      80             : static void
      81          90 : log_invalid_page(RelFileNode node, ForkNumber forkno, BlockNumber blkno,
      82             :                  bool present)
      83             : {
      84             :     xl_invalid_page_key key;
      85             :     xl_invalid_page *hentry;
      86             :     bool        found;
      87             : 
      88             :     /*
      89             :      * Once recovery has reached a consistent state, the invalid-page table
      90             :      * should be empty and remain so. If a reference to an invalid page is
      91             :      * found after consistency is reached, PANIC immediately. This might seem
      92             :      * aggressive, but it's better than letting the invalid reference linger
      93             :      * in the hash table until the end of recovery and PANIC there, which
      94             :      * might come only much later if this is a standby server.
      95             :      */
      96          90 :     if (reachedConsistency)
      97             :     {
      98           0 :         report_invalid_page(WARNING, node, forkno, blkno, present);
      99           0 :         elog(ignore_invalid_pages ? WARNING : PANIC,
     100             :              "WAL contains references to invalid pages");
     101             :     }
     102             : 
     103             :     /*
     104             :      * Log references to invalid pages at DEBUG1 level.  This allows some
     105             :      * tracing of the cause (note the elog context mechanism will tell us
     106             :      * something about the XLOG record that generated the reference).
     107             :      */
     108          90 :     if (log_min_messages <= DEBUG1 || client_min_messages <= DEBUG1)
     109           0 :         report_invalid_page(DEBUG1, node, forkno, blkno, present);
     110             : 
     111          90 :     if (invalid_page_tab == NULL)
     112             :     {
     113             :         /* create hash table when first needed */
     114             :         HASHCTL     ctl;
     115             : 
     116           2 :         memset(&ctl, 0, sizeof(ctl));
     117           2 :         ctl.keysize = sizeof(xl_invalid_page_key);
     118           2 :         ctl.entrysize = sizeof(xl_invalid_page);
     119             : 
     120           2 :         invalid_page_tab = hash_create("XLOG invalid-page table",
     121             :                                        100,
     122             :                                        &ctl,
     123             :                                        HASH_ELEM | HASH_BLOBS);
     124             :     }
     125             : 
     126             :     /* we currently assume xl_invalid_page_key contains no padding */
     127          90 :     key.node = node;
     128          90 :     key.forkno = forkno;
     129          90 :     key.blkno = blkno;
     130             :     hentry = (xl_invalid_page *)
     131          90 :         hash_search(invalid_page_tab, (void *) &key, HASH_ENTER, &found);
     132             : 
     133          90 :     if (!found)
     134             :     {
     135             :         /* hash_search already filled in the key */
     136          90 :         hentry->present = present;
     137             :     }
     138             :     else
     139             :     {
     140             :         /* repeat reference ... leave "present" as it was */
     141             :     }
     142          90 : }
     143             : 
     144             : /* Forget any invalid pages >= minblkno, because they've been dropped */
     145             : static void
     146         364 : forget_invalid_pages(RelFileNode node, ForkNumber forkno, BlockNumber minblkno)
     147             : {
     148             :     HASH_SEQ_STATUS status;
     149             :     xl_invalid_page *hentry;
     150             : 
     151         364 :     if (invalid_page_tab == NULL)
     152         332 :         return;                 /* nothing to do */
     153             : 
     154          32 :     hash_seq_init(&status, invalid_page_tab);
     155             : 
     156        1202 :     while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
     157             :     {
     158        1170 :         if (RelFileNodeEquals(hentry->key.node, node) &&
     159          90 :             hentry->key.forkno == forkno &&
     160          90 :             hentry->key.blkno >= minblkno)
     161             :         {
     162          90 :             if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
     163             :             {
     164           0 :                 char       *path = relpathperm(hentry->key.node, forkno);
     165             : 
     166           0 :                 elog(DEBUG2, "page %u of relation %s has been dropped",
     167             :                      hentry->key.blkno, path);
     168           0 :                 pfree(path);
     169             :             }
     170             : 
     171          90 :             if (hash_search(invalid_page_tab,
     172          90 :                             (void *) &hentry->key,
     173             :                             HASH_REMOVE, NULL) == NULL)
     174           0 :                 elog(ERROR, "hash table corrupted");
     175             :         }
     176             :     }
     177             : }
     178             : 
     179             : /* Forget any invalid pages in a whole database */
     180             : static void
     181           2 : forget_invalid_pages_db(Oid dbid)
     182             : {
     183             :     HASH_SEQ_STATUS status;
     184             :     xl_invalid_page *hentry;
     185             : 
     186           2 :     if (invalid_page_tab == NULL)
     187           2 :         return;                 /* nothing to do */
     188             : 
     189           0 :     hash_seq_init(&status, invalid_page_tab);
     190             : 
     191           0 :     while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
     192             :     {
     193           0 :         if (hentry->key.node.dbNode == dbid)
     194             :         {
     195           0 :             if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
     196             :             {
     197           0 :                 char       *path = relpathperm(hentry->key.node, hentry->key.forkno);
     198             : 
     199           0 :                 elog(DEBUG2, "page %u of relation %s has been dropped",
     200             :                      hentry->key.blkno, path);
     201           0 :                 pfree(path);
     202             :             }
     203             : 
     204           0 :             if (hash_search(invalid_page_tab,
     205           0 :                             (void *) &hentry->key,
     206             :                             HASH_REMOVE, NULL) == NULL)
     207           0 :                 elog(ERROR, "hash table corrupted");
     208             :         }
     209             :     }
     210             : }
     211             : 
     212             : /* Are there any unresolved references to invalid pages? */
     213             : bool
     214         202 : XLogHaveInvalidPages(void)
     215             : {
     216         202 :     if (invalid_page_tab != NULL &&
     217           0 :         hash_get_num_entries(invalid_page_tab) > 0)
     218           0 :         return true;
     219         202 :     return false;
     220             : }
     221             : 
     222             : /* Complain about any remaining invalid-page entries */
     223             : void
     224          82 : XLogCheckInvalidPages(void)
     225             : {
     226             :     HASH_SEQ_STATUS status;
     227             :     xl_invalid_page *hentry;
     228          82 :     bool        foundone = false;
     229             : 
     230          82 :     if (invalid_page_tab == NULL)
     231          82 :         return;                 /* nothing to do */
     232             : 
     233           0 :     hash_seq_init(&status, invalid_page_tab);
     234             : 
     235             :     /*
     236             :      * Our strategy is to emit WARNING messages for all remaining entries and
     237             :      * only PANIC after we've dumped all the available info.
     238             :      */
     239           0 :     while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
     240             :     {
     241           0 :         report_invalid_page(WARNING, hentry->key.node, hentry->key.forkno,
     242           0 :                             hentry->key.blkno, hentry->present);
     243           0 :         foundone = true;
     244             :     }
     245             : 
     246           0 :     if (foundone)
     247           0 :         elog(ignore_invalid_pages ? WARNING : PANIC,
     248             :              "WAL contains references to invalid pages");
     249             : 
     250           0 :     hash_destroy(invalid_page_tab);
     251           0 :     invalid_page_tab = NULL;
     252             : }
     253             : 
     254             : 
     255             : /*
     256             :  * XLogReadBufferForRedo
     257             :  *      Read a page during XLOG replay
     258             :  *
     259             :  * Reads a block referenced by a WAL record into shared buffer cache, and
     260             :  * determines what needs to be done to redo the changes to it.  If the WAL
     261             :  * record includes a full-page image of the page, it is restored.
     262             :  *
     263             :  * 'lsn' is the LSN of the record being replayed.  It is compared with the
     264             :  * page's LSN to determine if the record has already been replayed.
     265             :  * 'block_id' is the ID number the block was registered with, when the WAL
     266             :  * record was created.
     267             :  *
     268             :  * Returns one of the following:
     269             :  *
     270             :  *  BLK_NEEDS_REDO  - changes from the WAL record need to be applied
     271             :  *  BLK_DONE        - block doesn't need replaying
     272             :  *  BLK_RESTORED    - block was restored from a full-page image included in
     273             :  *                    the record
     274             :  *  BLK_NOTFOUND    - block was not found (because it was truncated away by
     275             :  *                    an operation later in the WAL stream)
     276             :  *
     277             :  * On return, the buffer is locked in exclusive-mode, and returned in *buf.
     278             :  * Note that the buffer is locked and returned even if it doesn't need
     279             :  * replaying.  (Getting the buffer lock is not really necessary during
     280             :  * single-process crash recovery, but some subroutines such as MarkBufferDirty
     281             :  * will complain if we don't have the lock.  In hot standby mode it's
     282             :  * definitely necessary.)
     283             :  *
     284             :  * Note: when a backup block is available in XLOG with the BKPIMAGE_APPLY flag
     285             :  * set, we restore it, even if the page in the database appears newer.  This
     286             :  * is to protect ourselves against database pages that were partially or
     287             :  * incorrectly written during a crash.  We assume that the XLOG data must be
     288             :  * good because it has passed a CRC check, while the database page might not
     289             :  * be.  This will force us to replay all subsequent modifications of the page
     290             :  * that appear in XLOG, rather than possibly ignoring them as already
     291             :  * applied, but that's not a huge drawback.
     292             :  */
     293             : XLogRedoAction
     294      580260 : XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
     295             :                       Buffer *buf)
     296             : {
     297      580260 :     return XLogReadBufferForRedoExtended(record, block_id, RBM_NORMAL,
     298             :                                          false, buf);
     299             : }
     300             : 
     301             : /*
     302             :  * Pin and lock a buffer referenced by a WAL record, for the purpose of
     303             :  * re-initializing it.
     304             :  */
     305             : Buffer
     306        6556 : XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
     307             : {
     308             :     Buffer      buf;
     309             : 
     310        6556 :     XLogReadBufferForRedoExtended(record, block_id, RBM_ZERO_AND_LOCK, false,
     311             :                                   &buf);
     312        6556 :     return buf;
     313             : }
     314             : 
     315             : /*
     316             :  * XLogReadBufferForRedoExtended
     317             :  *      Like XLogReadBufferForRedo, but with extra options.
     318             :  *
     319             :  * In RBM_ZERO_* modes, if the page doesn't exist, the relation is extended
     320             :  * with all-zeroes pages up to the referenced block number.  In
     321             :  * RBM_ZERO_AND_LOCK and RBM_ZERO_AND_CLEANUP_LOCK modes, the return value
     322             :  * is always BLK_NEEDS_REDO.
     323             :  *
     324             :  * (The RBM_ZERO_AND_CLEANUP_LOCK mode is redundant with the get_cleanup_lock
     325             :  * parameter. Do not use an inconsistent combination!)
     326             :  *
     327             :  * If 'get_cleanup_lock' is true, a "cleanup lock" is acquired on the buffer
     328             :  * using LockBufferForCleanup(), instead of a regular exclusive lock.
     329             :  */
     330             : XLogRedoAction
     331      589154 : XLogReadBufferForRedoExtended(XLogReaderState *record,
     332             :                               uint8 block_id,
     333             :                               ReadBufferMode mode, bool get_cleanup_lock,
     334             :                               Buffer *buf)
     335             : {
     336      589154 :     XLogRecPtr  lsn = record->EndRecPtr;
     337             :     RelFileNode rnode;
     338             :     ForkNumber  forknum;
     339             :     BlockNumber blkno;
     340             :     Page        page;
     341             :     bool        zeromode;
     342             :     bool        willinit;
     343             : 
     344      589154 :     if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
     345             :     {
     346             :         /* Caller specified a bogus block_id */
     347           0 :         elog(PANIC, "failed to locate backup block with ID %d", block_id);
     348             :     }
     349             : 
     350             :     /*
     351             :      * Make sure that if the block is marked with WILL_INIT, the caller is
     352             :      * going to initialize it. And vice versa.
     353             :      */
     354      589154 :     zeromode = (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK);
     355      589154 :     willinit = (record->blocks[block_id].flags & BKPBLOCK_WILL_INIT) != 0;
     356      589154 :     if (willinit && !zeromode)
     357           0 :         elog(PANIC, "block with WILL_INIT flag in WAL record must be zeroed by redo routine");
     358      589154 :     if (!willinit && zeromode)
     359           0 :         elog(PANIC, "block to be initialized in redo routine must be marked with WILL_INIT flag in the WAL record");
     360             : 
     361             :     /* If it has a full-page image and it should be restored, do it. */
     362      589154 :     if (XLogRecBlockImageApply(record, block_id))
     363             :     {
     364             :         Assert(XLogRecHasBlockImage(record, block_id));
     365        5984 :         *buf = XLogReadBufferExtended(rnode, forknum, blkno,
     366             :                                       get_cleanup_lock ? RBM_ZERO_AND_CLEANUP_LOCK : RBM_ZERO_AND_LOCK);
     367        5984 :         page = BufferGetPage(*buf);
     368        5984 :         if (!RestoreBlockImage(record, block_id, page))
     369           0 :             elog(ERROR, "failed to restore block image");
     370             : 
     371             :         /*
     372             :          * The page may be uninitialized. If so, we can't set the LSN because
     373             :          * that would corrupt the page.
     374             :          */
     375        5984 :         if (!PageIsNew(page))
     376             :         {
     377        5984 :             PageSetLSN(page, lsn);
     378             :         }
     379             : 
     380        5984 :         MarkBufferDirty(*buf);
     381             : 
     382             :         /*
     383             :          * At the end of crash recovery the init forks of unlogged relations
     384             :          * are copied, without going through shared buffers. So we need to
     385             :          * force the on-disk state of init forks to always be in sync with the
     386             :          * state in shared buffers.
     387             :          */
     388        5984 :         if (forknum == INIT_FORKNUM)
     389           0 :             FlushOneBuffer(*buf);
     390             : 
     391        5984 :         return BLK_RESTORED;
     392             :     }
     393             :     else
     394             :     {
     395      583170 :         *buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
     396      583170 :         if (BufferIsValid(*buf))
     397             :         {
     398      583080 :             if (mode != RBM_ZERO_AND_LOCK && mode != RBM_ZERO_AND_CLEANUP_LOCK)
     399             :             {
     400      576524 :                 if (get_cleanup_lock)
     401        1856 :                     LockBufferForCleanup(*buf);
     402             :                 else
     403      574668 :                     LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
     404             :             }
     405      583080 :             if (lsn <= PageGetLSN(BufferGetPage(*buf)))
     406           0 :                 return BLK_DONE;
     407             :             else
     408      583080 :                 return BLK_NEEDS_REDO;
     409             :         }
     410             :         else
     411          90 :             return BLK_NOTFOUND;
     412             :     }
     413             : }
     414             : 
     415             : /*
     416             :  * XLogReadBufferExtended
     417             :  *      Read a page during XLOG replay
     418             :  *
     419             :  * This is functionally comparable to ReadBufferExtended. There's some
     420             :  * differences in the behavior wrt. the "mode" argument:
     421             :  *
     422             :  * In RBM_NORMAL mode, if the page doesn't exist, or contains all-zeroes, we
     423             :  * return InvalidBuffer. In this case the caller should silently skip the
     424             :  * update on this page. (In this situation, we expect that the page was later
     425             :  * dropped or truncated. If we don't see evidence of that later in the WAL
     426             :  * sequence, we'll complain at the end of WAL replay.)
     427             :  *
     428             :  * In RBM_ZERO_* modes, if the page doesn't exist, the relation is extended
     429             :  * with all-zeroes pages up to the given block number.
     430             :  *
     431             :  * In RBM_NORMAL_NO_LOG mode, we return InvalidBuffer if the page doesn't
     432             :  * exist, and we don't check for all-zeroes.  Thus, no log entry is made
     433             :  * to imply that the page should be dropped or truncated later.
     434             :  *
     435             :  * NB: A redo function should normally not call this directly. To get a page
     436             :  * to modify, use XLogReadBufferForRedoExtended instead. It is important that
     437             :  * all pages modified by a WAL record are registered in the WAL records, or
     438             :  * they will be invisible to tools that that need to know which pages are
     439             :  * modified.
     440             :  */
     441             : Buffer
     442      651820 : XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
     443             :                        BlockNumber blkno, ReadBufferMode mode)
     444             : {
     445             :     BlockNumber lastblock;
     446             :     Buffer      buffer;
     447             :     SMgrRelation smgr;
     448             : 
     449             :     Assert(blkno != P_NEW);
     450             : 
     451             :     /* Open the relation at smgr level */
     452      651820 :     smgr = smgropen(rnode, InvalidBackendId);
     453             : 
     454             :     /*
     455             :      * Create the target file if it doesn't already exist.  This lets us cope
     456             :      * if the replay sequence contains writes to a relation that is later
     457             :      * deleted.  (The original coding of this routine would instead suppress
     458             :      * the writes, but that seems like it risks losing valuable data if the
     459             :      * filesystem loses an inode during a crash.  Better to write the data
     460             :      * until we are actually told to delete the file.)
     461             :      */
     462      651820 :     smgrcreate(smgr, forknum, true);
     463             : 
     464      651820 :     lastblock = smgrnblocks(smgr, forknum);
     465             : 
     466      651820 :     if (blkno < lastblock)
     467             :     {
     468             :         /* page exists in file */
     469      649084 :         buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
     470             :                                            mode, NULL);
     471             :     }
     472             :     else
     473             :     {
     474             :         /* hm, page doesn't exist in file */
     475        2736 :         if (mode == RBM_NORMAL)
     476             :         {
     477          90 :             log_invalid_page(rnode, forknum, blkno, false);
     478          90 :             return InvalidBuffer;
     479             :         }
     480        2646 :         if (mode == RBM_NORMAL_NO_LOG)
     481           0 :             return InvalidBuffer;
     482             :         /* OK to extend the file */
     483             :         /* we do this in recovery only - no rel-extension lock needed */
     484             :         Assert(InRecovery);
     485        2646 :         buffer = InvalidBuffer;
     486             :         do
     487             :         {
     488        2722 :             if (buffer != InvalidBuffer)
     489             :             {
     490          76 :                 if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
     491           8 :                     LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
     492          76 :                 ReleaseBuffer(buffer);
     493             :             }
     494        2722 :             buffer = ReadBufferWithoutRelcache(rnode, forknum,
     495             :                                                P_NEW, mode, NULL);
     496             :         }
     497        2722 :         while (BufferGetBlockNumber(buffer) < blkno);
     498             :         /* Handle the corner case that P_NEW returns non-consecutive pages */
     499        2646 :         if (BufferGetBlockNumber(buffer) != blkno)
     500             :         {
     501           0 :             if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
     502           0 :                 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
     503           0 :             ReleaseBuffer(buffer);
     504           0 :             buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
     505             :                                                mode, NULL);
     506             :         }
     507             :     }
     508             : 
     509      651730 :     if (mode == RBM_NORMAL)
     510             :     {
     511             :         /* check that page has been initialized */
     512      576072 :         Page        page = (Page) BufferGetPage(buffer);
     513             : 
     514             :         /*
     515             :          * We assume that PageIsNew is safe without a lock. During recovery,
     516             :          * there should be no other backends that could modify the buffer at
     517             :          * the same time.
     518             :          */
     519      576072 :         if (PageIsNew(page))
     520             :         {
     521           0 :             ReleaseBuffer(buffer);
     522           0 :             log_invalid_page(rnode, forknum, blkno, true);
     523           0 :             return InvalidBuffer;
     524             :         }
     525             :     }
     526             : 
     527      651730 :     return buffer;
     528             : }
     529             : 
     530             : /*
     531             :  * Struct actually returned by CreateFakeRelcacheEntry, though the declared
     532             :  * return type is Relation.
     533             :  */
     534             : typedef struct
     535             : {
     536             :     RelationData reldata;       /* Note: this must be first */
     537             :     FormData_pg_class pgc;
     538             : } FakeRelCacheEntryData;
     539             : 
     540             : typedef FakeRelCacheEntryData *FakeRelCacheEntry;
     541             : 
     542             : /*
     543             :  * Create a fake relation cache entry for a physical relation
     544             :  *
     545             :  * It's often convenient to use the same functions in XLOG replay as in the
     546             :  * main codepath, but those functions typically work with a relcache entry.
     547             :  * We don't have a working relation cache during XLOG replay, but this
     548             :  * function can be used to create a fake relcache entry instead. Only the
     549             :  * fields related to physical storage, like rd_rel, are initialized, so the
     550             :  * fake entry is only usable in low-level operations like ReadBuffer().
     551             :  *
     552             :  * This is also used for syncing WAL-skipped files.
     553             :  *
     554             :  * Caller must free the returned entry with FreeFakeRelcacheEntry().
     555             :  */
     556             : Relation
     557        6226 : CreateFakeRelcacheEntry(RelFileNode rnode)
     558             : {
     559             :     FakeRelCacheEntry fakeentry;
     560             :     Relation    rel;
     561             : 
     562             :     /* Allocate the Relation struct and all related space in one block. */
     563        6226 :     fakeentry = palloc0(sizeof(FakeRelCacheEntryData));
     564        6226 :     rel = (Relation) fakeentry;
     565             : 
     566        6226 :     rel->rd_rel = &fakeentry->pgc;
     567        6226 :     rel->rd_node = rnode;
     568             : 
     569             :     /*
     570             :      * We will never be working with temp rels during recovery or while
     571             :      * syncing WAL-skipped files.
     572             :      */
     573        6226 :     rel->rd_backend = InvalidBackendId;
     574             : 
     575             :     /* It must be a permanent table here */
     576        6226 :     rel->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
     577             : 
     578             :     /* We don't know the name of the relation; use relfilenode instead */
     579        6226 :     sprintf(RelationGetRelationName(rel), "%u", rnode.relNode);
     580             : 
     581             :     /*
     582             :      * We set up the lockRelId in case anything tries to lock the dummy
     583             :      * relation.  Note that this is fairly bogus since relNode may be
     584             :      * different from the relation's OID.  It shouldn't really matter though.
     585             :      * In recovery, we are running by ourselves and can't have any lock
     586             :      * conflicts.  While syncing, we already hold AccessExclusiveLock.
     587             :      */
     588        6226 :     rel->rd_lockInfo.lockRelId.dbId = rnode.dbNode;
     589        6226 :     rel->rd_lockInfo.lockRelId.relId = rnode.relNode;
     590             : 
     591        6226 :     rel->rd_smgr = NULL;
     592             : 
     593        6226 :     return rel;
     594             : }
     595             : 
     596             : /*
     597             :  * Free a fake relation cache entry.
     598             :  */
     599             : void
     600        6226 : FreeFakeRelcacheEntry(Relation fakerel)
     601             : {
     602             :     /* make sure the fakerel is not referenced by the SmgrRelation anymore */
     603        6226 :     if (fakerel->rd_smgr != NULL)
     604        5454 :         smgrclearowner(&fakerel->rd_smgr, fakerel->rd_smgr);
     605        6226 :     pfree(fakerel);
     606        6226 : }
     607             : 
     608             : /*
     609             :  * Drop a relation during XLOG replay
     610             :  *
     611             :  * This is called when the relation is about to be deleted; we need to remove
     612             :  * any open "invalid-page" records for the relation.
     613             :  */
     614             : void
     615         320 : XLogDropRelation(RelFileNode rnode, ForkNumber forknum)
     616             : {
     617         320 :     forget_invalid_pages(rnode, forknum, 0);
     618         320 : }
     619             : 
     620             : /*
     621             :  * Drop a whole database during XLOG replay
     622             :  *
     623             :  * As above, but for DROP DATABASE instead of dropping a single rel
     624             :  */
     625             : void
     626           2 : XLogDropDatabase(Oid dbid)
     627             : {
     628             :     /*
     629             :      * This is unnecessarily heavy-handed, as it will close SMgrRelation
     630             :      * objects for other databases as well. DROP DATABASE occurs seldom enough
     631             :      * that it's not worth introducing a variant of smgrclose for just this
     632             :      * purpose. XXX: Or should we rather leave the smgr entries dangling?
     633             :      */
     634           2 :     smgrcloseall();
     635             : 
     636           2 :     forget_invalid_pages_db(dbid);
     637           2 : }
     638             : 
     639             : /*
     640             :  * Truncate a relation during XLOG replay
     641             :  *
     642             :  * We need to clean up any open "invalid-page" records for the dropped pages.
     643             :  */
     644             : void
     645          44 : XLogTruncateRelation(RelFileNode rnode, ForkNumber forkNum,
     646             :                      BlockNumber nblocks)
     647             : {
     648          44 :     forget_invalid_pages(rnode, forkNum, nblocks);
     649          44 : }
     650             : 
     651             : /*
     652             :  * Determine which timeline to read an xlog page from and set the
     653             :  * XLogReaderState's currTLI to that timeline ID.
     654             :  *
     655             :  * We care about timelines in xlogreader when we might be reading xlog
     656             :  * generated prior to a promotion, either if we're currently a standby in
     657             :  * recovery or if we're a promoted master reading xlogs generated by the old
     658             :  * master before our promotion.
     659             :  *
     660             :  * wantPage must be set to the start address of the page to read and
     661             :  * wantLength to the amount of the page that will be read, up to
     662             :  * XLOG_BLCKSZ. If the amount to be read isn't known, pass XLOG_BLCKSZ.
     663             :  *
     664             :  * We switch to an xlog segment from the new timeline eagerly when on a
     665             :  * historical timeline, as soon as we reach the start of the xlog segment
     666             :  * containing the timeline switch.  The server copied the segment to the new
     667             :  * timeline so all the data up to the switch point is the same, but there's no
     668             :  * guarantee the old segment will still exist. It may have been deleted or
     669             :  * renamed with a .partial suffix so we can't necessarily keep reading from
     670             :  * the old TLI even though tliSwitchPoint says it's OK.
     671             :  *
     672             :  * We can't just check the timeline when we read a page on a different segment
     673             :  * to the last page. We could've received a timeline switch from a cascading
     674             :  * upstream, so the current segment ends abruptly (possibly getting renamed to
     675             :  * .partial) and we have to switch to a new one.  Even in the middle of reading
     676             :  * a page we could have to dump the cached page and switch to a new TLI.
     677             :  *
     678             :  * Because of this, callers MAY NOT assume that currTLI is the timeline that
     679             :  * will be in a page's xlp_tli; the page may begin on an older timeline or we
     680             :  * might be reading from historical timeline data on a segment that's been
     681             :  * copied to a new timeline.
     682             :  *
     683             :  * The caller must also make sure it doesn't read past the current replay
     684             :  * position (using GetXLogReplayRecPtr) if executing in recovery, so it
     685             :  * doesn't fail to notice that the current timeline became historical. The
     686             :  * caller must also update ThisTimeLineID with the result of
     687             :  * GetXLogReplayRecPtr and must check RecoveryInProgress().
     688             :  */
     689             : void
     690      166982 : XLogReadDetermineTimeline(XLogReaderState *state, XLogRecPtr wantPage, uint32 wantLength)
     691             : {
     692      500946 :     const XLogRecPtr lastReadPage = (state->seg.ws_segno *
     693      333964 :                                      state->segcxt.ws_segsize + state->segoff);
     694             : 
     695             :     Assert(wantPage != InvalidXLogRecPtr && wantPage % XLOG_BLCKSZ == 0);
     696             :     Assert(wantLength <= XLOG_BLCKSZ);
     697             :     Assert(state->readLen == 0 || state->readLen <= XLOG_BLCKSZ);
     698             : 
     699             :     /*
     700             :      * If the desired page is currently read in and valid, we have nothing to
     701             :      * do.
     702             :      *
     703             :      * The caller should've ensured that it didn't previously advance readOff
     704             :      * past the valid limit of this timeline, so it doesn't matter if the
     705             :      * current TLI has since become historical.
     706             :      */
     707      166982 :     if (lastReadPage == wantPage &&
     708        1328 :         state->readLen != 0 &&
     709        1328 :         lastReadPage + state->readLen >= wantPage + Min(wantLength, XLOG_BLCKSZ - 1))
     710           0 :         return;
     711             : 
     712             :     /*
     713             :      * If we're reading from the current timeline, it hasn't become historical
     714             :      * and the page we're reading is after the last page read, we can again
     715             :      * just carry on. (Seeking backwards requires a check to make sure the
     716             :      * older page isn't on a prior timeline).
     717             :      *
     718             :      * ThisTimeLineID might've become historical since we last looked, but the
     719             :      * caller is required not to read past the flush limit it saw at the time
     720             :      * it looked up the timeline. There's nothing we can do about it if
     721             :      * StartupXLOG() renames it to .partial concurrently.
     722             :      */
     723      166982 :     if (state->currTLI == ThisTimeLineID && wantPage >= lastReadPage)
     724             :     {
     725             :         Assert(state->currTLIValidUntil == InvalidXLogRecPtr);
     726      166210 :         return;
     727             :     }
     728             : 
     729             :     /*
     730             :      * If we're just reading pages from a previously validated historical
     731             :      * timeline and the timeline we're reading from is valid until the end of
     732             :      * the current segment we can just keep reading.
     733             :      */
     734         772 :     if (state->currTLIValidUntil != InvalidXLogRecPtr &&
     735          68 :         state->currTLI != ThisTimeLineID &&
     736          68 :         state->currTLI != 0 &&
     737          68 :         ((wantPage + wantLength) / state->segcxt.ws_segsize) <
     738          68 :         (state->currTLIValidUntil / state->segcxt.ws_segsize))
     739          64 :         return;
     740             : 
     741             :     /*
     742             :      * If we reach this point we're either looking up a page for random
     743             :      * access, the current timeline just became historical, or we're reading
     744             :      * from a new segment containing a timeline switch. In all cases we need
     745             :      * to determine the newest timeline on the segment.
     746             :      *
     747             :      * If it's the current timeline we can just keep reading from here unless
     748             :      * we detect a timeline switch that makes the current timeline historical.
     749             :      * If it's a historical timeline we can read all the segment on the newest
     750             :      * timeline because it contains all the old timelines' data too. So only
     751             :      * one switch check is required.
     752             :      */
     753             :     {
     754             :         /*
     755             :          * We need to re-read the timeline history in case it's been changed
     756             :          * by a promotion or replay from a cascaded replica.
     757             :          */
     758         708 :         List       *timelineHistory = readTimeLineHistory(ThisTimeLineID);
     759             :         XLogRecPtr  endOfSegment;
     760             : 
     761        1416 :         endOfSegment = ((wantPage / state->segcxt.ws_segsize) + 1) *
     762         708 :             state->segcxt.ws_segsize - 1;
     763             :         Assert(wantPage / state->segcxt.ws_segsize ==
     764             :                endOfSegment / state->segcxt.ws_segsize);
     765             : 
     766             :         /*
     767             :          * Find the timeline of the last LSN on the segment containing
     768             :          * wantPage.
     769             :          */
     770         708 :         state->currTLI = tliOfPointInHistory(endOfSegment, timelineHistory);
     771         708 :         state->currTLIValidUntil = tliSwitchPoint(state->currTLI, timelineHistory,
     772             :                                                   &state->nextTLI);
     773             : 
     774             :         Assert(state->currTLIValidUntil == InvalidXLogRecPtr ||
     775             :                wantPage + wantLength < state->currTLIValidUntil);
     776             : 
     777         708 :         list_free_deep(timelineHistory);
     778             : 
     779         708 :         elog(DEBUG3, "switched to timeline %u valid until %X/%X",
     780             :              state->currTLI,
     781             :              (uint32) (state->currTLIValidUntil >> 32),
     782             :              (uint32) (state->currTLIValidUntil));
     783             :     }
     784             : }
     785             : 
     786             : /* XLogReaderRoutine->segment_open callback for local pg_wal files */
     787             : void
     788         526 : wal_segment_open(XLogReaderState *state, XLogSegNo nextSegNo,
     789             :                  TimeLineID *tli_p)
     790             : {
     791         526 :     TimeLineID  tli = *tli_p;
     792             :     char        path[MAXPGPATH];
     793             : 
     794         526 :     XLogFilePath(path, tli, nextSegNo, state->segcxt.ws_segsize);
     795         526 :     state->seg.ws_file = BasicOpenFile(path, O_RDONLY | PG_BINARY);
     796         526 :     if (state->seg.ws_file >= 0)
     797         526 :         return;
     798             : 
     799           0 :     if (errno == ENOENT)
     800           0 :         ereport(ERROR,
     801             :                 (errcode_for_file_access(),
     802             :                  errmsg("requested WAL segment %s has already been removed",
     803             :                         path)));
     804             :     else
     805           0 :         ereport(ERROR,
     806             :                 (errcode_for_file_access(),
     807             :                  errmsg("could not open file \"%s\": %m",
     808             :                         path)));
     809             : }
     810             : 
     811             : /* stock XLogReaderRoutine->segment_close callback */
     812             : void
     813       66862 : wal_segment_close(XLogReaderState *state)
     814             : {
     815       66862 :     close(state->seg.ws_file);
     816             :     /* need to check errno? */
     817       66862 :     state->seg.ws_file = -1;
     818       66862 : }
     819             : 
     820             : /*
     821             :  * XLogReaderRoutine->page_read callback for reading local xlog files
     822             :  *
     823             :  * Public because it would likely be very helpful for someone writing another
     824             :  * output method outside walsender, e.g. in a bgworker.
     825             :  *
     826             :  * TODO: The walsender has its own version of this, but it relies on the
     827             :  * walsender's latch being set whenever WAL is flushed. No such infrastructure
     828             :  * exists for normal backends, so we have to do a check/sleep/repeat style of
     829             :  * loop for now.
     830             :  */
     831             : int
     832       32022 : read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
     833             :                      int reqLen, XLogRecPtr targetRecPtr, char *cur_page)
     834             : {
     835             :     XLogRecPtr  read_upto,
     836             :                 loc;
     837             :     TimeLineID  tli;
     838             :     int         count;
     839             :     WALReadError errinfo;
     840             : 
     841       32022 :     loc = targetPagePtr + reqLen;
     842             : 
     843             :     /* Loop waiting for xlog to be available if necessary */
     844             :     while (1)
     845             :     {
     846             :         /*
     847             :          * Determine the limit of xlog we can currently read to, and what the
     848             :          * most recent timeline is.
     849             :          *
     850             :          * RecoveryInProgress() will update ThisTimeLineID when it first
     851             :          * notices recovery finishes, so we only have to maintain it for the
     852             :          * local process until recovery ends.
     853             :          */
     854       33614 :         if (!RecoveryInProgress())
     855       33454 :             read_upto = GetFlushRecPtr();
     856             :         else
     857         160 :             read_upto = GetXLogReplayRecPtr(&ThisTimeLineID);
     858       33614 :         tli = ThisTimeLineID;
     859             : 
     860             :         /*
     861             :          * Check which timeline to get the record from.
     862             :          *
     863             :          * We have to do it each time through the loop because if we're in
     864             :          * recovery as a cascading standby, the current timeline might've
     865             :          * become historical. We can't rely on RecoveryInProgress() because in
     866             :          * a standby configuration like
     867             :          *
     868             :          * A => B => C
     869             :          *
     870             :          * if we're a logical decoding session on C, and B gets promoted, our
     871             :          * timeline will change while we remain in recovery.
     872             :          *
     873             :          * We can't just keep reading from the old timeline as the last WAL
     874             :          * archive in the timeline will get renamed to .partial by
     875             :          * StartupXLOG().
     876             :          *
     877             :          * If that happens after our caller updated ThisTimeLineID but before
     878             :          * we actually read the xlog page, we might still try to read from the
     879             :          * old (now renamed) segment and fail. There's not much we can do
     880             :          * about this, but it can only happen when we're a leaf of a cascading
     881             :          * standby whose master gets promoted while we're decoding, so a
     882             :          * one-off ERROR isn't too bad.
     883             :          */
     884       33614 :         XLogReadDetermineTimeline(state, targetPagePtr, reqLen);
     885             : 
     886       33614 :         if (state->currTLI == ThisTimeLineID)
     887             :         {
     888             : 
     889       33546 :             if (loc <= read_upto)
     890       31954 :                 break;
     891             : 
     892        1592 :             CHECK_FOR_INTERRUPTS();
     893        1592 :             pg_usleep(1000L);
     894             :         }
     895             :         else
     896             :         {
     897             :             /*
     898             :              * We're on a historical timeline, so limit reading to the switch
     899             :              * point where we moved to the next timeline.
     900             :              *
     901             :              * We don't need to GetFlushRecPtr or GetXLogReplayRecPtr. We know
     902             :              * about the new timeline, so we must've received past the end of
     903             :              * it.
     904             :              */
     905          68 :             read_upto = state->currTLIValidUntil;
     906             : 
     907             :             /*
     908             :              * Setting tli to our wanted record's TLI is slightly wrong; the
     909             :              * page might begin on an older timeline if it contains a timeline
     910             :              * switch, since its xlog segment will have been copied from the
     911             :              * prior timeline. This is pretty harmless though, as nothing
     912             :              * cares so long as the timeline doesn't go backwards.  We should
     913             :              * read the page header instead; FIXME someday.
     914             :              */
     915          68 :             tli = state->currTLI;
     916             : 
     917             :             /* No need to wait on a historical timeline */
     918          68 :             break;
     919             :         }
     920             :     }
     921             : 
     922       32022 :     if (targetPagePtr + XLOG_BLCKSZ <= read_upto)
     923             :     {
     924             :         /*
     925             :          * more than one block available; read only that block, have caller
     926             :          * come back if they need more.
     927             :          */
     928       31520 :         count = XLOG_BLCKSZ;
     929             :     }
     930         502 :     else if (targetPagePtr + reqLen > read_upto)
     931             :     {
     932             :         /* not enough data there */
     933           0 :         return -1;
     934             :     }
     935             :     else
     936             :     {
     937             :         /* enough bytes available to satisfy the request */
     938         502 :         count = read_upto - targetPagePtr;
     939             :     }
     940             : 
     941             :     /*
     942             :      * Even though we just determined how much of the page can be validly read
     943             :      * as 'count', read the whole page anyway. It's guaranteed to be
     944             :      * zero-padded up to the page boundary if it's incomplete.
     945             :      */
     946       32022 :     if (!WALRead(state, cur_page, targetPagePtr, XLOG_BLCKSZ, tli,
     947             :                  &errinfo))
     948           0 :         WALReadRaiseError(&errinfo);
     949             : 
     950             :     /* number of valid bytes in the buffer */
     951       32022 :     return count;
     952             : }
     953             : 
     954             : /*
     955             :  * Backend-specific convenience code to handle read errors encountered by
     956             :  * WALRead().
     957             :  */
     958             : void
     959           0 : WALReadRaiseError(WALReadError *errinfo)
     960             : {
     961           0 :     WALOpenSegment *seg = &errinfo->wre_seg;
     962             :     char        fname[MAXFNAMELEN];
     963             : 
     964           0 :     XLogFileName(fname, seg->ws_tli, seg->ws_segno, wal_segment_size);
     965             : 
     966           0 :     if (errinfo->wre_read < 0)
     967             :     {
     968           0 :         errno = errinfo->wre_errno;
     969           0 :         ereport(ERROR,
     970             :                 (errcode_for_file_access(),
     971             :                  errmsg("could not read from log segment %s, offset %u: %m",
     972             :                         fname, errinfo->wre_off)));
     973             :     }
     974           0 :     else if (errinfo->wre_read == 0)
     975             :     {
     976           0 :         ereport(ERROR,
     977             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     978             :                  errmsg("could not read from log segment %s, offset %u: read %d of %zu",
     979             :                         fname, errinfo->wre_off, errinfo->wre_read,
     980             :                         (Size) errinfo->wre_req)));
     981             :     }
     982           0 : }

Generated by: LCOV version 1.13