1212 *
1313 * Each line in the file represents a timeline switch:
1414 *
15- * <parentTLI> <xlogfname > <reason>
15+ * <parentTLI> <switchpoint > <reason>
1616 *
1717 *parentTLIID of the parent timeline
18- *xlogfnamefilename of the WALsegment where the switch happened
18+ *switchpointXLogRecPtr of the WALposition where the switch happened
1919 *reasonhuman-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)
5656char histfname [MAXFNAMELEN ];
5757char fline [MAXPGPATH ];
5858FILE * 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 */
6164if (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
6472if (InArchiveRecovery )
6573{
@@ -77,20 +85,26 @@ readTimeLineHistory(TimeLineID targetTLI)
7785(errcode_for_file_access (),
7886errmsg ("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
8394result = NIL ;
8495
8596/*
8697 * Parse the file...
8798 */
99+ prevend = InvalidXLogRecPtr ;
88100while (fgets (fline ,sizeof (fline ),fd )!= NULL )
89101{
90102/* skip leading whitespace and check for # comment */
91103char * ptr ;
92- char * endptr ;
93104TimeLineID tli ;
105+ uint32 switchpoint_hi ;
106+ uint32 switchpoint_lo ;
107+ int nfields ;
94108
95109for (ptr = fline ;* ptr ;ptr ++ )
96110{
@@ -100,38 +114,56 @@ readTimeLineHistory(TimeLineID targetTLI)
100114if (* ptr == '\0' || * ptr == '#' )
101115continue ;
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 */
106122ereport (FATAL ,
107123(errmsg ("syntax error in history file: %s" ,fline ),
108124errhint ("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 )
112132ereport (FATAL ,
113133(errmsg ("invalid data in history file: %s" ,fline ),
114134errhint ("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
122150FreeFile (fd );
123151
124- if (result &&
125- targetTLI <= (TimeLineID )linitial_int (result ))
152+ if (result && targetTLI <=lasttli )
126153ereport (FATAL ,
127154(errmsg ("invalid data in history file \"%s\"" ,path ),
128155errhint ("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
136168return 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 thelast used WAL file, for annotation purposes
249+ *switchpoint: XLOG position where thesystem 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{
228260char path [MAXPGPATH ];
229261char tmppath [MAXPGPATH ];
230262char histfname [MAXFNAMELEN ];
231- char xlogfname [MAXFNAMELEN ];
232263char buffer [BLCKSZ ];
233264int srcfd ;
234265int 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-
318347snprintf (buffer ,sizeof (buffer ),
319- "%s%u\t%s \t%s\n" ,
348+ "%s%u\t%X/%X \t%s\n" ,
320349 (srcfd < 0 ) ?"" :"\n" ,
321350parentTLI ,
322- xlogfname ,
351+ ( uint32 ) ( switchpoint >> 32 ), ( uint32 ) ( switchpoint ) ,
323352reason );
324353
325354nbytes = strlen (buffer );
@@ -380,3 +409,70 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
380409TLHistoryFileName (histfname ,newTLI );
381410XLogArchiveNotify (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+ }