1212 *
1313 * Each line in the file represents a timeline switch:
1414 *
15- * <parentTLI> <xlogfname > <reason>
15+ * <parentTLI> <switchpoint > <reason>
1616 *
1717 * parentTLI ID of the parent timeline
18- * xlogfname filename of the WAL segment where the switch happened
18+ * switchpoint XLogRecPtr of the WAL position where the switch happened
1919 * reason human-readable explanation of why the timeline was changed
2020 *
2121 * The fields are separated by tabs. Lines beginning with # are comments, and
@@ -56,10 +56,18 @@ readTimeLineHistory(TimeLineID targetTLI)
5656 char histfname [MAXFNAMELEN ];
5757 char fline [MAXPGPATH ];
5858 FILE * fd ;
59+ TimeLineHistoryEntry * entry ;
60+ TimeLineID lasttli = 0 ;
61+ XLogRecPtr prevend ;
5962
6063 /* Timeline 1 does not have a history file, so no need to check */
6164 if (targetTLI == 1 )
62- return list_make1_int ((int ) targetTLI );
65+ {
66+ entry = (TimeLineHistoryEntry * ) palloc (sizeof (TimeLineHistoryEntry ));
67+ entry -> tli = targetTLI ;
68+ entry -> begin = entry -> end = InvalidXLogRecPtr ;
69+ return list_make1 (entry );
70+ }
6371
6472 if (InArchiveRecovery )
6573 {
@@ -77,20 +85,26 @@ readTimeLineHistory(TimeLineID targetTLI)
7785 (errcode_for_file_access (),
7886 errmsg ("could not open file \"%s\": %m" , path )));
7987 /* Not there, so assume no parents */
80- return list_make1_int ((int ) targetTLI );
88+ entry = (TimeLineHistoryEntry * ) palloc (sizeof (TimeLineHistoryEntry ));
89+ entry -> tli = targetTLI ;
90+ entry -> begin = entry -> end = InvalidXLogRecPtr ;
91+ return list_make1 (entry );
8192 }
8293
8394 result = NIL ;
8495
8596 /*
8697 * Parse the file...
8798 */
99+ prevend = InvalidXLogRecPtr ;
88100 while (fgets (fline , sizeof (fline ), fd ) != NULL )
89101 {
90102 /* skip leading whitespace and check for # comment */
91103 char * ptr ;
92- char * endptr ;
93104 TimeLineID tli ;
105+ uint32 switchpoint_hi ;
106+ uint32 switchpoint_lo ;
107+ int nfields ;
94108
95109 for (ptr = fline ; * ptr ; ptr ++ )
96110 {
@@ -100,38 +114,56 @@ readTimeLineHistory(TimeLineID targetTLI)
100114 if (* ptr == '\0' || * ptr == '#' )
101115 continue ;
102116
103- /* expect a numeric timeline ID as first field of line */
104- tli = (TimeLineID ) strtoul (ptr , & endptr , 0 );
105- if (endptr == ptr )
117+ nfields = sscanf (fline , "%u\t%X/%X" , & tli , & switchpoint_hi , & switchpoint_lo );
118+
119+ if (nfields < 1 )
120+ {
121+ /* expect a numeric timeline ID as first field of line */
106122 ereport (FATAL ,
107123 (errmsg ("syntax error in history file: %s" , fline ),
108124 errhint ("Expected a numeric timeline ID." )));
125+ }
126+ if (nfields != 3 )
127+ ereport (FATAL ,
128+ (errmsg ("syntax error in history file: %s" , fline ),
129+ errhint ("Expected an XLOG switchpoint location." )));
109130
110- if (result &&
111- tli <= (TimeLineID ) linitial_int (result ))
131+ if (result && tli <= lasttli )
112132 ereport (FATAL ,
113133 (errmsg ("invalid data in history file: %s" , fline ),
114134 errhint ("Timeline IDs must be in increasing sequence." )));
115135
136+ lasttli = tli ;
137+
138+ entry = (TimeLineHistoryEntry * ) palloc (sizeof (TimeLineHistoryEntry ));
139+ entry -> tli = tli ;
140+ entry -> begin = prevend ;
141+ entry -> end = ((uint64 ) (switchpoint_hi )) << 32 | (uint64 ) switchpoint_lo ;
142+ prevend = entry -> end ;
143+
116144 /* Build list with newest item first */
117- result = lcons_int (( int ) tli , result );
145+ result = lcons ( entry , result );
118146
119147 /* we ignore the remainder of each line */
120148 }
121149
122150 FreeFile (fd );
123151
124- if (result &&
125- targetTLI <= (TimeLineID ) linitial_int (result ))
152+ if (result && targetTLI <= lasttli )
126153 ereport (FATAL ,
127154 (errmsg ("invalid data in history file \"%s\"" , path ),
128155 errhint ("Timeline IDs must be less than child timeline's ID." )));
129156
130- result = lcons_int ((int ) targetTLI , result );
157+ /*
158+ * Create one more entry for the "tip" of the timeline, which has no
159+ * entry in the history file.
160+ */
161+ entry = (TimeLineHistoryEntry * ) palloc (sizeof (TimeLineHistoryEntry ));
162+ entry -> tli = targetTLI ;
163+ entry -> begin = prevend ;
164+ entry -> end = InvalidXLogRecPtr ;
131165
132- ereport (DEBUG3 ,
133- (errmsg_internal ("history of timeline %u is %s" ,
134- targetTLI , nodeToString (result ))));
166+ result = lcons (entry , result );
135167
136168 return result ;
137169}
@@ -214,7 +246,7 @@ findNewestTimeLine(TimeLineID startTLI)
214246 *
215247 * newTLI: ID of the new timeline
216248 * parentTLI: ID of its immediate parent
217- * endTLI et al: ID of the last used WAL file, for annotation purposes
249+ * switchpoint: XLOG position where the system switched to the new timeline
218250 * reason: human-readable explanation of why the timeline was switched
219251 *
220252 * Currently this is only used at the end recovery, and so there are no locking
@@ -223,12 +255,11 @@ findNewestTimeLine(TimeLineID startTLI)
223255 */
224256void
225257writeTimeLineHistory (TimeLineID newTLI , TimeLineID parentTLI ,
226- TimeLineID endTLI , XLogSegNo endLogSegNo , char * reason )
258+ XLogRecPtr switchpoint , char * reason )
227259{
228260 char path [MAXPGPATH ];
229261 char tmppath [MAXPGPATH ];
230262 char histfname [MAXFNAMELEN ];
231- char xlogfname [MAXFNAMELEN ];
232263 char buffer [BLCKSZ ];
233264 int srcfd ;
234265 int fd ;
@@ -313,13 +344,11 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
313344 * If we did have a parent file, insert an extra newline just in case the
314345 * parent file failed to end with one.
315346 */
316- XLogFileName (xlogfname , endTLI , endLogSegNo );
317-
318347 snprintf (buffer , sizeof (buffer ),
319- "%s%u\t%s \t%s\n" ,
348+ "%s%u\t%X/%X \t%s\n" ,
320349 (srcfd < 0 ) ? "" : "\n" ,
321350 parentTLI ,
322- xlogfname ,
351+ ( uint32 ) ( switchpoint >> 32 ), ( uint32 ) ( switchpoint ) ,
323352 reason );
324353
325354 nbytes = strlen (buffer );
@@ -380,3 +409,70 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
380409 TLHistoryFileName (histfname , newTLI );
381410 XLogArchiveNotify (histfname );
382411}
412+
413+ /*
414+ * Returns true if 'expectedTLEs' contains a timeline with id 'tli'
415+ */
416+ bool
417+ tliInHistory (TimeLineID tli , List * expectedTLEs )
418+ {
419+ ListCell * cell ;
420+
421+ foreach (cell , expectedTLEs )
422+ {
423+ if (((TimeLineHistoryEntry * ) lfirst (cell ))-> tli == tli )
424+ return true;
425+ }
426+
427+ return false;
428+ }
429+
430+ /*
431+ * Returns the ID of the timeline in use at a particular point in time, in
432+ * the given timeline history.
433+ */
434+ TimeLineID
435+ tliOfPointInHistory (XLogRecPtr ptr , List * history )
436+ {
437+ ListCell * cell ;
438+
439+ foreach (cell , history )
440+ {
441+ TimeLineHistoryEntry * tle = (TimeLineHistoryEntry * ) lfirst (cell );
442+ if ((XLogRecPtrIsInvalid (tle -> begin ) || XLByteLE (tle -> begin , ptr )) &&
443+ (XLogRecPtrIsInvalid (tle -> end ) || XLByteLT (ptr , tle -> end )))
444+ {
445+ /* found it */
446+ return tle -> tli ;
447+ }
448+ }
449+
450+ /* shouldn't happen. */
451+ elog (ERROR , "timeline history was not contiguous" );
452+ return 0 ; /* keep compiler quiet */
453+ }
454+
455+ /*
456+ * Returns the point in history where we branched off the given timeline.
457+ * Returns InvalidXLogRecPtr if the timeline is current (= we have not
458+ * branched off from it), and throws an error if the timeline is not part of
459+ * this server's history.
460+ */
461+ XLogRecPtr
462+ tliSwitchPoint (TimeLineID tli , List * history )
463+ {
464+ ListCell * cell ;
465+
466+ foreach (cell , history )
467+ {
468+ TimeLineHistoryEntry * tle = (TimeLineHistoryEntry * ) lfirst (cell );
469+
470+ if (tle -> tli == tli )
471+ return tle -> end ;
472+ }
473+
474+ ereport (ERROR ,
475+ (errmsg ("requested timeline %u is not in this server's history" ,
476+ tli )));
477+ return InvalidXLogRecPtr ; /* keep compiler quiet */
478+ }
0 commit comments