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