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