Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * timeline.c
4 : * timeline-related functions.
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : *
8 : *-------------------------------------------------------------------------
9 : */
10 : #include "postgres_fe.h"
11 :
12 : #include "access/timeline.h"
13 : #include "pg_rewind.h"
14 :
15 : /*
16 : * This is copy-pasted from the backend readTimeLineHistory, modified to
17 : * return a malloc'd array and to work without backend functions.
18 : */
19 : /*
20 : * Try to read a timeline's history file.
21 : *
22 : * If successful, return the list of component TLIs (the given TLI followed by
23 : * its ancestor TLIs). If we can't find the history file, assume that the
24 : * timeline has no parents, and return a list of just the specified timeline
25 : * ID.
26 : */
27 : TimeLineHistoryEntry *
28 0 : rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
29 : {
30 0 : char *fline;
31 0 : TimeLineHistoryEntry *entry;
32 0 : TimeLineHistoryEntry *entries = NULL;
33 0 : int nlines = 0;
34 0 : TimeLineID lasttli = 0;
35 0 : XLogRecPtr prevend;
36 0 : char *bufptr;
37 0 : bool lastline = false;
38 :
39 : /*
40 : * Parse the file...
41 : */
42 0 : prevend = InvalidXLogRecPtr;
43 0 : bufptr = buffer;
44 0 : while (!lastline)
45 : {
46 0 : char *ptr;
47 0 : TimeLineID tli;
48 0 : uint32 switchpoint_hi;
49 0 : uint32 switchpoint_lo;
50 0 : int nfields;
51 :
52 0 : fline = bufptr;
53 0 : while (*bufptr && *bufptr != '\n')
54 0 : bufptr++;
55 0 : if (!(*bufptr))
56 0 : lastline = true;
57 : else
58 0 : *bufptr++ = '\0';
59 :
60 : /* skip leading whitespace and check for # comment */
61 0 : for (ptr = fline; *ptr; ptr++)
62 : {
63 0 : if (!isspace((unsigned char) *ptr))
64 0 : break;
65 0 : }
66 0 : if (*ptr == '\0' || *ptr == '#')
67 0 : continue;
68 :
69 0 : nfields = sscanf(fline, "%u\t%X/%08X", &tli, &switchpoint_hi, &switchpoint_lo);
70 :
71 0 : if (nfields < 1)
72 : {
73 : /* expect a numeric timeline ID as first field of line */
74 0 : pg_log_error("syntax error in history file: %s", fline);
75 0 : pg_log_error_detail("Expected a numeric timeline ID.");
76 0 : exit(1);
77 : }
78 0 : if (nfields != 3)
79 : {
80 0 : pg_log_error("syntax error in history file: %s", fline);
81 0 : pg_log_error_detail("Expected a write-ahead log switchpoint location.");
82 0 : exit(1);
83 : }
84 0 : if (entries && tli <= lasttli)
85 : {
86 0 : pg_log_error("invalid data in history file: %s", fline);
87 0 : pg_log_error_detail("Timeline IDs must be in increasing sequence.");
88 0 : exit(1);
89 : }
90 :
91 0 : lasttli = tli;
92 :
93 0 : nlines++;
94 0 : entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
95 :
96 0 : entry = &entries[nlines - 1];
97 0 : entry->tli = tli;
98 0 : entry->begin = prevend;
99 0 : entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
100 0 : prevend = entry->end;
101 :
102 : /* we ignore the remainder of each line */
103 0 : }
104 :
105 0 : if (entries && targetTLI <= lasttli)
106 : {
107 0 : pg_log_error("invalid data in history file");
108 0 : pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
109 0 : exit(1);
110 : }
111 :
112 : /*
113 : * Create one more entry for the "tip" of the timeline, which has no entry
114 : * in the history file.
115 : */
116 0 : nlines++;
117 0 : if (entries)
118 0 : entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
119 : else
120 0 : entries = pg_malloc(1 * sizeof(TimeLineHistoryEntry));
121 :
122 0 : entry = &entries[nlines - 1];
123 0 : entry->tli = targetTLI;
124 0 : entry->begin = prevend;
125 0 : entry->end = InvalidXLogRecPtr;
126 :
127 0 : *nentries = nlines;
128 0 : return entries;
129 0 : }
|