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-2024, 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
4099 heikki.linnakangas@i 50 :CBC 834 : restoreTimeLineHistoryFiles(TimeLineID begin, TimeLineID end)
51 : : {
52 : : char path[MAXPGPATH];
53 : : char histfname[MAXFNAMELEN];
54 : : TimeLineID tli;
55 : :
56 [ + + ]: 852 : for (tli = begin; tli < end; tli++)
57 : : {
58 [ + + ]: 18 : if (tli == 1)
59 : 15 : continue;
60 : :
61 : 3 : TLHistoryFileName(histfname, tli);
62 [ + + ]: 3 : if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
63 : 1 : KeepFileRestoredFromArchive(path, histfname);
64 : : }
65 : 834 : }
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 *
4212 76 : 2409 : readTimeLineHistory(TimeLineID targetTLI)
77 : : {
78 : : List *result;
79 : : char path[MAXPGPATH];
80 : : char histfname[MAXFNAMELEN];
81 : : FILE *fd;
82 : : TimeLineHistoryEntry *entry;
4149 83 : 2409 : TimeLineID lasttli = 0;
84 : : XLogRecPtr prevend;
4123 85 : 2409 : bool fromArchive = false;
86 : :
87 : : /* Timeline 1 does not have a history file, so no need to check */
4212 88 [ + + ]: 2409 : if (targetTLI == 1)
89 : : {
4149 90 : 2294 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
91 : 2294 : entry->tli = targetTLI;
92 : 2294 : entry->begin = entry->end = InvalidXLogRecPtr;
93 : 2294 : return list_make1(entry);
94 : : }
95 : :
4056 96 [ + + ]: 115 : if (ArchiveRecoveryRequested)
97 : : {
4212 98 : 55 : TLHistoryFileName(histfname, targetTLI);
99 : : fromArchive =
4123 100 : 55 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
101 : : }
102 : : else
4212 103 : 60 : TLHistoryFilePath(path, targetTLI);
104 : :
105 : 115 : fd = AllocateFile(path, "r");
106 [ - + ]: 115 : if (fd == NULL)
107 : : {
4212 heikki.linnakangas@i 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 */
4149 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 : :
4212 heikki.linnakangas@i 119 :CBC 115 : result = NIL;
120 : :
121 : : /*
122 : : * Parse the file...
123 : : */
4149 124 : 115 : prevend = InvalidXLogRecPtr;
125 : : for (;;)
4212 126 : 272 : {
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 : :
1437 fujii@postgresql.org 135 : 387 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
136 : 387 : res = fgets(fline, sizeof(fline), fd);
137 : 387 : pgstat_report_wait_end();
138 [ + + ]: 387 : if (res == NULL)
139 : : {
140 [ - + ]: 115 : if (ferror(fd))
1437 fujii@postgresql.org 141 [ # # ]:UBC 0 : ereport(ERROR,
142 : : (errcode_for_file_access(),
143 : : errmsg("could not read file \"%s\": %m", path)));
144 : :
1437 fujii@postgresql.org 145 :CBC 115 : break;
146 : : }
147 : :
148 : : /* skip leading whitespace and check for # comment */
4212 heikki.linnakangas@i 149 [ + + ]: 353 : for (ptr = fline; *ptr; ptr++)
150 : : {
151 [ + + ]: 272 : if (!isspace((unsigned char) *ptr))
152 : 191 : break;
153 : : }
154 [ + + - + ]: 272 : if (*ptr == '\0' || *ptr == '#')
155 : 81 : continue;
156 : :
4149 157 : 191 : nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
158 : :
159 [ - + ]: 191 : if (nfields < 1)
160 : : {
161 : : /* expect a numeric timeline ID as first field of line */
4212 heikki.linnakangas@i 162 [ # # ]:UBC 0 : ereport(FATAL,
163 : : (errmsg("syntax error in history file: %s", fline),
164 : : errhint("Expected a numeric timeline ID.")));
165 : : }
4149 heikki.linnakangas@i 166 [ - + ]:CBC 191 : if (nfields != 3)
4149 heikki.linnakangas@i 167 [ # # ]:UBC 0 : ereport(FATAL,
168 : : (errmsg("syntax error in history file: %s", fline),
169 : : errhint("Expected a write-ahead log switchpoint location.")));
170 : :
4149 heikki.linnakangas@i 171 [ + + - + ]:CBC 191 : if (result && tli <= lasttli)
4212 heikki.linnakangas@i 172 [ # # ]:UBC 0 : ereport(FATAL,
173 : : (errmsg("invalid data in history file: %s", fline),
174 : : errhint("Timeline IDs must be in increasing sequence.")));
175 : :
4149 heikki.linnakangas@i 176 :CBC 191 : lasttli = tli;
177 : :
178 : 191 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
179 : 191 : entry->tli = tli;
180 : 191 : entry->begin = prevend;
181 : 191 : entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
182 : 191 : prevend = entry->end;
183 : :
184 : : /* Build list with newest item first */
185 : 191 : result = lcons(entry, result);
186 : :
187 : : /* we ignore the remainder of each line */
188 : : }
189 : :
4212 190 : 115 : FreeFile(fd);
191 : :
4149 192 [ + - - + ]: 115 : if (result && targetTLI <= lasttli)
4212 heikki.linnakangas@i 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 : : */
4149 heikki.linnakangas@i 201 :CBC 115 : entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
202 : 115 : entry->tli = targetTLI;
203 : 115 : entry->begin = prevend;
204 : 115 : entry->end = InvalidXLogRecPtr;
205 : :
206 : 115 : 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 : : */
4123 212 [ + + ]: 115 : if (fromArchive)
213 : 5 : KeepFileRestoredFromArchive(path, histfname);
214 : :
4212 215 : 115 : return result;
216 : : }
217 : :
218 : : /*
219 : : * Probe whether a timeline history file exists for the given timeline ID
220 : : */
221 : : bool
222 : 566 : 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 [ - + ]: 566 : if (probeTLI == 1)
4212 heikki.linnakangas@i 230 :UBC 0 : return false;
231 : :
4056 heikki.linnakangas@i 232 [ + + ]:CBC 566 : if (ArchiveRecoveryRequested)
233 : : {
4212 234 : 517 : TLHistoryFileName(histfname, probeTLI);
4164 235 : 517 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
236 : : }
237 : : else
4212 238 : 49 : TLHistoryFilePath(path, probeTLI);
239 : :
240 : 566 : fd = AllocateFile(path, "r");
241 [ + + ]: 566 : if (fd != NULL)
242 : : {
243 : 55 : FreeFile(fd);
244 : 55 : return true;
245 : : }
246 : : else
247 : : {
248 [ - + ]: 511 : if (errno != ENOENT)
4212 heikki.linnakangas@i 249 [ # # ]:UBC 0 : ereport(FATAL,
250 : : (errcode_for_file_access(),
251 : : errmsg("could not open file \"%s\": %m", path)));
4212 heikki.linnakangas@i 252 :CBC 511 : 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 : 500 : 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 : 500 : newestTLI = startTLI;
274 : :
275 : 500 : for (probeTLI = startTLI + 1;; probeTLI++)
276 : : {
277 [ + + ]: 517 : if (existsTimeLineHistory(probeTLI))
278 : : {
2489 tgl@sss.pgh.pa.us 279 : 17 : newestTLI = probeTLI; /* probeTLI exists */
280 : : }
281 : : else
282 : : {
283 : : /* doesn't exist, assume we're done */
4212 heikki.linnakangas@i 284 : 500 : break;
285 : : }
286 : : }
287 : :
288 : 500 : 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 : 46 : 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 [ - + ]: 46 : Assert(newTLI > parentTLI); /* else bad selection of newTLI */
316 : :
317 : : /*
318 : : * Write into a temp file name.
319 : : */
320 : 46 : snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
321 : :
322 : 46 : unlink(tmppath);
323 : :
324 : : /* do not use get_sync_bit() here --- want to fsync only at end of fill */
2395 peter_e@gmx.net 325 : 46 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
4212 heikki.linnakangas@i 326 [ - + ]: 46 : if (fd < 0)
4212 heikki.linnakangas@i 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 : : */
4056 heikki.linnakangas@i 334 [ + - ]:CBC 46 : if (ArchiveRecoveryRequested)
335 : : {
4212 336 : 46 : TLHistoryFileName(histfname, parentTLI);
4164 337 : 46 : RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
338 : : }
339 : : else
4212 heikki.linnakangas@i 340 :UBC 0 : TLHistoryFilePath(path, parentTLI);
341 : :
2395 peter_e@gmx.net 342 :CBC 46 : srcfd = OpenTransientFile(path, O_RDONLY);
4212 heikki.linnakangas@i 343 [ + + ]: 46 : if (srcfd < 0)
344 : : {
345 [ - + ]: 38 : if (errno != ENOENT)
4212 heikki.linnakangas@i 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 : : {
4212 heikki.linnakangas@i 355 :CBC 8 : errno = 0;
2584 rhaas@postgresql.org 356 : 16 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_READ);
4212 heikki.linnakangas@i 357 : 16 : nbytes = (int) read(srcfd, buffer, sizeof(buffer));
2584 rhaas@postgresql.org 358 : 16 : pgstat_report_wait_end();
4212 heikki.linnakangas@i 359 [ + - - + ]: 16 : if (nbytes < 0 || errno != 0)
4212 heikki.linnakangas@i 360 [ # # ]:UBC 0 : ereport(ERROR,
361 : : (errcode_for_file_access(),
362 : : errmsg("could not read file \"%s\": %m", path)));
4212 heikki.linnakangas@i 363 [ + + ]:CBC 16 : if (nbytes == 0)
364 : 8 : break;
365 : 8 : errno = 0;
2584 rhaas@postgresql.org 366 : 8 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
4212 heikki.linnakangas@i 367 [ - + ]: 8 : if ((int) write(fd, buffer, nbytes) != nbytes)
368 : : {
4212 heikki.linnakangas@i 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 : : }
2584 rhaas@postgresql.org 386 :CBC 8 : pgstat_report_wait_end();
387 : : }
388 : :
1744 peter@eisentraut.org 389 [ - + ]: 8 : if (CloseTransientFile(srcfd) != 0)
1863 michael@paquier.xyz 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 : : */
4212 heikki.linnakangas@i 401 :CBC 46 : snprintf(buffer, sizeof(buffer),
402 : : "%s%u\t%X/%X\t%s\n",
403 : : (srcfd < 0) ? "" : "\n",
404 : : parentTLI,
1146 peter@eisentraut.org 405 [ + + ]: 46 : LSN_FORMAT_ARGS(switchpoint),
406 : : reason);
407 : :
4212 heikki.linnakangas@i 408 : 46 : nbytes = strlen(buffer);
409 : 46 : errno = 0;
1437 fujii@postgresql.org 410 : 46 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_WRITE);
4212 heikki.linnakangas@i 411 [ - + ]: 46 : if ((int) write(fd, buffer, nbytes) != nbytes)
412 : : {
4212 heikki.linnakangas@i 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 : : }
1437 fujii@postgresql.org 426 :CBC 46 : pgstat_report_wait_end();
427 : :
2584 rhaas@postgresql.org 428 : 46 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_SYNC);
4212 heikki.linnakangas@i 429 [ - + ]: 46 : if (pg_fsync(fd) != 0)
1973 tmunro@postgresql.or 430 [ # # ]:UBC 0 : ereport(data_sync_elevel(ERROR),
431 : : (errcode_for_file_access(),
432 : : errmsg("could not fsync file \"%s\": %m", tmppath)));
2584 rhaas@postgresql.org 433 :CBC 46 : pgstat_report_wait_end();
434 : :
1744 peter@eisentraut.org 435 [ - + ]: 46 : if (CloseTransientFile(fd) != 0)
4212 heikki.linnakangas@i 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 : : */
4212 heikki.linnakangas@i 443 :CBC 46 : TLHistoryFilePath(path, newTLI);
649 michael@paquier.xyz 444 [ + - - + ]: 46 : Assert(access(path, F_OK) != 0 && errno == ENOENT);
445 : 46 : durable_rename(tmppath, path, ERROR);
446 : :
447 : : /* The history file can be archived immediately. */
3447 fujii@postgresql.org 448 [ + + - + : 46 : if (XLogArchivingActive())
+ + ]
449 : : {
450 : 11 : TLHistoryFileName(histfname, newTLI);
953 alvherre@alvh.no-ip. 451 : 11 : XLogArchiveNotify(histfname);
452 : : }
4212 heikki.linnakangas@i 453 : 46 : }
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
459 : : * no locking considerations. But we should be just as tense as XLogFileInit
460 : : * to avoid emplacing a bogus file.
461 : : */
462 : : void
4140 463 : 11 : writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
464 : : {
465 : : char path[MAXPGPATH];
466 : : char tmppath[MAXPGPATH];
467 : : int fd;
468 : :
469 : : /*
470 : : * Write into a temp file name.
471 : : */
472 : 11 : snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());
473 : :
474 : 11 : unlink(tmppath);
475 : :
476 : : /* do not use get_sync_bit() here --- want to fsync only at end of fill */
2395 peter_e@gmx.net 477 : 11 : fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL);
4140 heikki.linnakangas@i 478 [ - + ]: 11 : if (fd < 0)
4140 heikki.linnakangas@i 479 [ # # ]:UBC 0 : ereport(ERROR,
480 : : (errcode_for_file_access(),
481 : : errmsg("could not create file \"%s\": %m", tmppath)));
482 : :
4140 heikki.linnakangas@i 483 :CBC 11 : errno = 0;
2584 rhaas@postgresql.org 484 : 11 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE);
4140 heikki.linnakangas@i 485 [ - + ]: 11 : if ((int) write(fd, content, size) != size)
486 : : {
4140 heikki.linnakangas@i 487 :UBC 0 : int save_errno = errno;
488 : :
489 : : /*
490 : : * If we fail to make the file, delete it to release disk space
491 : : */
492 : 0 : unlink(tmppath);
493 : : /* if write didn't set errno, assume problem is no disk space */
494 [ # # ]: 0 : errno = save_errno ? save_errno : ENOSPC;
495 : :
496 [ # # ]: 0 : ereport(ERROR,
497 : : (errcode_for_file_access(),
498 : : errmsg("could not write to file \"%s\": %m", tmppath)));
499 : : }
2584 rhaas@postgresql.org 500 :CBC 11 : pgstat_report_wait_end();
501 : :
502 : 11 : pgstat_report_wait_start(WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC);
4140 heikki.linnakangas@i 503 [ - + ]: 11 : if (pg_fsync(fd) != 0)
1973 tmunro@postgresql.or 504 [ # # ]:UBC 0 : ereport(data_sync_elevel(ERROR),
505 : : (errcode_for_file_access(),
506 : : errmsg("could not fsync file \"%s\": %m", tmppath)));
2584 rhaas@postgresql.org 507 :CBC 11 : pgstat_report_wait_end();
508 : :
1744 peter@eisentraut.org 509 [ - + ]: 11 : if (CloseTransientFile(fd) != 0)
4140 heikki.linnakangas@i 510 [ # # ]:UBC 0 : ereport(ERROR,
511 : : (errcode_for_file_access(),
512 : : errmsg("could not close file \"%s\": %m", tmppath)));
513 : :
514 : : /*
515 : : * Now move the completed history file into place with its final name,
516 : : * replacing any existing file with the same name.
517 : : */
4140 heikki.linnakangas@i 518 :CBC 11 : TLHistoryFilePath(path, tli);
649 michael@paquier.xyz 519 : 11 : durable_rename(tmppath, path, ERROR);
4140 heikki.linnakangas@i 520 : 11 : }
521 : :
522 : : /*
523 : : * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
524 : : */
525 : : bool
4149 526 : 2983701 : tliInHistory(TimeLineID tli, List *expectedTLEs)
527 : : {
528 : : ListCell *cell;
529 : :
530 [ + - + - : 3007879 : foreach(cell, expectedTLEs)
+ - ]
531 : : {
532 [ + + ]: 3007879 : if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
533 : 2983701 : return true;
534 : : }
535 : :
4149 heikki.linnakangas@i 536 :UBC 0 : return false;
537 : : }
538 : :
539 : : /*
540 : : * Returns the ID of the timeline in use at a particular point in time, in
541 : : * the given timeline history.
542 : : */
543 : : TimeLineID
4149 heikki.linnakangas@i 544 :CBC 2594 : tliOfPointInHistory(XLogRecPtr ptr, List *history)
545 : : {
546 : : ListCell *cell;
547 : :
548 [ + - + - : 2618 : foreach(cell, history)
+ - ]
549 : : {
550 : 2618 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
551 : :
4125 alvherre@alvh.no-ip. 552 [ + + + + ]: 2618 : if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
553 [ + + + - ]: 2594 : (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
554 : : {
555 : : /* found it */
4149 heikki.linnakangas@i 556 : 2594 : return tle->tli;
557 : : }
558 : : }
559 : :
560 : : /* shouldn't happen. */
4149 heikki.linnakangas@i 561 [ # # ]:UBC 0 : elog(ERROR, "timeline history was not contiguous");
562 : : return 0; /* keep compiler quiet */
563 : : }
564 : :
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
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.
570 : : */
571 : : XLogRecPtr
4105 heikki.linnakangas@i 572 :CBC 1410 : tliSwitchPoint(TimeLineID tli, List *history, TimeLineID *nextTLI)
573 : : {
574 : : ListCell *cell;
575 : :
576 [ + + ]: 1410 : if (nextTLI)
577 : 1408 : *nextTLI = 0;
3973 bruce@momjian.us 578 [ + - + - : 1430 : foreach(cell, history)
+ - ]
579 : : {
4149 heikki.linnakangas@i 580 : 1430 : TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
581 : :
582 [ + + ]: 1430 : if (tle->tli == tli)
583 : 1410 : return tle->end;
4105 584 [ + + ]: 20 : if (nextTLI)
585 : 18 : *nextTLI = tle->tli;
586 : : }
587 : :
4149 heikki.linnakangas@i 588 [ # # ]:UBC 0 : ereport(ERROR,
589 : : (errmsg("requested timeline %u is not in this server's history",
590 : : tli)));
591 : : return InvalidXLogRecPtr; /* keep compiler quiet */
592 : : }
|