6767#include "utils/builtins.h"
6868#include "utils/guc.h"
6969#include "utils/memutils.h"
70+ #include "utils/pg_lsn.h"
7071#include "utils/ps_status.h"
7172#include "utils/relmapper.h"
7273#include "utils/snapmgr.h"
@@ -254,6 +255,7 @@ static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
254255static TransactionId recoveryTargetXid ;
255256static TimestampTz recoveryTargetTime ;
256257static char * recoveryTargetName ;
258+ static XLogRecPtr recoveryTargetLSN ;
257259static int recovery_min_apply_delay = 0 ;
258260static TimestampTz recoveryDelayUntilTime ;
259261
@@ -275,6 +277,7 @@ static bool fast_promote = false;
275277 */
276278static TransactionId recoveryStopXid ;
277279static TimestampTz recoveryStopTime ;
280+ static XLogRecPtr recoveryStopLSN ;
278281static char recoveryStopName [MAXFNAMELEN ];
279282static bool recoveryStopAfter ;
280283
@@ -5078,6 +5081,23 @@ readRecoveryCommandFile(void)
50785081(errmsg_internal ("recovery_target_name = '%s'" ,
50795082recoveryTargetName )));
50805083}
5084+ else if (strcmp (item -> name ,"recovery_target_lsn" )== 0 )
5085+ {
5086+ recoveryTarget = RECOVERY_TARGET_LSN ;
5087+
5088+ /*
5089+ * Convert the LSN string given by the user to XLogRecPtr form.
5090+ */
5091+ recoveryTargetLSN =
5092+ DatumGetLSN (DirectFunctionCall3 (pg_lsn_in ,
5093+ CStringGetDatum (item -> value ),
5094+ ObjectIdGetDatum (InvalidOid ),
5095+ Int32GetDatum (-1 )));
5096+ ereport (DEBUG2 ,
5097+ (errmsg_internal ("recovery_target_lsn = '%X/%X'" ,
5098+ (uint32 ) (recoveryTargetLSN >>32 ),
5099+ (uint32 )recoveryTargetLSN )));
5100+ }
50815101else if (strcmp (item -> name ,"recovery_target" )== 0 )
50825102{
50835103if (strcmp (item -> value ,"immediate" )== 0 )
@@ -5400,8 +5420,26 @@ recoveryStopsBefore(XLogReaderState *record)
54005420
54015421recoveryStopAfter = false;
54025422recoveryStopXid = InvalidTransactionId ;
5423+ recoveryStopLSN = InvalidXLogRecPtr ;
5424+ recoveryStopTime = 0 ;
5425+ recoveryStopName [0 ]= '\0' ;
5426+ return true;
5427+ }
5428+
5429+ /* Check if target LSN has been reached */
5430+ if (recoveryTarget == RECOVERY_TARGET_LSN &&
5431+ !recoveryTargetInclusive &&
5432+ record -> ReadRecPtr >=recoveryTargetLSN )
5433+ {
5434+ recoveryStopAfter = false;
5435+ recoveryStopXid = InvalidTransactionId ;
5436+ recoveryStopLSN = record -> ReadRecPtr ;
54035437recoveryStopTime = 0 ;
54045438recoveryStopName [0 ]= '\0' ;
5439+ ereport (LOG ,
5440+ (errmsg ("recovery stopping before WAL position (LSN) \"%X/%X\"" ,
5441+ (uint32 ) (recoveryStopLSN >>32 ),
5442+ (uint32 )recoveryStopLSN )));
54055443return true;
54065444}
54075445
@@ -5479,6 +5517,7 @@ recoveryStopsBefore(XLogReaderState *record)
54795517recoveryStopAfter = false;
54805518recoveryStopXid = recordXid ;
54815519recoveryStopTime = recordXtime ;
5520+ recoveryStopLSN = InvalidXLogRecPtr ;
54825521recoveryStopName [0 ]= '\0' ;
54835522
54845523if (isCommit )
@@ -5532,6 +5571,7 @@ recoveryStopsAfter(XLogReaderState *record)
55325571{
55335572recoveryStopAfter = true;
55345573recoveryStopXid = InvalidTransactionId ;
5574+ recoveryStopLSN = InvalidXLogRecPtr ;
55355575(void )getRecordTimestamp (record ,& recoveryStopTime );
55365576strlcpy (recoveryStopName ,recordRestorePointData -> rp_name ,MAXFNAMELEN );
55375577
@@ -5543,6 +5583,23 @@ recoveryStopsAfter(XLogReaderState *record)
55435583}
55445584}
55455585
5586+ /* Check if the target LSN has been reached */
5587+ if (recoveryTarget == RECOVERY_TARGET_LSN &&
5588+ recoveryTargetInclusive &&
5589+ record -> ReadRecPtr >=recoveryTargetLSN )
5590+ {
5591+ recoveryStopAfter = true;
5592+ recoveryStopXid = InvalidTransactionId ;
5593+ recoveryStopLSN = record -> ReadRecPtr ;
5594+ recoveryStopTime = 0 ;
5595+ recoveryStopName [0 ]= '\0' ;
5596+ ereport (LOG ,
5597+ (errmsg ("recovery stopping after WAL position (LSN) \"%X/%X\"" ,
5598+ (uint32 ) (recoveryStopLSN >>32 ),
5599+ (uint32 )recoveryStopLSN )));
5600+ return true;
5601+ }
5602+
55465603if (rmid != RM_XACT_ID )
55475604return false;
55485605
@@ -5598,6 +5655,7 @@ recoveryStopsAfter(XLogReaderState *record)
55985655recoveryStopAfter = true;
55995656recoveryStopXid = recordXid ;
56005657recoveryStopTime = recordXtime ;
5658+ recoveryStopLSN = InvalidXLogRecPtr ;
56015659recoveryStopName [0 ]= '\0' ;
56025660
56035661if (xact_info == XLOG_XACT_COMMIT ||
@@ -5629,6 +5687,7 @@ recoveryStopsAfter(XLogReaderState *record)
56295687recoveryStopAfter = true;
56305688recoveryStopXid = InvalidTransactionId ;
56315689recoveryStopTime = 0 ;
5690+ recoveryStopLSN = InvalidXLogRecPtr ;
56325691recoveryStopName [0 ]= '\0' ;
56335692return true;
56345693}
@@ -6055,6 +6114,11 @@ StartupXLOG(void)
60556114ereport (LOG ,
60566115(errmsg ("starting point-in-time recovery to \"%s\"" ,
60576116recoveryTargetName )));
6117+ else if (recoveryTarget == RECOVERY_TARGET_LSN )
6118+ ereport (LOG ,
6119+ (errmsg ("starting point-in-time recovery to WAL position (LSN) \"%X/%X\"" ,
6120+ (uint32 ) (recoveryTargetLSN >>32 ),
6121+ (uint32 )recoveryTargetLSN )));
60586122else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE )
60596123ereport (LOG ,
60606124(errmsg ("starting point-in-time recovery to earliest consistent point" )));
@@ -7124,6 +7188,12 @@ StartupXLOG(void)
71247188"%s %s\n" ,
71257189recoveryStopAfter ?"after" :"before" ,
71267190timestamptz_to_str (recoveryStopTime ));
7191+ else if (recoveryTarget == RECOVERY_TARGET_LSN )
7192+ snprintf (reason ,sizeof (reason ),
7193+ "%s LSN %X/%X\n" ,
7194+ recoveryStopAfter ?"after" :"before" ,
7195+ (uint32 ) (recoveryStopLSN >>32 ),
7196+ (uint32 )recoveryStopLSN );
71277197else if (recoveryTarget == RECOVERY_TARGET_NAME )
71287198snprintf (reason ,sizeof (reason ),
71297199"at restore point \"%s\"" ,