Age Owner Branch data TLA 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 *
4175 heikki.linnakangas@i 29 :CBC 16 : rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
30 : : {
31 : : char *fline;
32 : : TimeLineHistoryEntry *entry;
33 : 16 : TimeLineHistoryEntry *entries = NULL;
34 : 16 : int nlines = 0;
35 : 16 : TimeLineID lasttli = 0;
36 : : XLogRecPtr prevend;
37 : : char *bufptr;
38 : 16 : bool lastline = false;
39 : :
40 : : /*
41 : : * Parse the file...
42 : : */
43 : 16 : prevend = InvalidXLogRecPtr;
44 : 16 : bufptr = buffer;
45 [ + + ]: 50 : while (!lastline)
46 : : {
47 : : char *ptr;
48 : : char *token_end;
49 : : char save;
50 : : TimeLineID tli;
51 : : XLogRecPtr switchpoint;
6 fujii@postgresql.org 52 :GNC 34 : bool valid_switchpoint = false;
53 : : int nchars;
54 : : size_t nspaces;
55 : :
4175 heikki.linnakangas@i 56 :CBC 34 : fline = bufptr;
57 [ + + + + ]: 731 : while (*bufptr && *bufptr != '\n')
58 : 697 : bufptr++;
59 [ + + ]: 34 : if (!(*bufptr))
60 : 16 : lastline = true;
61 : : else
62 : 18 : *bufptr++ = '\0';
63 : :
64 : : /* skip leading whitespace and check for # comment */
65 [ + + ]: 34 : for (ptr = fline; *ptr; ptr++)
66 : : {
67 [ + - ]: 17 : if (!isspace((unsigned char) *ptr))
68 : 17 : break;
69 : : }
70 [ + + - + ]: 34 : if (*ptr == '\0' || *ptr == '#')
71 : 17 : continue;
72 : :
6 fujii@postgresql.org 73 [ - + ]:GNC 17 : if (sscanf(fline, "%u%n", &tli, &nchars) != 1)
74 : : {
75 : : /* expect a numeric timeline ID as first field of line */
2705 peter@eisentraut.org 76 :UBC 0 : pg_log_error("syntax error in history file: %s", fline);
1602 tgl@sss.pgh.pa.us 77 : 0 : pg_log_error_detail("Expected a numeric timeline ID.");
4175 heikki.linnakangas@i 78 : 0 : exit(1);
79 : : }
80 : :
81 : : /* the switchpoint location follows, separated by whitespace */
6 fujii@postgresql.org 82 :GNC 17 : ptr = fline + nchars;
83 : 17 : nspaces = strspn(ptr, " \t\n\r\f\v");
84 [ + - ]: 17 : if (nspaces > 0)
85 : : {
86 : 17 : ptr += nspaces;
87 : :
88 : : /*
89 : : * isolate the location from the rest of the line before parsing
90 : : * it
91 : : */
92 : 17 : token_end = ptr + strcspn(ptr, " \t\n\r\f\v");
93 : 17 : save = *token_end;
94 : 17 : *token_end = '\0';
95 : 17 : valid_switchpoint = pg_parse_lsn(ptr, &switchpoint);
96 : 17 : *token_end = save;
97 : : }
98 : :
99 [ - + ]: 17 : if (!valid_switchpoint)
100 : : {
2705 peter@eisentraut.org 101 :UBC 0 : pg_log_error("syntax error in history file: %s", fline);
1602 tgl@sss.pgh.pa.us 102 : 0 : pg_log_error_detail("Expected a write-ahead log switchpoint location.");
4175 heikki.linnakangas@i 103 : 0 : exit(1);
104 : : }
4175 heikki.linnakangas@i 105 [ + + - + ]:CBC 17 : if (entries && tli <= lasttli)
106 : : {
2705 peter@eisentraut.org 107 :UBC 0 : pg_log_error("invalid data in history file: %s", fline);
1602 tgl@sss.pgh.pa.us 108 : 0 : pg_log_error_detail("Timeline IDs must be in increasing sequence.");
4175 heikki.linnakangas@i 109 : 0 : exit(1);
110 : : }
111 : :
4175 heikki.linnakangas@i 112 :CBC 17 : lasttli = tli;
113 : :
114 : 17 : nlines++;
181 michael@paquier.xyz 115 : 17 : entries = pg_realloc_array(entries, TimeLineHistoryEntry, nlines);
116 : :
4175 heikki.linnakangas@i 117 : 17 : entry = &entries[nlines - 1];
118 : 17 : entry->tli = tli;
119 : 17 : entry->begin = prevend;
6 fujii@postgresql.org 120 :GNC 17 : entry->end = switchpoint;
4175 heikki.linnakangas@i 121 :CBC 17 : prevend = entry->end;
122 : :
123 : : /* we ignore the remainder of each line */
124 : : }
125 : :
126 [ + - - + ]: 16 : if (entries && targetTLI <= lasttli)
127 : : {
2705 peter@eisentraut.org 128 :UBC 0 : pg_log_error("invalid data in history file");
1602 tgl@sss.pgh.pa.us 129 : 0 : pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
4175 heikki.linnakangas@i 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 : : */
4175 heikki.linnakangas@i 137 :CBC 16 : nlines++;
138 [ + - ]: 16 : if (entries)
181 michael@paquier.xyz 139 : 16 : entries = pg_realloc_array(entries, TimeLineHistoryEntry, nlines);
140 : : else
181 michael@paquier.xyz 141 :UBC 0 : entries = pg_malloc_array(TimeLineHistoryEntry, 1);
142 : :
4175 heikki.linnakangas@i 143 :CBC 16 : entry = &entries[nlines - 1];
144 : 16 : entry->tli = targetTLI;
145 : 16 : entry->begin = prevend;
146 : 16 : entry->end = InvalidXLogRecPtr;
147 : :
148 : 16 : *nentries = nlines;
149 : 16 : return entries;
150 : : }
|