Age Owner Branch data 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-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
4799 heikki.linnakangas@i 51 :CBC 1007 : restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
52 : : {
53 : : char path[MAXPGPATH];
54 : : char histfname[MAXFNAMELEN];
55 : : TimeLineID tli;
56 : :
57 [ + + ]: 1015 : 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 : 1007 : }
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 *
4912 77 : 2738 : readTimeLineHistory(TimeLineID targetTLI)
78 : : {
79 : : List *result;
80 : : char path[MAXPGPATH];
81 : : char histfname[MAXFNAMELEN];
82 : : FILE *fd;
83 : : TimeLineHistoryEntry *entry;
4849 84 : 2738 : TimeLineID lasttli = 0;
85 : : XLogRecPtr prevend;
4823 86 : 2738 : bool fromArchive = false;
87 : :
88 : : /* Timeline 1 does not have a history file, so no need to check */
4912 89 [ + + ]: 2738 : if (targetTLI == 1)
90 : : {
95 michael@paquier.xyz 91 :GNC 2642 : entry = palloc_object(TimeLineHistoryEntry);
4849 heikki.linnakangas@i 92 :CBC 2642 : entry->tli = targetTLI;
93 : 2642 : entry->begin = entry->end = InvalidXLogRecPtr;
94 : 2642 : return list_make1(entry);
95 : : }
96 : :
4756 97 [ + + ]: 96 : if (ArchiveRecoveryRequested)
98 : : {
4912 99 : 40 : TLHistoryFileName(histfname, targetTLI);
100 : : fromArchive =
4823 101 : 40 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
102 : : }
103 : : else
4912 104 : 56 : TLHistoryFilePath(path, targetTLI);
105 : :
106 : 96 : fd = AllocateFile(path, "r");
107 [ - + ]: 96 : if (fd == NULL)
108 : : {
4912 heikki.linnakangas@i 109 [ # # ]:UBC 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 */
95 michael@paquier.xyz 114 :UNC 0 : entry = palloc_object(TimeLineHistoryEntry);
4849 heikki.linnakangas@i 115 :UBC 0 : entry->tli = targetTLI;
116 : 0 : entry->begin = entry->end = InvalidXLogRecPtr;
117 : 0 : return list_make1(entry);
118 : : }
119 : :
4912 heikki.linnakangas@i 120 :CBC 96 : result = NIL;
121 : :
122 : : /*
123 : : * Parse the file...
124 : : */
4849 125 : 96 : prevend = InvalidXLogRecPtr;
126 : : for (;;)
4912 127 : 262 : {
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 : :
2137 fujii@postgresql.org 136 : 358 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
137 : 358 : res = fgets(fline, sizeof(fline), fd);
138 : 358 : pgstat_report_wait_end();
139 [ + + ]: 358 : if (res == NULL)
140 : : {
141 [ - + ]: 96 : if (ferror(fd))
2137 fujii@postgresql.org 142 [ # # ]:UBC 0 : ereport(ERROR,
143 : : (errcode_for_file_access(),
144 : : errmsg("could not read file \"%s\": %m", path)));
145 : :
2137 fujii@postgresql.org 146 :CBC 96 : break;
147 : : }
148 : :
149 : : /* skip leading whitespace and check for # comment */
4912 heikki.linnakangas@i 150 [ + + ]: 348 : for (ptr = fline; *ptr; ptr++)
151 : : {
152 [ + + ]: 262 : if (!isspace((unsigned char) *ptr))
153 : 176 : break;
154 : : }
155 [ + + - + ]: 262 : if (*ptr == '\0' || *ptr == '#')
156 : 86 : continue;
157 : :
251 alvherre@kurilemu.de 158 :GNC 176 : nfields = sscanf(fline, "%u\t%X/%08X", &tli, &switchpoint_hi, &switchpoint_lo);
159 : :
4849 heikki.linnakangas@i 160 [ - + ]:CBC 176 : if (nfields < 1)
161 : : {
162 : : /* expect a numeric timeline ID as first field of line */
4912 heikki.linnakangas@i 163 [ # # ]:UBC 0 : ereport(FATAL,
164 : : (errmsg("syntax error in history file: %s", fline),
165 : : errhint("Expected a numeric timeline ID.")));
166 : : }
4849 heikki.linnakangas@i 167 [ - + ]:CBC 176 : if (nfields != 3)
4849 heikki.linnakangas@i 168 [ # # ]:UBC 0 : ereport(FATAL,
169 : : (errmsg("syntax error in history file: %s", fline),
170 : : errhint("Expected a write-ahead log switchpoint location.")));
171 : :
4849 heikki.linnakangas@i 172 [ + + - + ]:CBC 176 : if (result && tli <= lasttli)
4912 heikki.linnakangas@i 173 [ # # ]:UBC 0 : ereport(FATAL,
174 : : (errmsg("invalid data in history file: %s", fline),
175 : : errhint("Timeline IDs must be in increasing sequence.")));
176 : :
4849 heikki.linnakangas@i 177 :CBC 176 : lasttli = tli;
178 : :
95 michael@paquier.xyz 179 :GNC 176 : entry = palloc_object(TimeLineHistoryEntry);
4849 heikki.linnakangas@i 180 :CBC 176 : entry->tli = tli;
181 : 176 : entry->begin = prevend;
182 : 176 : entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
183 : 176 : prevend = entry->end;
184 : :
185 : : /* Build list with newest item first */
186 : 176 : result = lcons(entry, result);
187 : :
188 : : /* we ignore the remainder of each line */
189 : : }
190 : :
4912 191 : 96 : FreeFile(fd);
192 : :
4849 193 [ + - - + ]: 96 : if (result && targetTLI <= lasttli)
4912 heikki.linnakangas@i 194 [ # # ]:UBC 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 : : */
95 michael@paquier.xyz 202 :GNC 96 : entry = palloc_object(TimeLineHistoryEntry);
4849 heikki.linnakangas@i 203 :CBC 96 : entry->tli = targetTLI;
204 : 96 : entry->begin = prevend;
205 : 96 : entry->end = InvalidXLogRecPtr;
206 : :
207 : 96 : 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 : : */
4823 213 [ + + ]: 96 : if (fromArchive)
214 : 3 : KeepFileRestoredFromArchive(path, histfname);
215 : :
4912 216 : 96 : return result;
217 : : }
218 : :
219 : : /*
220 : : * Probe whether a timeline history file exists for the given timeline ID
221 : : */
222 : : bool
223 : 409 : 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 [ - + ]: 409 : if (probeTLI == 1)
4912 heikki.linnakangas@i 231 :UBC 0 : return false;
232 : :
4756 heikki.linnakangas@i 233 [ + + ]:CBC 409 : if (ArchiveRecoveryRequested)
234 : : {
4912 235 : 364 : TLHistoryFileName(histfname, probeTLI);
4864 236 : 364 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
237 : : }
238 : : else
4912 239 : 45 : TLHistoryFilePath(path, probeTLI);
240 : :
241 : 408 : fd = AllocateFile(path, "r");
242 [ + + ]: 408 : if (fd != NULL)
243 : : {
244 : 46 : FreeFile(fd);
245 : 46 : return true;
246 : : }
247 : : else
248 : : {
249 [ - + ]: 362 : if (errno != ENOENT)
4912 heikki.linnakangas@i 250 [ # # ]:UBC 0 : ereport(FATAL,
251 : : (errcode_for_file_access(),
252 : : errmsg("could not open file \"%s\": %m", path)));
4912 heikki.linnakangas@i 253 :CBC 362 : 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 : 352 : 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 : 352 : newestTLI = startTLI;
275 : :
276 : 352 : for (probeTLI = startTLI + 1;; probeTLI++)
277 : : {
278 [ + + ]: 364 : if (existsTimeLineHistory(probeTLI))
279 : : {
3189 tgl@sss.pgh.pa.us 280 : 12 : newestTLI = probeTLI; /* probeTLI exists */
281 : : }
282 : : else
283 : : {
284 : : /* doesn't exist, assume we're done */
4912 heikki.linnakangas@i 285 : 351 : break;
286 : : }
287 : : }
288 : :
289 : 351 : 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 [ - + ]: 55 : 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 */
3095 peter_e@gmx.net 326 : 55 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
4912 heikki.linnakangas@i 327 [ - + ]: 55 : if (fd < 0)
4912 heikki.linnakangas@i 328 [ # # ]:UBC 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 : : */
4756 heikki.linnakangas@i 335 [ + - ]:CBC 55 : if (ArchiveRecoveryRequested)
336 : : {
4912 337 : 55 : TLHistoryFileName(histfname, parentTLI);
4864 338 : 55 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
339 : : }
340 : : else
4912 heikki.linnakangas@i 341 :UBC 0 : TLHistoryFilePath(path, parentTLI);
342 : :
3095 peter_e@gmx.net 343 :CBC 55 : srcfd = OpenTransientFile(path, O_RDONLY);
4912 heikki.linnakangas@i 344 [ + + ]: 55 : if (srcfd < 0)
345 : : {
346 [ - + ]: 47 : if (errno != ENOENT)
4912 heikki.linnakangas@i 347 [ # # ]:UBC 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 : : {
4912 heikki.linnakangas@i 356 :CBC 8 : errno = 0;
3284 rhaas@postgresql.org 357 : 16 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
4912 heikki.linnakangas@i 358 : 16 : nbytes = (int) read(srcfd, buffer, sizeof(buffer));
3284 rhaas@postgresql.org 359 : 16 : pgstat_report_wait_end();
4912 heikki.linnakangas@i 360 [ + - - + ]: 16 : if (nbytes < 0 || errno != 0)
4912 heikki.linnakangas@i 361 [ # # ]:UBC 0 : ereport(ERROR,
362 : : (errcode_for_file_access(),
363 : : errmsg("could not read file \"%s\": %m", path)));
4912 heikki.linnakangas@i 364 [ + + ]:CBC 16 : if (nbytes == 0)
365 : 8 : break;
366 : 8 : errno = 0;
3284 rhaas@postgresql.org 367 : 8 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
4912 heikki.linnakangas@i 368 [ - + ]: 8 : if ((int) write(fd, buffer, nbytes) != nbytes)
369 : : {
4912 heikki.linnakangas@i 370 :UBC 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 : : }
3284 rhaas@postgresql.org 387 :CBC 8 : pgstat_report_wait_end();
388 : : }
389 : :
2444 peter@eisentraut.org 390 [ - + ]: 8 : if (CloseTransientFile(srcfd) != 0)
2563 michael@paquier.xyz 391 [ # # ]:UBC 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 : : */
4912 heikki.linnakangas@i 402 :CBC 55 : snprintf(buffer, sizeof(buffer),
403 : : "%s%u\t%X/%08X\t%s\n",
404 : : (srcfd < 0) ? "" : "\n",
405 : : parentTLI,
1846 peter@eisentraut.org 406 [ + + ]: 55 : LSN_FORMAT_ARGS(switchpoint),
407 : : reason);
408 : :
4912 heikki.linnakangas@i 409 : 55 : nbytes = strlen(buffer);
410 : 55 : errno = 0;
2137 fujii@postgresql.org 411 : 55 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
4912 heikki.linnakangas@i 412 [ - + ]: 55 : if ((int) write(fd, buffer, nbytes) != nbytes)
413 : : {
4912 heikki.linnakangas@i 414 :UBC 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 : : }
2137 fujii@postgresql.org 427 :CBC 55 : pgstat_report_wait_end();
428 : :
3284 rhaas@postgresql.org 429 : 55 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
4912 heikki.linnakangas@i 430 [ - + ]: 55 : if (pg_fsync(fd) != 0)
2673 tmunro@postgresql.or 431 [ # # ]:UBC 0 : ereport(data_sync_elevel(ERROR),
432 : : (errcode_for_file_access(),
433 : : errmsg("could not fsync file \"%s\": %m", tmppath)));
3284 rhaas@postgresql.org 434 :CBC 55 : pgstat_report_wait_end();
435 : :
2444 peter@eisentraut.org 436 [ - + ]: 55 : if (CloseTransientFile(fd) != 0)
4912 heikki.linnakangas@i 437 [ # # ]:UBC 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 : : */
4912 heikki.linnakangas@i 444 :CBC 55 : TLHistoryFilePath(path, newTLI);
1349 michael@paquier.xyz 445 [ + - - + ]: 55 : Assert(access(path, F_OK) != 0 && errno == ENOENT);
446 : 55 : durable_rename(tmppath, path, ERROR);
447 : :
448 : : /* The history file can be archived immediately. */
4147 fujii@postgresql.org 449 [ + + - + : 55 : if (XLogArchivingActive())
+ + ]
450 : : {
451 : 14 : TLHistoryFileName(histfname, newTLI);
1653 alvherre@alvh.no-ip. 452 : 14 : XLogArchiveNotify(histfname);
453 : : }
4912 heikki.linnakangas@i 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
4840 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 */
3095 peter_e@gmx.net 478 : 11 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
4840 heikki.linnakangas@i 479 [ - + ]: 11 : if (fd < 0)
4840 heikki.linnakangas@i 480 [ # # ]:UBC 0 : ereport(ERROR,
481 : : (errcode_for_file_access(),
482 : : errmsg("could not create file \"%s\": %m", tmppath)));
483 : :
4840 heikki.linnakangas@i 484 :CBC 11 : errno = 0;
3284 rhaas@postgresql.org 485 : 11 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
4840 heikki.linnakangas@i 486 [ - + ]: 11 : if ((int) write(fd, content, size) != size)
487 : : {
4840 heikki.linnakangas@i 488 :UBC 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 : : }
3284 rhaas@postgresql.org 501 :CBC 11 : pgstat_report_wait_end();
502 : :
503 : 11 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
4840 heikki.linnakangas@i 504 [ - + ]: 11 : if (pg_fsync(fd) != 0)
2673 tmunro@postgresql.or 505 [ # # ]:UBC 0 : ereport(data_sync_elevel(ERROR),
506 : : (errcode_for_file_access(),
507 : : errmsg("could not fsync file \"%s\": %m", tmppath)));
3284 rhaas@postgresql.org 508 :CBC 11 : pgstat_report_wait_end();
509 : :
2444 peter@eisentraut.org 510 [ - + ]: 11 : if (CloseTransientFile(fd) != 0)
4840 heikki.linnakangas@i 511 [ # # ]:UBC 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 : : */
4840 heikki.linnakangas@i 519 :CBC 11 : TLHistoryFilePath(path, tli);
1349 michael@paquier.xyz 520 : 11 : durable_rename(tmppath, path, ERROR);
4840 heikki.linnakangas@i 521 : 11 : }
522 : :
523 : : /*
524 : : * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
525 : : */
526 : : bool
4849 527 : 2864319 : tliInHistory(TimeLineID tli, List *expectedTLEs)
528 : : {
529 : : ListCell *cell;
530 : :
531 [ + - + - : 2876021 : foreach(cell, expectedTLEs)
+ - ]
532 : : {
533 [ + + ]: 2876021 : if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
534 : 2864319 : return true;
535 : : }
536 : :
4849 heikki.linnakangas@i 537 :UBC 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
4849 heikki.linnakangas@i 545 :CBC 2767 : tliOfPointInHistory(XLogRecPtr ptr, List *history)
546 : : {
547 : : ListCell *cell;
548 : :
549 [ + - + - : 2779 : foreach(cell, history)
+ - ]
550 : : {
551 : 2779 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
552 : :
129 alvherre@kurilemu.de 553 [ + + + + ]:GNC 2779 : if ((!XLogRecPtrIsValid(tle->begin) || tle->begin <= ptr) &&
554 [ + + + - ]: 2767 : (!XLogRecPtrIsValid(tle->end) || ptr < tle->end))
555 : : {
556 : : /* found it */
4849 heikki.linnakangas@i 557 :CBC 2767 : return tle->tli;
558 : : }
559 : : }
560 : :
561 : : /* shouldn't happen. */
4849 heikki.linnakangas@i 562 [ # # ]:UBC 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
4805 heikki.linnakangas@i 573 :CBC 1542 : tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
574 : : {
575 : : ListCell *cell;
576 : :
577 [ + - ]: 1542 : if (nextTLI)
578 : 1542 : *nextTLI = 0;
4673 bruce@momjian.us 579 [ + - + - : 1554 : foreach(cell, history)
+ - ]
580 : : {
4849 heikki.linnakangas@i 581 : 1554 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
582 : :
583 [ + + ]: 1554 : if (tle->tli == tli)
584 : 1542 : return tle->end;
4805 585 [ + - ]: 12 : if (nextTLI)
586 : 12 : *nextTLI = tle->tli;
587 : : }
588 : :
4849 heikki.linnakangas@i 589 [ # # ]:UBC 0 : ereport(ERROR,
590 : : (errmsg("requested timeline %u is not in this server's history",
591 : : tli)));
592 : : return InvalidXLogRecPtr; /* keep compiler quiet */
593 : : }
|