From 3f5f03a942abf08763e9692b00c82f3bfd2c8ed4 Mon Sep 17 00:00:00 2001
From: Yura Sokolov <y.sokolov@postgrespro.ru>
Date: Tue, 18 Jun 2024 12:07:41 +0300
Subject: [PATCH v2 1/2] Restore ability to react on timestamp in
 XLOG_RESTORE_POINT

Looks like, reaction on XLOG_RESTORE_POINT's timestamp as a
recovery_target_time were lost during refactoring in
c945af80 "Refactor checking whether we've reached the recovery target."

Co-authored-by: Sergey Fukanchik <s.fukanchik@postgrespro.ru>
---
 src/backend/access/transam/xlogrecovery.c   | 43 +++++++++++++++++++++
 src/test/recovery/t/003_recovery_targets.pl | 19 ++++++++-
 2 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index b45b8331720..e1ba4452113 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -2783,6 +2783,49 @@ recoveryStopsAfter(XLogReaderState *record)
 		return true;
 	}
 
+	if (recoveryTarget == RECOVERY_TARGET_TIME &&
+		rmid == RM_XLOG_ID && getRecordTimestamp(record, &recordXtime))
+	{
+		bool		stops_here = false;
+
+		/*
+		 * Use same conditions as in recoveryStopsBefore for transaction
+		 * records to not override transactions time handling.
+		 */
+		if (recoveryTargetInclusive)
+			stops_here = recordXtime > recoveryTargetTime;
+		else
+			stops_here = recordXtime >= recoveryTargetTime;
+
+		if (stops_here)
+		{
+			recoveryStopAfter = true;
+			recoveryStopXid = InvalidTransactionId;
+			recoveryStopTime = recordXtime;
+			recoveryStopLSN = InvalidXLogRecPtr;
+			recoveryStopName[0] = '\0';
+
+			if (info == XLOG_RESTORE_POINT)
+			{
+				xl_restore_point *recordRestorePointData;
+
+				recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
+
+				ereport(LOG,
+						(errmsg("recovery stopping at restore point \"%.*s\", time %s",
+								MAXFNAMELEN, recordRestorePointData->rp_name,
+								timestamptz_to_str(recoveryStopTime))));
+			}
+			else
+			{
+				ereport(LOG,
+						(errmsg("recovery stopping at %s, time %s",
+								xlog_identify(info),
+								timestamptz_to_str(recoveryStopTime))));
+			}
+		}
+	}
+
 	if (rmid != RM_XACT_ID)
 		return false;
 
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index 58241a68f0a..d5345c1bf1e 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -22,6 +22,7 @@ sub test_recovery_standby
 	my $recovery_params = shift;
 	my $num_rows = shift;
 	my $until_lsn = shift;
+	my $expect_in_log = shift;
 
 	my $node_standby = PostgreSQL::Test::Cluster->new($node_name);
 	$node_standby->init_from_backup($node_primary, 'my_backup',
@@ -45,6 +46,16 @@ sub test_recovery_standby
 	  $node_standby->safe_psql('postgres', "SELECT count(*) FROM tab_int");
 	is($result, qq($num_rows), "check standby content for $test_name");
 
+	if (defined $expect_in_log)
+	{
+		local $/;
+		open(my $FH, '<', $node_standby->logfile())
+			or die "Can't open standby nodes's log";
+		my $lines = <$FH>;
+		close($FH);
+		like($lines, qr/${expect_in_log}/i, "expecting \'$expect_in_log\' in log file for $test_name");
+	}
+
 	# Stop standby node
 	$node_standby->teardown_node;
 
@@ -93,8 +104,9 @@ $node_primary->safe_psql('postgres',
 my $recovery_name = "my_target";
 my $lsn4 =
   $node_primary->safe_psql('postgres', "SELECT pg_current_wal_lsn();");
-$node_primary->safe_psql('postgres',
-	"SELECT pg_create_restore_point('$recovery_name');");
+$ret = $node_primary->safe_psql('postgres',
+	"SELECT now(), pg_create_restore_point('$recovery_name');");
+my $recovery_restore_point_time = (split /\|/, $ret)[0];
 
 # And now for a recovery target LSN
 $node_primary->safe_psql('postgres',
@@ -121,6 +133,9 @@ test_recovery_standby('time', 'standby_3', $node_primary, \@recovery_params,
 @recovery_params = ("recovery_target_name = '$recovery_name'");
 test_recovery_standby('name', 'standby_4', $node_primary, \@recovery_params,
 	"4000", $lsn4);
+@recovery_params = ("recovery_target_time = '$recovery_restore_point_time'");
+test_recovery_standby('rp_time', 'standby_4_time', $node_primary, \@recovery_params,
+	"4000", $lsn4, "recovery stopping at restore point \"$recovery_name\"");
 @recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
 test_recovery_standby('LSN', 'standby_5', $node_primary, \@recovery_params,
 	"5000", $lsn5);
-- 
2.43.0

