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 16 : rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
29 : {
30 : char *fline;
31 : TimeLineHistoryEntry *entry;
32 16 : TimeLineHistoryEntry *entries = NULL;
33 16 : int nlines = 0;
34 16 : TimeLineID lasttli = 0;
35 : XLogRecPtr prevend;
36 : char *bufptr;
37 16 : bool lastline = false;
38 :
39 : /*
40 : * Parse the file...
41 : */
42 16 : prevend = InvalidXLogRecPtr;
43 16 : bufptr = buffer;
44 50 : while (!lastline)
45 : {
46 : char *ptr;
47 : TimeLineID tli;
48 : uint32 switchpoint_hi;
49 : uint32 switchpoint_lo;
50 : int nfields;
51 :
52 34 : fline = bufptr;
53 731 : while (*bufptr && *bufptr != '\n')
54 697 : bufptr++;
55 34 : if (!(*bufptr))
56 16 : lastline = true;
57 : else
58 18 : *bufptr++ = '\0';
59 :
60 : /* skip leading whitespace and check for # comment */
61 34 : for (ptr = fline; *ptr; ptr++)
62 : {
63 17 : if (!isspace((unsigned char) *ptr))
64 17 : break;
65 : }
66 34 : if (*ptr == '\0' || *ptr == '#')
67 17 : continue;
68 :
69 17 : nfields = sscanf(fline, "%u\t%X/%08X", &tli, &switchpoint_hi, &switchpoint_lo);
70 :
71 17 : 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 17 : 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 17 : 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 17 : lasttli = tli;
92 :
93 17 : nlines++;
94 17 : entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
95 :
96 17 : entry = &entries[nlines - 1];
97 17 : entry->tli = tli;
98 17 : entry->begin = prevend;
99 17 : entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
100 17 : prevend = entry->end;
101 :
102 : /* we ignore the remainder of each line */
103 : }
104 :
105 16 : 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 16 : nlines++;
117 16 : if (entries)
118 16 : entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
119 : else
120 0 : entries = pg_malloc(1 * sizeof(TimeLineHistoryEntry));
121 :
122 16 : entry = &entries[nlines - 1];
123 16 : entry->tli = targetTLI;
124 16 : entry->begin = prevend;
125 16 : entry->end = InvalidXLogRecPtr;
126 :
127 16 : *nentries = nlines;
128 16 : return entries;
129 : }
|