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