@@ -447,6 +447,7 @@ typedef struct XLogCtlData
447447
448448/* end+1 of the last record replayed (or being replayed) */
449449XLogRecPtr replayEndRecPtr ;
450+ TimeLineID replayEndTLI ;
450451/* end+1 of the last record replayed */
451452XLogRecPtr recoveryLastRecPtr ;
452453/* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
@@ -580,6 +581,7 @@ static TimeLineID lastSegmentTLI = 0;
580581
581582static XLogRecPtr minRecoveryPoint ;/* local copy of
582583 * ControlFile->minRecoveryPoint */
584+ static TimeLineID minRecoveryPointTLI ;
583585static bool updateMinRecoveryPoint = true;
584586
585587/*
@@ -1778,6 +1780,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
17781780
17791781/* update local copy */
17801782minRecoveryPoint = ControlFile -> minRecoveryPoint ;
1783+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
17811784
17821785/*
17831786 * An invalid minRecoveryPoint means that we need to recover all the WAL,
@@ -1791,6 +1794,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
17911794/* use volatile pointer to prevent code rearrangement */
17921795volatile XLogCtlData * xlogctl = XLogCtl ;
17931796XLogRecPtr newMinRecoveryPoint ;
1797+ TimeLineID newMinRecoveryPointTLI ;
17941798
17951799/*
17961800 * To avoid having to update the control file too often, we update it
@@ -1807,6 +1811,7 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
18071811 */
18081812SpinLockAcquire (& xlogctl -> info_lck );
18091813newMinRecoveryPoint = xlogctl -> replayEndRecPtr ;
1814+ newMinRecoveryPointTLI = xlogctl -> replayEndTLI ;
18101815SpinLockRelease (& xlogctl -> info_lck );
18111816
18121817if (!force && XLByteLT (newMinRecoveryPoint ,lsn ))
@@ -1820,13 +1825,16 @@ UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)
18201825if (XLByteLT (ControlFile -> minRecoveryPoint ,newMinRecoveryPoint ))
18211826{
18221827ControlFile -> minRecoveryPoint = newMinRecoveryPoint ;
1828+ ControlFile -> minRecoveryPointTLI = newMinRecoveryPointTLI ;
18231829UpdateControlFile ();
18241830minRecoveryPoint = newMinRecoveryPoint ;
1831+ minRecoveryPointTLI = newMinRecoveryPointTLI ;
18251832
18261833ereport (DEBUG2 ,
1827- (errmsg ("updated min recovery point to %X/%X" ,
1834+ (errmsg ("updated min recovery point to %X/%X on timeline %u " ,
18281835(uint32 ) (minRecoveryPoint >>32 ),
1829- (uint32 )minRecoveryPoint )));
1836+ (uint32 )minRecoveryPoint ,
1837+ newMinRecoveryPointTLI )));
18301838}
18311839}
18321840LWLockRelease (ControlFileLock );
@@ -2132,6 +2140,7 @@ XLogNeedsFlush(XLogRecPtr record)
21322140if (!LWLockConditionalAcquire (ControlFileLock ,LW_SHARED ))
21332141return true;
21342142minRecoveryPoint = ControlFile -> minRecoveryPoint ;
2143+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
21352144LWLockRelease (ControlFileLock );
21362145
21372146/*
@@ -5305,6 +5314,19 @@ StartupXLOG(void)
53055314recoveryTargetTLI ,
53065315ControlFile -> checkPointCopy .ThisTimeLineID )));
53075316
5317+ /*
5318+ * The min recovery point should be part of the requested timeline's
5319+ * history, too.
5320+ */
5321+ if (!XLogRecPtrIsInvalid (ControlFile -> minRecoveryPoint )&&
5322+ !list_member_int (expectedTLIs ,ControlFile -> minRecoveryPointTLI ))
5323+ ereport (FATAL ,
5324+ (errmsg ("requested timeline %u does not contain minimum recovery point %X/%X on timeline %u" ,
5325+ recoveryTargetTLI ,
5326+ (uint32 ) (ControlFile -> minRecoveryPoint >>32 ),
5327+ (uint32 )ControlFile -> minRecoveryPoint ,
5328+ ControlFile -> minRecoveryPointTLI )));
5329+
53085330/*
53095331 * Save the selected recovery target timeline ID and
53105332 * archive_cleanup_command in shared memory so that other processes can
@@ -5523,7 +5545,10 @@ StartupXLOG(void)
55235545{
55245546/* initialize minRecoveryPoint if not set yet */
55255547if (XLByteLT (ControlFile -> minRecoveryPoint ,checkPoint .redo ))
5548+ {
55265549ControlFile -> minRecoveryPoint = checkPoint .redo ;
5550+ ControlFile -> minRecoveryPointTLI = checkPoint .ThisTimeLineID ;
5551+ }
55275552}
55285553
55295554/*
@@ -5556,6 +5581,7 @@ StartupXLOG(void)
55565581
55575582/* initialize our local copy of minRecoveryPoint */
55585583minRecoveryPoint = ControlFile -> minRecoveryPoint ;
5584+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
55595585
55605586/*
55615587 * Reset pgstat data, because it may be invalid after recovery.
@@ -5681,6 +5707,7 @@ StartupXLOG(void)
56815707 */
56825708SpinLockAcquire (& xlogctl -> info_lck );
56835709xlogctl -> replayEndRecPtr = ReadRecPtr ;
5710+ xlogctl -> replayEndTLI = ThisTimeLineID ;
56845711xlogctl -> recoveryLastRecPtr = EndRecPtr ;
56855712xlogctl -> recoveryLastXTime = 0 ;
56865713xlogctl -> currentChunkStartTime = 0 ;
@@ -7202,6 +7229,7 @@ CreateCheckPoint(int flags)
72027229ControlFile -> time = (pg_time_t )time (NULL );
72037230/* crash recovery should always recover to the end of WAL */
72047231MemSet (& ControlFile -> minRecoveryPoint ,0 ,sizeof (XLogRecPtr ));
7232+ ControlFile -> minRecoveryPointTLI = 0 ;
72057233UpdateControlFile ();
72067234LWLockRelease (ControlFileLock );
72077235
@@ -7878,16 +7906,42 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
78787906}
78797907
78807908/*
7881- * TLI may change in a shutdown checkpoint, but it shouldn't decrease
7909+ * TLI may change in a shutdown checkpoint.
78827910 */
78837911if (checkPoint .ThisTimeLineID != ThisTimeLineID )
78847912{
7913+ /*
7914+ * The new timeline better be in the list of timelines we expect
7915+ * to see, according to the timeline history. It should also not
7916+ * decrease.
7917+ */
78857918if (checkPoint .ThisTimeLineID < ThisTimeLineID ||
78867919!list_member_int (expectedTLIs ,
78877920 (int )checkPoint .ThisTimeLineID ))
78887921ereport (PANIC ,
78897922(errmsg ("unexpected timeline ID %u (after %u) in checkpoint record" ,
78907923checkPoint .ThisTimeLineID ,ThisTimeLineID )));
7924+
7925+ /*
7926+ * If we have not yet reached min recovery point, and we're about
7927+ * to switch to a timeline greater than the timeline of the min
7928+ * recovery point: trouble. After switching to the new timeline,
7929+ * we could not possibly visit the min recovery point on the
7930+ * correct timeline anymore. This can happen if there is a newer
7931+ * timeline in the archive that branched before the timeline the
7932+ * min recovery point is on, and you attempt to do PITR to the
7933+ * new timeline.
7934+ */
7935+ if (!XLogRecPtrIsInvalid (minRecoveryPoint )&&
7936+ XLByteLT (lsn ,minRecoveryPoint )&&
7937+ checkPoint .ThisTimeLineID > minRecoveryPointTLI )
7938+ ereport (PANIC ,
7939+ (errmsg ("unexpected timeline ID %u in checkpoint record, before reaching minimum recovery point %X/%X on timeline %u" ,
7940+ checkPoint .ThisTimeLineID ,
7941+ (uint32 ) (minRecoveryPoint >>32 ),
7942+ (uint32 )minRecoveryPoint ,
7943+ minRecoveryPointTLI )));
7944+
78917945/* Following WAL records should be run with new TLI */
78927946ThisTimeLineID = checkPoint .ThisTimeLineID ;
78937947}
@@ -7972,7 +8026,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
79728026LWLockAcquire (ControlFileLock ,LW_EXCLUSIVE );
79738027
79748028if (XLByteLT (ControlFile -> minRecoveryPoint ,lsn ))
8029+ {
79758030ControlFile -> minRecoveryPoint = lsn ;
8031+ ControlFile -> minRecoveryPointTLI = ThisTimeLineID ;
8032+ }
79768033MemSet (& ControlFile -> backupStartPoint ,0 ,sizeof (XLogRecPtr ));
79778034ControlFile -> backupEndRequired = false;
79788035UpdateControlFile ();
@@ -8002,9 +8059,11 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
80028059 * decreasing max_* settings.
80038060 */
80048061minRecoveryPoint = ControlFile -> minRecoveryPoint ;
8062+ minRecoveryPointTLI = ControlFile -> minRecoveryPointTLI ;
80058063if (minRecoveryPoint != 0 && XLByteLT (minRecoveryPoint ,lsn ))
80068064{
80078065ControlFile -> minRecoveryPoint = lsn ;
8066+ ControlFile -> minRecoveryPointTLI = ThisTimeLineID ;
80088067}
80098068
80108069UpdateControlFile ();