diff options
Diffstat (limited to '')
-rw-r--r-- | src/bin/pg_rewind/timeline.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c new file mode 100644 index 0000000..6756c5d --- /dev/null +++ b/src/bin/pg_rewind/timeline.c @@ -0,0 +1,130 @@ +/*------------------------------------------------------------------------- + * + * timeline.c + * timeline-related functions. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "access/timeline.h" +#include "access/xlog_internal.h" +#include "pg_rewind.h" + +/* + * This is copy-pasted from the backend readTimeLineHistory, modified to + * return a malloc'd array and to work without backend functions. + */ +/* + * Try to read a timeline's history file. + * + * If successful, return the list of component TLIs (the given TLI followed by + * its ancestor TLIs). If we can't find the history file, assume that the + * timeline has no parents, and return a list of just the specified timeline + * ID. + */ +TimeLineHistoryEntry * +rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries) +{ + char *fline; + TimeLineHistoryEntry *entry; + TimeLineHistoryEntry *entries = NULL; + int nlines = 0; + TimeLineID lasttli = 0; + XLogRecPtr prevend; + char *bufptr; + bool lastline = false; + + /* + * Parse the file... + */ + prevend = InvalidXLogRecPtr; + bufptr = buffer; + while (!lastline) + { + char *ptr; + TimeLineID tli; + uint32 switchpoint_hi; + uint32 switchpoint_lo; + int nfields; + + fline = bufptr; + while (*bufptr && *bufptr != '\n') + bufptr++; + if (!(*bufptr)) + lastline = true; + else + *bufptr++ = '\0'; + + /* skip leading whitespace and check for # comment */ + for (ptr = fline; *ptr; ptr++) + { + if (!isspace((unsigned char) *ptr)) + break; + } + if (*ptr == '\0' || *ptr == '#') + continue; + + nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo); + + if (nfields < 1) + { + /* expect a numeric timeline ID as first field of line */ + pg_log_error("syntax error in history file: %s", fline); + pg_log_error("Expected a numeric timeline ID."); + exit(1); + } + if (nfields != 3) + { + pg_log_error("syntax error in history file: %s", fline); + pg_log_error("Expected a write-ahead log switchpoint location."); + exit(1); + } + if (entries && tli <= lasttli) + { + pg_log_error("invalid data in history file: %s", fline); + pg_log_error("Timeline IDs must be in increasing sequence."); + exit(1); + } + + lasttli = tli; + + nlines++; + entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry)); + + entry = &entries[nlines - 1]; + entry->tli = tli; + entry->begin = prevend; + entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo; + prevend = entry->end; + + /* we ignore the remainder of each line */ + } + + if (entries && targetTLI <= lasttli) + { + pg_log_error("invalid data in history file"); + pg_log_error("Timeline IDs must be less than child timeline's ID."); + exit(1); + } + + /* + * Create one more entry for the "tip" of the timeline, which has no entry + * in the history file. + */ + nlines++; + if (entries) + entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry)); + else + entries = pg_malloc(1 * sizeof(TimeLineHistoryEntry)); + + entry = &entries[nlines - 1]; + entry->tli = targetTLI; + entry->begin = prevend; + entry->end = InvalidXLogRecPtr; + + *nentries = nlines; + return entries; +} |