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