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