From 349df8eada0d3eae63b009e34691240aad8cf258 Mon Sep 17 00:00:00 2001
From: Shlok Kyal <shlok.kyal.oss@gmail.com>
Date: Tue, 4 Feb 2025 14:58:32 +0530
Subject: [PATCH v1] Restrict copying of invalidated replication slots

Currently we can copy an invalidated slot using function
'pg_copy_logical_replication_slot'. With this patch we will throw an
error in such case.
---
 src/backend/replication/slotfuncs.c                 | 8 ++++++++
 src/test/recovery/t/035_standby_logical_decoding.pl | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 8be4b8c65b..f83a890c50 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -604,6 +604,7 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
 	ReplicationSlot second_slot_contents;
 	XLogRecPtr	src_restart_lsn;
 	bool		src_islogical;
+	bool		src_isinvalidated;
 	bool		temporary;
 	char	   *plugin;
 	Datum		values[2];
@@ -661,6 +662,7 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
 	src_restart_lsn = first_slot_contents.data.restart_lsn;
 	temporary = (first_slot_contents.data.persistency == RS_TEMPORARY);
 	plugin = logical_slot ? NameStr(first_slot_contents.data.plugin) : NULL;
+	src_isinvalidated = (first_slot_contents.data.invalidated != RS_INVAL_NONE);
 
 	/* Check type of replication slot */
 	if (src_islogical != logical_slot)
@@ -678,6 +680,12 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("cannot copy a replication slot that doesn't reserve WAL")));
 
+	/* We should not copy invalidated replication slots */
+	if (src_isinvalidated)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("cannot copy an invalidated replication slot")));
+
 	/* Overwrite params from optional arguments */
 	if (PG_NARGS() >= 3)
 		temporary = PG_GETARG_BOOL(2);
diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 505e85d1eb..efebf72bd6 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -553,6 +553,14 @@ $handle =
 check_pg_recvlogical_stderr($handle,
 	"can no longer access replication slot \"vacuum_full_activeslot\"");
 
+# Attempting to copy an invalidated slot
+($result, $stdout, $stderr) = $node_standby->psql(
+	'postgres',
+	qq[select pg_copy_logical_replication_slot('vacuum_full_inactiveslot', 'vacuum_full_inactiveslot_copy');],
+	replication => 'database');
+ok($stderr =~ /ERROR:  cannot copy an invalidated replication slot/,
+	"invalidated slot cannot be copied");
+
 # Turn hot_standby_feedback back on
 change_hot_standby_feedback_and_wait_for_xmins(1, 1);
 
-- 
2.34.1

