LCOV - code coverage report
Current view: top level - src/backend/access/transam - timeline.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.9 % 189 151
Test Date: 2026-03-24 01:16:09 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * timeline.c
       4              :  *      Functions for reading and writing timeline history files.
       5              :  *
       6              :  * A timeline history file lists the timeline changes of the timeline, in
       7              :  * a simple text format. They are archived along with the WAL segments.
       8              :  *
       9              :  * The files are named like "<tli>.history". For example, if the database
      10              :  * starts up and switches to timeline 5, the timeline history file would be
      11              :  * called "00000005.history".
      12              :  *
      13              :  * Each line in the file represents a timeline switch:
      14              :  *
      15              :  * <parentTLI> <switchpoint> <reason>
      16              :  *
      17              :  *  parentTLI   ID of the parent timeline
      18              :  *  switchpoint XLogRecPtr of the WAL location where the switch happened
      19              :  *  reason      human-readable explanation of why the timeline was changed
      20              :  *
      21              :  * The fields are separated by tabs. Lines beginning with # are comments, and
      22              :  * are ignored. Empty lines are also ignored.
      23              :  *
      24              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      25              :  * Portions Copyright (c) 1994, Regents of the University of California
      26              :  *
      27              :  * src/backend/access/transam/timeline.c
      28              :  *
      29              :  *-------------------------------------------------------------------------
      30              :  */
      31              : 
      32              : #include "postgres.h"
      33              : 
      34              : #include <sys/stat.h>
      35              : #include <unistd.h>
      36              : 
      37              : #include "access/timeline.h"
      38              : #include "access/xlog.h"
      39              : #include "access/xlog_internal.h"
      40              : #include "access/xlogarchive.h"
      41              : #include "access/xlogdefs.h"
      42              : #include "pgstat.h"
      43              : #include "storage/fd.h"
      44              : #include "utils/wait_event.h"
      45              : 
      46              : /*
      47              :  * Copies all timeline history files with id's between 'begin' and 'end'
      48              :  * from archive to pg_wal.
      49              :  */
      50              : void
      51         1038 : restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
      52              : {
      53              :     char        path[MAXPGPATH];
      54              :     char        histfname[MAXFNAMELEN];
      55              :     TimeLineID  tli;
      56              : 
      57         1046 :     for (tli = begin; tli < end; tli++)
      58              :     {
      59            8 :         if (tli == 1)
      60            5 :             continue;
      61              : 
      62            3 :         TLHistoryFileName(histfname, tli);
      63            3 :         if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
      64            1 :             KeepFileRestoredFromArchive(path, histfname);
      65              :     }
      66         1038 : }
      67              : 
      68              : /*
      69              :  * Try to read a timeline's history file.
      70              :  *
      71              :  * If successful, return the list of component TLIs (the given TLI followed by
      72              :  * its ancestor TLIs).  If we can't find the history file, assume that the
      73              :  * timeline has no parents, and return a list of just the specified timeline
      74              :  * ID.
      75              :  */
      76              : List *
      77         2801 : readTimeLineHistory(TimeLineID targetTLI)
      78              : {
      79              :     List       *result;
      80              :     char        path[MAXPGPATH];
      81              :     char        histfname[MAXFNAMELEN];
      82              :     FILE       *fd;
      83              :     TimeLineHistoryEntry *entry;
      84         2801 :     TimeLineID  lasttli = 0;
      85              :     XLogRecPtr  prevend;
      86         2801 :     bool        fromArchive = false;
      87              : 
      88              :     /* Timeline 1 does not have a history file, so no need to check */
      89         2801 :     if (targetTLI == 1)
      90              :     {
      91         2702 :         entry = palloc_object(TimeLineHistoryEntry);
      92         2702 :         entry->tli = targetTLI;
      93         2702 :         entry->begin = entry->end = InvalidXLogRecPtr;
      94         2702 :         return list_make1(entry);
      95              :     }
      96              : 
      97           99 :     if (ArchiveRecoveryRequested)
      98              :     {
      99           40 :         TLHistoryFileName(histfname, targetTLI);
     100              :         fromArchive =
     101           40 :             RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     102              :     }
     103              :     else
     104           59 :         TLHistoryFilePath(path, targetTLI);
     105              : 
     106           99 :     fd = AllocateFile(path, "r");
     107           99 :     if (fd == NULL)
     108              :     {
     109            0 :         if (errno != ENOENT)
     110            0 :             ereport(FATAL,
     111              :                     (errcode_for_file_access(),
     112              :                      errmsg("could not open file \"%s\": %m", path)));
     113              :         /* Not there, so assume no parents */
     114            0 :         entry = palloc_object(TimeLineHistoryEntry);
     115            0 :         entry->tli = targetTLI;
     116            0 :         entry->begin = entry->end = InvalidXLogRecPtr;
     117            0 :         return list_make1(entry);
     118              :     }
     119              : 
     120           99 :     result = NIL;
     121              : 
     122              :     /*
     123              :      * Parse the file...
     124              :      */
     125           99 :     prevend = InvalidXLogRecPtr;
     126              :     for (;;)
     127          265 :     {
     128              :         char        fline[MAXPGPATH];
     129              :         char       *res;
     130              :         char       *ptr;
     131              :         TimeLineID  tli;
     132              :         uint32      switchpoint_hi;
     133              :         uint32      switchpoint_lo;
     134              :         int         nfields;
     135              : 
     136          364 :         pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
     137          364 :         res = fgets(fline, sizeof(fline), fd);
     138          364 :         pgstat_report_wait_end();
     139          364 :         if (res == NULL)
     140              :         {
     141           99 :             if (ferror(fd))
     142            0 :                 ereport(ERROR,
     143              :                         (errcode_for_file_access(),
     144              :                          errmsg("could not read file \"%s\": %m", path)));
     145              : 
     146           99 :             break;
     147              :         }
     148              : 
     149              :         /* skip leading whitespace and check for # comment */
     150          351 :         for (ptr = fline; *ptr; ptr++)
     151              :         {
     152          265 :             if (!isspace((unsigned char) *ptr))
     153          179 :                 break;
     154              :         }
     155          265 :         if (*ptr == '\0' || *ptr == '#')
     156           86 :             continue;
     157              : 
     158          179 :         nfields = sscanf(fline, "%u\t%X/%08X", &tli, &switchpoint_hi, &switchpoint_lo);
     159              : 
     160          179 :         if (nfields < 1)
     161              :         {
     162              :             /* expect a numeric timeline ID as first field of line */
     163            0 :             ereport(FATAL,
     164              :                     (errmsg("syntax error in history file: %s", fline),
     165              :                      errhint("Expected a numeric timeline ID.")));
     166              :         }
     167          179 :         if (nfields != 3)
     168            0 :             ereport(FATAL,
     169              :                     (errmsg("syntax error in history file: %s", fline),
     170              :                      errhint("Expected a write-ahead log switchpoint location.")));
     171              : 
     172          179 :         if (result && tli <= lasttli)
     173            0 :             ereport(FATAL,
     174              :                     (errmsg("invalid data in history file: %s", fline),
     175              :                      errhint("Timeline IDs must be in increasing sequence.")));
     176              : 
     177          179 :         lasttli = tli;
     178              : 
     179          179 :         entry = palloc_object(TimeLineHistoryEntry);
     180          179 :         entry->tli = tli;
     181          179 :         entry->begin = prevend;
     182          179 :         entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
     183          179 :         prevend = entry->end;
     184              : 
     185              :         /* Build list with newest item first */
     186          179 :         result = lcons(entry, result);
     187              : 
     188              :         /* we ignore the remainder of each line */
     189              :     }
     190              : 
     191           99 :     FreeFile(fd);
     192              : 
     193           99 :     if (result && targetTLI <= lasttli)
     194            0 :         ereport(FATAL,
     195              :                 (errmsg("invalid data in history file \"%s\"", path),
     196              :                  errhint("Timeline IDs must be less than child timeline's ID.")));
     197              : 
     198              :     /*
     199              :      * Create one more entry for the "tip" of the timeline, which has no entry
     200              :      * in the history file.
     201              :      */
     202           99 :     entry = palloc_object(TimeLineHistoryEntry);
     203           99 :     entry->tli = targetTLI;
     204           99 :     entry->begin = prevend;
     205           99 :     entry->end = InvalidXLogRecPtr;
     206              : 
     207           99 :     result = lcons(entry, result);
     208              : 
     209              :     /*
     210              :      * If the history file was fetched from archive, save it in pg_wal for
     211              :      * future reference.
     212              :      */
     213           99 :     if (fromArchive)
     214            3 :         KeepFileRestoredFromArchive(path, histfname);
     215              : 
     216           99 :     return result;
     217              : }
     218              : 
     219              : /*
     220              :  * Probe whether a timeline history file exists for the given timeline ID
     221              :  */
     222              : bool
     223          417 : existsTimeLineHistory(TimeLineID probeTLI)
     224              : {
     225              :     char        path[MAXPGPATH];
     226              :     char        histfname[MAXFNAMELEN];
     227              :     FILE       *fd;
     228              : 
     229              :     /* Timeline 1 does not have a history file, so no need to check */
     230          417 :     if (probeTLI == 1)
     231            0 :         return false;
     232              : 
     233          417 :     if (ArchiveRecoveryRequested)
     234              :     {
     235          372 :         TLHistoryFileName(histfname, probeTLI);
     236          372 :         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     237              :     }
     238              :     else
     239           45 :         TLHistoryFilePath(path, probeTLI);
     240              : 
     241          417 :     fd = AllocateFile(path, "r");
     242          417 :     if (fd != NULL)
     243              :     {
     244           46 :         FreeFile(fd);
     245           46 :         return true;
     246              :     }
     247              :     else
     248              :     {
     249          371 :         if (errno != ENOENT)
     250            0 :             ereport(FATAL,
     251              :                     (errcode_for_file_access(),
     252              :                      errmsg("could not open file \"%s\": %m", path)));
     253          371 :         return false;
     254              :     }
     255              : }
     256              : 
     257              : /*
     258              :  * Find the newest existing timeline, assuming that startTLI exists.
     259              :  *
     260              :  * Note: while this is somewhat heuristic, it does positively guarantee
     261              :  * that (result + 1) is not a known timeline, and therefore it should
     262              :  * be safe to assign that ID to a new timeline.
     263              :  */
     264              : TimeLineID
     265          360 : findNewestTimeLine(TimeLineID startTLI)
     266              : {
     267              :     TimeLineID  newestTLI;
     268              :     TimeLineID  probeTLI;
     269              : 
     270              :     /*
     271              :      * The algorithm is just to probe for the existence of timeline history
     272              :      * files.  XXX is it useful to allow gaps in the sequence?
     273              :      */
     274          360 :     newestTLI = startTLI;
     275              : 
     276          360 :     for (probeTLI = startTLI + 1;; probeTLI++)
     277              :     {
     278          372 :         if (existsTimeLineHistory(probeTLI))
     279              :         {
     280           12 :             newestTLI = probeTLI;   /* probeTLI exists */
     281              :         }
     282              :         else
     283              :         {
     284              :             /* doesn't exist, assume we're done */
     285          360 :             break;
     286              :         }
     287              :     }
     288              : 
     289          360 :     return newestTLI;
     290              : }
     291              : 
     292              : /*
     293              :  * Create a new timeline history file.
     294              :  *
     295              :  *  newTLI: ID of the new timeline
     296              :  *  parentTLI: ID of its immediate parent
     297              :  *  switchpoint: WAL location where the system switched to the new timeline
     298              :  *  reason: human-readable explanation of why the timeline was switched
     299              :  *
     300              :  * Currently this is only used at the end recovery, and so there are no locking
     301              :  * considerations.  But we should be just as tense as XLogFileInit to avoid
     302              :  * emplacing a bogus file.
     303              :  */
     304              : void
     305           55 : writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
     306              :                      XLogRecPtr switchpoint, char *reason)
     307              : {
     308              :     char        path[MAXPGPATH];
     309              :     char        tmppath[MAXPGPATH];
     310              :     char        histfname[MAXFNAMELEN];
     311              :     char        buffer[BLCKSZ];
     312              :     int         srcfd;
     313              :     int         fd;
     314              :     int         nbytes;
     315              : 
     316              :     Assert(newTLI > parentTLI); /* else bad selection of newTLI */
     317              : 
     318              :     /*
     319              :      * Write into a temp file name.
     320              :      */
     321           55 :     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
     322              : 
     323           55 :     unlink(tmppath);
     324              : 
     325              :     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
     326           55 :     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
     327           55 :     if (fd < 0)
     328            0 :         ereport(ERROR,
     329              :                 (errcode_for_file_access(),
     330              :                  errmsg("could not create file \"%s\": %m", tmppath)));
     331              : 
     332              :     /*
     333              :      * If a history file exists for the parent, copy it verbatim
     334              :      */
     335           55 :     if (ArchiveRecoveryRequested)
     336              :     {
     337           55 :         TLHistoryFileName(histfname, parentTLI);
     338           55 :         RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
     339              :     }
     340              :     else
     341            0 :         TLHistoryFilePath(path, parentTLI);
     342              : 
     343           55 :     srcfd = OpenTransientFile(path, O_RDONLY);
     344           55 :     if (srcfd < 0)
     345              :     {
     346           47 :         if (errno != ENOENT)
     347            0 :             ereport(ERROR,
     348              :                     (errcode_for_file_access(),
     349              :                      errmsg("could not open file \"%s\": %m", path)));
     350              :         /* Not there, so assume parent has no parents */
     351              :     }
     352              :     else
     353              :     {
     354              :         for (;;)
     355              :         {
     356            8 :             errno = 0;
     357           16 :             pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
     358           16 :             nbytes = (int) read(srcfd, buffer, sizeof(buffer));
     359           16 :             pgstat_report_wait_end();
     360           16 :             if (nbytes < 0 || errno != 0)
     361            0 :                 ereport(ERROR,
     362              :                         (errcode_for_file_access(),
     363              :                          errmsg("could not read file \"%s\": %m", path)));
     364           16 :             if (nbytes == 0)
     365            8 :                 break;
     366            8 :             errno = 0;
     367            8 :             pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
     368            8 :             if ((int) write(fd, buffer, nbytes) != nbytes)
     369              :             {
     370            0 :                 int         save_errno = errno;
     371              : 
     372              :                 /*
     373              :                  * If we fail to make the file, delete it to release disk
     374              :                  * space
     375              :                  */
     376            0 :                 unlink(tmppath);
     377              : 
     378              :                 /*
     379              :                  * if write didn't set errno, assume problem is no disk space
     380              :                  */
     381            0 :                 errno = save_errno ? save_errno : ENOSPC;
     382              : 
     383            0 :                 ereport(ERROR,
     384              :                         (errcode_for_file_access(),
     385              :                          errmsg("could not write to file \"%s\": %m", tmppath)));
     386              :             }
     387            8 :             pgstat_report_wait_end();
     388              :         }
     389              : 
     390            8 :         if (CloseTransientFile(srcfd) != 0)
     391            0 :             ereport(ERROR,
     392              :                     (errcode_for_file_access(),
     393              :                      errmsg("could not close file \"%s\": %m", path)));
     394              :     }
     395              : 
     396              :     /*
     397              :      * Append one line with the details of this timeline split.
     398              :      *
     399              :      * If we did have a parent file, insert an extra newline just in case the
     400              :      * parent file failed to end with one.
     401              :      */
     402           55 :     snprintf(buffer, sizeof(buffer),
     403              :              "%s%u\t%X/%08X\t%s\n",
     404              :              (srcfd < 0) ? "" : "\n",
     405              :              parentTLI,
     406           55 :              LSN_FORMAT_ARGS(switchpoint),
     407              :              reason);
     408              : 
     409           55 :     nbytes = strlen(buffer);
     410           55 :     errno = 0;
     411           55 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
     412           55 :     if ((int) write(fd, buffer, nbytes) != nbytes)
     413              :     {
     414            0 :         int         save_errno = errno;
     415              : 
     416              :         /*
     417              :          * If we fail to make the file, delete it to release disk space
     418              :          */
     419            0 :         unlink(tmppath);
     420              :         /* if write didn't set errno, assume problem is no disk space */
     421            0 :         errno = save_errno ? save_errno : ENOSPC;
     422              : 
     423            0 :         ereport(ERROR,
     424              :                 (errcode_for_file_access(),
     425              :                  errmsg("could not write to file \"%s\": %m", tmppath)));
     426              :     }
     427           55 :     pgstat_report_wait_end();
     428              : 
     429           55 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
     430           55 :     if (pg_fsync(fd) != 0)
     431            0 :         ereport(data_sync_elevel(ERROR),
     432              :                 (errcode_for_file_access(),
     433              :                  errmsg("could not fsync file \"%s\": %m", tmppath)));
     434           55 :     pgstat_report_wait_end();
     435              : 
     436           55 :     if (CloseTransientFile(fd) != 0)
     437            0 :         ereport(ERROR,
     438              :                 (errcode_for_file_access(),
     439              :                  errmsg("could not close file \"%s\": %m", tmppath)));
     440              : 
     441              :     /*
     442              :      * Now move the completed history file into place with its final name.
     443              :      */
     444           55 :     TLHistoryFilePath(path, newTLI);
     445              :     Assert(access(path, F_OK) != 0 && errno == ENOENT);
     446           55 :     durable_rename(tmppath, path, ERROR);
     447              : 
     448              :     /* The history file can be archived immediately. */
     449           55 :     if (XLogArchivingActive())
     450              :     {
     451           14 :         TLHistoryFileName(histfname, newTLI);
     452           14 :         XLogArchiveNotify(histfname);
     453              :     }
     454           55 : }
     455              : 
     456              : /*
     457              :  * Writes a history file for given timeline and contents.
     458              :  *
     459              :  * Currently this is only used in the walreceiver process, and so there are
     460              :  * no locking considerations.  But we should be just as tense as XLogFileInit
     461              :  * to avoid emplacing a bogus file.
     462              :  */
     463              : void
     464           11 : writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
     465              : {
     466              :     char        path[MAXPGPATH];
     467              :     char        tmppath[MAXPGPATH];
     468              :     int         fd;
     469              : 
     470              :     /*
     471              :      * Write into a temp file name.
     472              :      */
     473           11 :     snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
     474              : 
     475           11 :     unlink(tmppath);
     476              : 
     477              :     /* do not use get_sync_bit() here --- want to fsync only at end of fill */
     478           11 :     fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
     479           11 :     if (fd < 0)
     480            0 :         ereport(ERROR,
     481              :                 (errcode_for_file_access(),
     482              :                  errmsg("could not create file \"%s\": %m", tmppath)));
     483              : 
     484           11 :     errno = 0;
     485           11 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
     486           11 :     if ((int) write(fd, content, size) != size)
     487              :     {
     488            0 :         int         save_errno = errno;
     489              : 
     490              :         /*
     491              :          * If we fail to make the file, delete it to release disk space
     492              :          */
     493            0 :         unlink(tmppath);
     494              :         /* if write didn't set errno, assume problem is no disk space */
     495            0 :         errno = save_errno ? save_errno : ENOSPC;
     496              : 
     497            0 :         ereport(ERROR,
     498              :                 (errcode_for_file_access(),
     499              :                  errmsg("could not write to file \"%s\": %m", tmppath)));
     500              :     }
     501           11 :     pgstat_report_wait_end();
     502              : 
     503           11 :     pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
     504           11 :     if (pg_fsync(fd) != 0)
     505            0 :         ereport(data_sync_elevel(ERROR),
     506              :                 (errcode_for_file_access(),
     507              :                  errmsg("could not fsync file \"%s\": %m", tmppath)));
     508           11 :     pgstat_report_wait_end();
     509              : 
     510           11 :     if (CloseTransientFile(fd) != 0)
     511            0 :         ereport(ERROR,
     512              :                 (errcode_for_file_access(),
     513              :                  errmsg("could not close file \"%s\": %m", tmppath)));
     514              : 
     515              :     /*
     516              :      * Now move the completed history file into place with its final name,
     517              :      * replacing any existing file with the same name.
     518              :      */
     519           11 :     TLHistoryFilePath(path, tli);
     520           11 :     durable_rename(tmppath, path, ERROR);
     521           11 : }
     522              : 
     523              : /*
     524              :  * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
     525              :  */
     526              : bool
     527      2889628 : tliInHistory(TimeLineID tli, List *expectedTLEs)
     528              : {
     529              :     ListCell   *cell;
     530              : 
     531      2901316 :     foreach(cell, expectedTLEs)
     532              :     {
     533      2901316 :         if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
     534      2889628 :             return true;
     535              :     }
     536              : 
     537            0 :     return false;
     538              : }
     539              : 
     540              : /*
     541              :  * Returns the ID of the timeline in use at a particular point in time, in
     542              :  * the given timeline history.
     543              :  */
     544              : TimeLineID
     545         2834 : tliOfPointInHistory(XLogRecPtr ptr, List *history)
     546              : {
     547              :     ListCell   *cell;
     548              : 
     549         2847 :     foreach(cell, history)
     550              :     {
     551         2847 :         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
     552              : 
     553         2847 :         if ((!XLogRecPtrIsValid(tle->begin) || tle->begin <= ptr) &&
     554         2834 :             (!XLogRecPtrIsValid(tle->end) || ptr < tle->end))
     555              :         {
     556              :             /* found it */
     557         2834 :             return tle->tli;
     558              :         }
     559              :     }
     560              : 
     561              :     /* shouldn't happen. */
     562            0 :     elog(ERROR, "timeline history was not contiguous");
     563              :     return 0;                   /* keep compiler quiet */
     564              : }
     565              : 
     566              : /*
     567              :  * Returns the point in history where we branched off the given timeline,
     568              :  * and the timeline we branched to (*nextTLI). Returns InvalidXLogRecPtr if
     569              :  * the timeline is current, ie. we have not branched off from it, and throws
     570              :  * an error if the timeline is not part of this server's history.
     571              :  */
     572              : XLogRecPtr
     573         1578 : tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
     574              : {
     575              :     ListCell   *cell;
     576              : 
     577         1578 :     if (nextTLI)
     578         1578 :         *nextTLI = 0;
     579         1591 :     foreach(cell, history)
     580              :     {
     581         1591 :         TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
     582              : 
     583         1591 :         if (tle->tli == tli)
     584         1578 :             return tle->end;
     585           13 :         if (nextTLI)
     586           13 :             *nextTLI = tle->tli;
     587              :     }
     588              : 
     589            0 :     ereport(ERROR,
     590              :             (errmsg("requested timeline %u is not in this server's history",
     591              :                     tli)));
     592              :     return InvalidXLogRecPtr;   /* keep compiler quiet */
     593              : }
        

Generated by: LCOV version 2.0-1