Branch data 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 "common/pg_parse_lsn.h"
14 : : #include "pg_rewind.h"
15 : :
16 : : /*
17 : : * This is copy-pasted from the backend readTimeLineHistory, modified to
18 : : * return a malloc'd array and to work without backend functions.
19 : : */
20 : : /*
21 : : * Try to read a timeline's history file.
22 : : *
23 : : * If successful, return the list of component TLIs (the given TLI followed by
24 : : * its ancestor TLIs). If we can't find the history file, assume that the
25 : : * timeline has no parents, and return a list of just the specified timeline
26 : : * ID.
27 : : */
28 : : TimeLineHistoryEntry *
29 : 18 : rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
30 : : {
31 : : char *fline;
32 : : TimeLineHistoryEntry *entry;
33 : 18 : TimeLineHistoryEntry *entries = NULL;
34 : 18 : int nlines = 0;
35 : 18 : TimeLineID lasttli = 0;
36 : : XLogRecPtr prevend;
37 : : char *bufptr;
38 : 18 : bool lastline = false;
39 : :
40 : : /*
41 : : * Parse the file...
42 : : */
43 : 18 : prevend = InvalidXLogRecPtr;
44 : 18 : bufptr = buffer;
45 [ + + ]: 56 : while (!lastline)
46 : : {
47 : : char *ptr;
48 : : char *token_end;
49 : : char save;
50 : : TimeLineID tli;
51 : : XLogRecPtr switchpoint;
52 : 38 : bool valid_switchpoint = false;
53 : : int nchars;
54 : : size_t nspaces;
55 : :
56 : 38 : fline = bufptr;
57 [ + + + + ]: 817 : while (*bufptr && *bufptr != '\n')
58 : 779 : bufptr++;
59 [ + + ]: 38 : if (!(*bufptr))
60 : 18 : lastline = true;
61 : : else
62 : 20 : *bufptr++ = '\0';
63 : :
64 : : /* skip leading whitespace and check for # comment */
65 [ + + ]: 38 : for (ptr = fline; *ptr; ptr++)
66 : : {
67 [ + - ]: 19 : if (!isspace((unsigned char) *ptr))
68 : 19 : break;
69 : : }
70 [ + + - + ]: 38 : if (*ptr == '\0' || *ptr == '#')
71 : 19 : continue;
72 : :
73 [ - + ]: 19 : if (sscanf(fline, "%u%n", &tli, &nchars) != 1)
74 : : {
75 : : /* expect a numeric timeline ID as first field of line */
76 : 0 : pg_log_error("syntax error in history file: %s", fline);
77 : 0 : pg_log_error_detail("Expected a numeric timeline ID.");
78 : 0 : exit(1);
79 : : }
80 : :
81 : : /* the switchpoint location follows, separated by whitespace */
82 : 19 : ptr = fline + nchars;
83 : 19 : nspaces = strspn(ptr, " \t\n\r\f\v");
84 [ + - ]: 19 : if (nspaces > 0)
85 : : {
86 : 19 : ptr += nspaces;
87 : :
88 : : /*
89 : : * isolate the location from the rest of the line before parsing
90 : : * it
91 : : */
92 : 19 : token_end = ptr + strcspn(ptr, " \t\n\r\f\v");
93 : 19 : save = *token_end;
94 : 19 : *token_end = '\0';
95 : 19 : valid_switchpoint = pg_parse_lsn(ptr, &switchpoint);
96 : 19 : *token_end = save;
97 : : }
98 : :
99 [ - + ]: 19 : if (!valid_switchpoint)
100 : : {
101 : 0 : pg_log_error("syntax error in history file: %s", fline);
102 : 0 : pg_log_error_detail("Expected a write-ahead log switchpoint location.");
103 : 0 : exit(1);
104 : : }
105 [ + + - + ]: 19 : if (entries && tli <= lasttli)
106 : : {
107 : 0 : pg_log_error("invalid data in history file: %s", fline);
108 : 0 : pg_log_error_detail("Timeline IDs must be in increasing sequence.");
109 : 0 : exit(1);
110 : : }
111 : :
112 : 19 : lasttli = tli;
113 : :
114 : 19 : nlines++;
115 : 19 : entries = pg_realloc_array(entries, TimeLineHistoryEntry, nlines);
116 : :
117 : 19 : entry = &entries[nlines - 1];
118 : 19 : entry->tli = tli;
119 : 19 : entry->begin = prevend;
120 : 19 : entry->end = switchpoint;
121 : 19 : prevend = entry->end;
122 : :
123 : : /* we ignore the remainder of each line */
124 : : }
125 : :
126 [ + - - + ]: 18 : if (entries && targetTLI <= lasttli)
127 : : {
128 : 0 : pg_log_error("invalid data in history file");
129 : 0 : pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
130 : 0 : exit(1);
131 : : }
132 : :
133 : : /*
134 : : * Create one more entry for the "tip" of the timeline, which has no entry
135 : : * in the history file.
136 : : */
137 : 18 : nlines++;
138 [ + - ]: 18 : if (entries)
139 : 18 : entries = pg_realloc_array(entries, TimeLineHistoryEntry, nlines);
140 : : else
141 : 0 : entries = pg_malloc_array(TimeLineHistoryEntry, 1);
142 : :
143 : 18 : entry = &entries[nlines - 1];
144 : 18 : entry->tli = targetTLI;
145 : 18 : entry->begin = prevend;
146 : 18 : entry->end = InvalidXLogRecPtr;
147 : :
148 : 18 : *nentries = nlines;
149 : 18 : return entries;
150 : : }
|