LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - parsexlog.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 79.2 % 149 118
Test Date: 2026-09-16 10:15:46 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 59.8 % 112 67

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * parsexlog.c
       4                 :             :  *    Functions for reading Write-Ahead-Log
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *-------------------------------------------------------------------------
      10                 :             :  */
      11                 :             : 
      12                 :             : #include "postgres_fe.h"
      13                 :             : 
      14                 :             : #include <unistd.h>
      15                 :             : 
      16                 :             : #include "access/rmgr.h"
      17                 :             : #include "access/xact.h"
      18                 :             : #include "access/xlog_internal.h"
      19                 :             : #include "access/xlogreader.h"
      20                 :             : #include "catalog/pg_control.h"
      21                 :             : #include "catalog/storage_xlog.h"
      22                 :             : #include "commands/dbcommands_xlog.h"
      23                 :             : #include "fe_utils/archive.h"
      24                 :             : #include "filemap.h"
      25                 :             : #include "pg_rewind.h"
      26                 :             : 
      27                 :             : /*
      28                 :             :  * RmgrNames is an array of the built-in resource manager names, to make error
      29                 :             :  * messages a bit nicer.
      30                 :             :  */
      31                 :             : #define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask,decode) \
      32                 :             :   name,
      33                 :             : 
      34                 :             : static const char *const RmgrNames[RM_MAX_ID + 1] = {
      35                 :             : #include "access/rmgrlist.h"
      36                 :             : };
      37                 :             : 
      38                 :             : #define RmgrName(rmid) (((rmid) <= RM_MAX_BUILTIN_ID) ? \
      39                 :             :                         RmgrNames[rmid] : "custom")
      40                 :             : 
      41                 :             : static void extractPageInfo(XLogReaderState *record);
      42                 :             : 
      43                 :             : static int  xlogreadfd = -1;
      44                 :             : static XLogSegNo xlogreadsegno = 0;
      45                 :             : static char xlogfpath[MAXPGPATH];
      46                 :             : 
      47                 :             : typedef struct XLogPageReadPrivate
      48                 :             : {
      49                 :             :     const char *restoreCommand;
      50                 :             :     int         tliIndex;
      51                 :             : } XLogPageReadPrivate;
      52                 :             : 
      53                 :             : static int  SimpleXLogPageRead(XLogReaderState *xlogreader,
      54                 :             :                                XLogRecPtr targetPagePtr,
      55                 :             :                                int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
      56                 :             : 
      57                 :             : /*
      58                 :             :  * Read WAL from the datadir/pg_wal, starting from 'startpoint' on timeline
      59                 :             :  * index 'tliIndex' in target timeline history, until 'endpoint'. Make note of
      60                 :             :  * the data blocks touched by the WAL records, and return them in a page map.
      61                 :             :  *
      62                 :             :  * 'endpoint' is the end of the last record to read. The record starting at
      63                 :             :  * 'endpoint' is the first one that is not read.
      64                 :             :  */
      65                 :             : void
      66                 :          17 : extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex,
      67                 :             :                XLogRecPtr endpoint, const char *restoreCommand)
      68                 :             : {
      69                 :             :     XLogRecord *record;
      70                 :             :     XLogReaderState *xlogreader;
      71                 :             :     char       *errormsg;
      72                 :             :     XLogPageReadPrivate private;
      73                 :             : 
      74                 :          17 :     private.tliIndex = tliIndex;
      75                 :          17 :     private.restoreCommand = restoreCommand;
      76                 :          17 :     xlogreader = XLogReaderAllocate(WalSegSz, datadir,
      77                 :          17 :                                     XL_ROUTINE(.page_read = &SimpleXLogPageRead),
      78                 :             :                                     &private);
      79         [ -  + ]:          17 :     if (xlogreader == NULL)
      80                 :           0 :         pg_fatal("out of memory while allocating a WAL reading processor");
      81                 :             : 
      82                 :          17 :     XLogBeginRead(xlogreader, startpoint);
      83                 :             :     do
      84                 :             :     {
      85                 :       86139 :         record = XLogReadRecord(xlogreader, &errormsg);
      86                 :             : 
      87         [ -  + ]:       86139 :         if (record == NULL)
      88                 :             :         {
      89                 :           0 :             XLogRecPtr  errptr = xlogreader->EndRecPtr;
      90                 :             : 
      91         [ #  # ]:           0 :             if (errormsg)
      92                 :           0 :                 pg_fatal("could not read WAL record at %X/%08X: %s",
      93                 :             :                          LSN_FORMAT_ARGS(errptr),
      94                 :             :                          errormsg);
      95                 :             :             else
      96                 :           0 :                 pg_fatal("could not read WAL record at %X/%08X",
      97                 :             :                          LSN_FORMAT_ARGS(errptr));
      98                 :             :         }
      99                 :             : 
     100                 :       86139 :         extractPageInfo(xlogreader);
     101         [ +  + ]:       86139 :     } while (xlogreader->EndRecPtr < endpoint);
     102                 :             : 
     103                 :             :     /*
     104                 :             :      * If 'endpoint' didn't point exactly at a record boundary, the caller
     105                 :             :      * messed up.
     106                 :             :      */
     107         [ -  + ]:          17 :     if (xlogreader->EndRecPtr != endpoint)
     108                 :           0 :         pg_fatal("end pointer %X/%08X is not a valid end point; expected %X/%08X",
     109                 :             :                  LSN_FORMAT_ARGS(endpoint), LSN_FORMAT_ARGS(xlogreader->EndRecPtr));
     110                 :             : 
     111                 :          17 :     XLogReaderFree(xlogreader);
     112         [ +  - ]:          17 :     if (xlogreadfd != -1)
     113                 :             :     {
     114                 :          17 :         close(xlogreadfd);
     115                 :          17 :         xlogreadfd = -1;
     116                 :             :     }
     117                 :          17 : }
     118                 :             : 
     119                 :             : /*
     120                 :             :  * Reads one WAL record. Returns the end position of the record, without
     121                 :             :  * doing anything with the record itself.
     122                 :             :  */
     123                 :             : XLogRecPtr
     124                 :          17 : readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex,
     125                 :             :               const char *restoreCommand)
     126                 :             : {
     127                 :             :     XLogRecord *record;
     128                 :             :     XLogReaderState *xlogreader;
     129                 :             :     char       *errormsg;
     130                 :             :     XLogPageReadPrivate private;
     131                 :             :     XLogRecPtr  endptr;
     132                 :             : 
     133                 :          17 :     private.tliIndex = tliIndex;
     134                 :          17 :     private.restoreCommand = restoreCommand;
     135                 :          17 :     xlogreader = XLogReaderAllocate(WalSegSz, datadir,
     136                 :          17 :                                     XL_ROUTINE(.page_read = &SimpleXLogPageRead),
     137                 :             :                                     &private);
     138         [ -  + ]:          17 :     if (xlogreader == NULL)
     139                 :           0 :         pg_fatal("out of memory while allocating a WAL reading processor");
     140                 :             : 
     141                 :          17 :     XLogBeginRead(xlogreader, ptr);
     142                 :          17 :     record = XLogReadRecord(xlogreader, &errormsg);
     143         [ -  + ]:          17 :     if (record == NULL)
     144                 :             :     {
     145         [ #  # ]:           0 :         if (errormsg)
     146                 :           0 :             pg_fatal("could not read WAL record at %X/%08X: %s",
     147                 :             :                      LSN_FORMAT_ARGS(ptr), errormsg);
     148                 :             :         else
     149                 :           0 :             pg_fatal("could not read WAL record at %X/%08X",
     150                 :             :                      LSN_FORMAT_ARGS(ptr));
     151                 :             :     }
     152                 :          17 :     endptr = xlogreader->EndRecPtr;
     153                 :             : 
     154                 :          17 :     XLogReaderFree(xlogreader);
     155         [ +  - ]:          17 :     if (xlogreadfd != -1)
     156                 :             :     {
     157                 :          17 :         close(xlogreadfd);
     158                 :          17 :         xlogreadfd = -1;
     159                 :             :     }
     160                 :             : 
     161                 :          17 :     return endptr;
     162                 :             : }
     163                 :             : 
     164                 :             : /*
     165                 :             :  * Find the previous checkpoint preceding given WAL location.
     166                 :             :  */
     167                 :             : void
     168                 :          17 : findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
     169                 :             :                    XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli,
     170                 :             :                    XLogRecPtr *lastchkptredo, uint32 *lastchkptdatachecksums,
     171                 :             :                    const char *restoreCommand)
     172                 :             : {
     173                 :             :     /* Walk backwards, starting from the given record */
     174                 :             :     XLogRecord *record;
     175                 :             :     XLogRecPtr  searchptr;
     176                 :             :     XLogReaderState *xlogreader;
     177                 :             :     char       *errormsg;
     178                 :             :     XLogPageReadPrivate private;
     179                 :          17 :     XLogSegNo   current_segno = 0;
     180                 :          17 :     TimeLineID  current_tli = 0;
     181                 :             : 
     182                 :             :     /*
     183                 :             :      * The given fork pointer points to the end of the last common record,
     184                 :             :      * which is not necessarily the beginning of the next record, if the
     185                 :             :      * previous record happens to end at a page boundary. Skip over the page
     186                 :             :      * header in that case to find the next record.
     187                 :             :      */
     188         [ -  + ]:          17 :     if (forkptr % XLOG_BLCKSZ == 0)
     189                 :             :     {
     190         [ #  # ]:           0 :         if (XLogSegmentOffset(forkptr, WalSegSz) == 0)
     191                 :           0 :             forkptr += SizeOfXLogLongPHD;
     192                 :             :         else
     193                 :           0 :             forkptr += SizeOfXLogShortPHD;
     194                 :             :     }
     195                 :             : 
     196                 :          17 :     private.tliIndex = tliIndex;
     197                 :          17 :     private.restoreCommand = restoreCommand;
     198                 :          17 :     xlogreader = XLogReaderAllocate(WalSegSz, datadir,
     199                 :          17 :                                     XL_ROUTINE(.page_read = &SimpleXLogPageRead),
     200                 :             :                                     &private);
     201         [ -  + ]:          17 :     if (xlogreader == NULL)
     202                 :           0 :         pg_fatal("out of memory while allocating a WAL reading processor");
     203                 :             : 
     204                 :          17 :     searchptr = forkptr;
     205                 :             :     for (;;)
     206                 :        2639 :     {
     207                 :             :         uint8       info;
     208                 :             : 
     209                 :        2656 :         XLogBeginRead(xlogreader, searchptr);
     210                 :        2656 :         record = XLogReadRecord(xlogreader, &errormsg);
     211                 :             : 
     212         [ -  + ]:        2656 :         if (record == NULL)
     213                 :             :         {
     214         [ #  # ]:           0 :             if (errormsg)
     215                 :           0 :                 pg_fatal("could not find previous WAL record at %X/%08X: %s",
     216                 :             :                          LSN_FORMAT_ARGS(searchptr),
     217                 :             :                          errormsg);
     218                 :             :             else
     219                 :           0 :                 pg_fatal("could not find previous WAL record at %X/%08X",
     220                 :             :                          LSN_FORMAT_ARGS(searchptr));
     221                 :             :         }
     222                 :             : 
     223                 :             :         /* Detect if a new WAL file has been opened */
     224         [ +  + ]:        2656 :         if (xlogreader->seg.ws_tli != current_tli ||
     225         [ +  + ]:        2639 :             xlogreader->seg.ws_segno != current_segno)
     226                 :             :         {
     227                 :             :             char        xlogfname[MAXFNAMELEN];
     228                 :             : 
     229                 :          22 :             snprintf(xlogfname, MAXFNAMELEN, XLOGDIR "/");
     230                 :             : 
     231                 :             :             /* update current values */
     232                 :          22 :             current_tli = xlogreader->seg.ws_tli;
     233                 :          22 :             current_segno = xlogreader->seg.ws_segno;
     234                 :             : 
     235                 :          22 :             XLogFileName(xlogfname + sizeof(XLOGDIR),
     236                 :             :                          current_tli, current_segno, WalSegSz);
     237                 :             : 
     238                 :             :             /* Track this filename as one to not remove */
     239                 :          22 :             keepwal_add_entry(xlogfname);
     240                 :             :         }
     241                 :             : 
     242                 :             :         /*
     243                 :             :          * Check if it is a checkpoint record. This checkpoint record needs to
     244                 :             :          * be the latest checkpoint before WAL forked and not the checkpoint
     245                 :             :          * where the primary has been stopped to be rewound.
     246                 :             :          */
     247                 :        2656 :         info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
     248         [ +  + ]:        2656 :         if (searchptr < forkptr &&
     249   [ +  +  +  + ]:        2639 :             XLogRecGetRmid(xlogreader) == RM_XLOG_ID &&
     250         [ +  + ]:        1909 :             (info == XLOG_CHECKPOINT_SHUTDOWN ||
     251                 :             :              info == XLOG_CHECKPOINT_ONLINE))
     252                 :             :         {
     253                 :             :             CheckPoint  checkPoint;
     254                 :             : 
     255                 :          17 :             memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
     256                 :          17 :             *lastchkptrec = searchptr;
     257                 :          17 :             *lastchkpttli = checkPoint.ThisTimeLineID;
     258                 :          17 :             *lastchkptredo = checkPoint.redo;
     259                 :          17 :             *lastchkptdatachecksums = checkPoint.dataChecksumState;
     260                 :          17 :             break;
     261                 :             :         }
     262                 :             : 
     263                 :             :         /* Walk backwards to previous record. */
     264                 :        2639 :         searchptr = record->xl_prev;
     265                 :             :     }
     266                 :             : 
     267                 :          17 :     XLogReaderFree(xlogreader);
     268         [ +  - ]:          17 :     if (xlogreadfd != -1)
     269                 :             :     {
     270                 :          17 :         close(xlogreadfd);
     271                 :          17 :         xlogreadfd = -1;
     272                 :             :     }
     273                 :          17 : }
     274                 :             : 
     275                 :             : /* XLogReader callback function, to read a WAL page */
     276                 :             : static int
     277                 :        5891 : SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
     278                 :             :                    int reqLen, XLogRecPtr targetRecPtr, char *readBuf)
     279                 :             : {
     280                 :        5891 :     XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
     281                 :             :     uint32      targetPageOff;
     282                 :             :     XLogRecPtr  targetSegEnd;
     283                 :             :     XLogSegNo   targetSegNo;
     284                 :             :     ssize_t     r;
     285                 :             : 
     286                 :        5891 :     XLByteToSeg(targetPagePtr, targetSegNo, WalSegSz);
     287                 :        5891 :     XLogSegNoOffsetToRecPtr(targetSegNo + 1, 0, WalSegSz, targetSegEnd);
     288                 :        5891 :     targetPageOff = XLogSegmentOffset(targetPagePtr, WalSegSz);
     289                 :             : 
     290                 :             :     /*
     291                 :             :      * See if we need to switch to a new segment because the requested record
     292                 :             :      * is not in the currently open one.
     293                 :             :      */
     294         [ +  + ]:        5891 :     if (xlogreadfd >= 0 &&
     295         [ +  + ]:        5840 :         !XLByteInSeg(targetPagePtr, xlogreadsegno, WalSegSz))
     296                 :             :     {
     297                 :          11 :         close(xlogreadfd);
     298                 :          11 :         xlogreadfd = -1;
     299                 :             :     }
     300                 :             : 
     301                 :        5891 :     XLByteToSeg(targetPagePtr, xlogreadsegno, WalSegSz);
     302                 :             : 
     303         [ +  + ]:        5891 :     if (xlogreadfd < 0)
     304                 :             :     {
     305                 :             :         char        xlogfname[MAXFNAMELEN];
     306                 :             : 
     307                 :             :         /*
     308                 :             :          * Since incomplete segments are copied into next timelines, switch to
     309                 :             :          * the timeline holding the required segment. Assuming this scan can
     310                 :             :          * be done both forward and backward, consider also switching timeline
     311                 :             :          * accordingly.
     312                 :             :          */
     313         [ +  + ]:          64 :         while (private->tliIndex < targetNentries - 1 &&
     314         [ +  - ]:           2 :                targetHistory[private->tliIndex].end < targetSegEnd)
     315                 :           2 :             private->tliIndex++;
     316         [ +  + ]:          62 :         while (private->tliIndex > 0 &&
     317         [ -  + ]:           6 :                targetHistory[private->tliIndex].begin >= targetSegEnd)
     318                 :           0 :             private->tliIndex--;
     319                 :             : 
     320                 :          62 :         XLogFileName(xlogfname, targetHistory[private->tliIndex].tli,
     321                 :             :                      xlogreadsegno, WalSegSz);
     322                 :             : 
     323                 :          62 :         snprintf(xlogfpath, MAXPGPATH, "%s/" XLOGDIR "/%s",
     324                 :          62 :                  xlogreader->segcxt.ws_dir, xlogfname);
     325                 :             : 
     326                 :          62 :         xlogreadfd = open(xlogfpath, O_RDONLY | PG_BINARY, 0);
     327                 :             : 
     328         [ +  + ]:          62 :         if (xlogreadfd < 0)
     329                 :             :         {
     330                 :             :             /*
     331                 :             :              * If we have no restore_command to execute, then exit.
     332                 :             :              */
     333         [ -  + ]:           1 :             if (private->restoreCommand == NULL)
     334                 :             :             {
     335                 :           0 :                 pg_log_error("could not open file \"%s\": %m", xlogfpath);
     336                 :           0 :                 return -1;
     337                 :             :             }
     338                 :             : 
     339                 :             :             /*
     340                 :             :              * Since we have restore_command, then try to retrieve missing WAL
     341                 :             :              * file from the archive.
     342                 :             :              */
     343                 :           1 :             xlogreadfd = RestoreArchivedFile(xlogreader->segcxt.ws_dir,
     344                 :             :                                              xlogfname,
     345                 :             :                                              WalSegSz,
     346                 :             :                                              private->restoreCommand);
     347                 :             : 
     348         [ -  + ]:           1 :             if (xlogreadfd < 0)
     349                 :           0 :                 return -1;
     350                 :             :             else
     351         [ +  - ]:           1 :                 pg_log_debug("using file \"%s\" restored from archive",
     352                 :             :                              xlogfpath);
     353                 :             :         }
     354                 :             :     }
     355                 :             : 
     356                 :             :     /*
     357                 :             :      * At this point, we have the right segment open.
     358                 :             :      */
     359                 :             :     Assert(xlogreadfd != -1);
     360                 :             : 
     361                 :             :     /* Read the requested page */
     362         [ -  + ]:        5891 :     if (lseek(xlogreadfd, (off_t) targetPageOff, SEEK_SET) < 0)
     363                 :             :     {
     364                 :           0 :         pg_log_error("could not seek in file \"%s\": %m", xlogfpath);
     365                 :           0 :         return -1;
     366                 :             :     }
     367                 :             : 
     368                 :             : 
     369                 :        5891 :     r = read(xlogreadfd, readBuf, XLOG_BLCKSZ);
     370         [ -  + ]:        5891 :     if (r != XLOG_BLCKSZ)
     371                 :             :     {
     372         [ #  # ]:           0 :         if (r < 0)
     373                 :           0 :             pg_log_error("could not read file \"%s\": %m", xlogfpath);
     374                 :             :         else
     375                 :           0 :             pg_log_error("could not read file \"%s\": read %zd of %zu",
     376                 :             :                          xlogfpath, r, (Size) XLOG_BLCKSZ);
     377                 :           0 :         return -1;
     378                 :             :     }
     379                 :             : 
     380                 :             :     Assert(targetSegNo == xlogreadsegno);
     381                 :             : 
     382                 :        5891 :     xlogreader->seg.ws_tli = targetHistory[private->tliIndex].tli;
     383                 :        5891 :     return XLOG_BLCKSZ;
     384                 :             : }
     385                 :             : 
     386                 :             : /*
     387                 :             :  * Extract information on which blocks the current record modifies.
     388                 :             :  */
     389                 :             : static void
     390                 :       86139 : extractPageInfo(XLogReaderState *record)
     391                 :             : {
     392                 :             :     int         block_id;
     393                 :       86139 :     RmgrId      rmid = XLogRecGetRmid(record);
     394                 :       86139 :     uint8       info = XLogRecGetInfo(record);
     395                 :       86139 :     uint8       rminfo = info & ~XLR_INFO_MASK;
     396                 :             : 
     397                 :             :     /* Is this a special record type that I recognize? */
     398                 :             : 
     399   [ +  +  -  + ]:       86139 :     if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_CREATE_FILE_COPY)
     400                 :             :     {
     401                 :             :         /*
     402                 :             :          * New databases can be safely ignored. It won't be present in the
     403                 :             :          * source system, so it will be deleted. There's one corner-case,
     404                 :             :          * though: if a new, different, database is also created in the source
     405                 :             :          * system, we'll see that the files already exist and not copy them.
     406                 :             :          * That's OK, though; WAL replay of creating the new database, from
     407                 :             :          * the source systems's WAL, will re-copy the new database,
     408                 :             :          * overwriting the database created in the target system.
     409                 :             :          */
     410                 :             :     }
     411   [ +  +  +  - ]:       86139 :     else if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_CREATE_WAL_LOG)
     412                 :             :     {
     413                 :             :         /*
     414                 :             :          * New databases can be safely ignored. It won't be present in the
     415                 :             :          * source system, so it will be deleted.
     416                 :             :          */
     417                 :             :     }
     418   [ -  +  -  - ]:       86135 :     else if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_DROP)
     419                 :             :     {
     420                 :             :         /*
     421                 :             :          * An existing database was dropped. We'll see that the files don't
     422                 :             :          * exist in the target data dir, and copy them in toto from the source
     423                 :             :          * system. No need to do anything special here.
     424                 :             :          */
     425                 :             :     }
     426   [ +  +  +  + ]:       86135 :     else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_CREATE)
     427                 :             :     {
     428                 :             :         /*
     429                 :             :          * We can safely ignore these. The file will be removed from the
     430                 :             :          * target, if it doesn't exist in source system. If a file with same
     431                 :             :          * name is created in source system, too, there will be WAL records
     432                 :             :          * for all the blocks in it.
     433                 :             :          */
     434                 :             :     }
     435   [ +  +  +  - ]:       84938 :     else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_TRUNCATE)
     436                 :             :     {
     437                 :             :         /*
     438                 :             :          * We can safely ignore these. When we compare the sizes later on,
     439                 :             :          * we'll notice that they differ, and copy the missing tail from
     440                 :             :          * source system.
     441                 :             :          */
     442                 :             :     }
     443         [ +  + ]:       84934 :     else if (rmid == RM_XACT_ID &&
     444         [ -  + ]:          45 :              ((rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT ||
     445         [ #  # ]:           0 :               (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT_PREPARED ||
     446         [ #  # ]:           0 :               (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT ||
     447         [ #  # ]:           0 :               (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT_PREPARED))
     448                 :             :     {
     449                 :             :         /*
     450                 :             :          * These records can include "dropped rels". We can safely ignore
     451                 :             :          * them, we will see that they are missing and copy them from the
     452                 :             :          * source.
     453                 :             :          */
     454                 :             :     }
     455         [ -  + ]:       84889 :     else if (info & XLR_SPECIAL_REL_UPDATE)
     456                 :             :     {
     457                 :             :         /*
     458                 :             :          * This record type modifies a relation file in some special way, but
     459                 :             :          * we don't recognize the type. That's bad - we don't know how to
     460                 :             :          * track that change.
     461                 :             :          */
     462         [ #  # ]:           0 :         pg_fatal("WAL record modifies a relation, but record type is not recognized:\n"
     463                 :             :                  "lsn: %X/%08X, rmid: %d, rmgr: %s, info: %02X",
     464                 :             :                  LSN_FORMAT_ARGS(record->ReadRecPtr),
     465                 :             :                  rmid, RmgrName(rmid), info);
     466                 :             :     }
     467                 :             : 
     468         [ +  + ]:      171299 :     for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
     469                 :             :     {
     470                 :             :         RelFileLocator rlocator;
     471                 :             :         ForkNumber  forknum;
     472                 :             :         BlockNumber blkno;
     473                 :             : 
     474         [ -  + ]:       85160 :         if (!XLogRecGetBlockTagExtended(record, block_id,
     475                 :             :                                         &rlocator, &forknum, &blkno, NULL))
     476                 :         968 :             continue;
     477                 :             : 
     478                 :             :         /* We only care about the main fork; others are copied in toto */
     479         [ +  + ]:       85160 :         if (forknum != MAIN_FORKNUM)
     480                 :         968 :             continue;
     481                 :             : 
     482                 :       84192 :         process_target_wal_block_change(forknum, rlocator, blkno);
     483                 :             :     }
     484                 :       86139 : }
        

Generated by: LCOV version 2.0-1