From b19815119909d0529d6c52889ab0959ec44e7e7f Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Date: Wed, 19 Feb 2025 11:37:26 +0900
Subject: [PATCH v4] Prohibit slot operations while in the single user mode

---
 src/backend/replication/logical/logicalfuncs.c |  2 ++
 src/backend/replication/slot.c                 | 12 ++++++++++++
 src/backend/replication/slotfuncs.c            | 12 ++++++++++++
 src/include/replication/slot.h                 |  1 +
 4 files changed, 27 insertions(+)

diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index ca53caac2f..178a07af7d 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -113,6 +113,8 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	List	   *options = NIL;
 	DecodingOutputState *p;
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckLogicalDecodingRequirements();
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index 719e531eb9..d041adfe48 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1442,6 +1442,18 @@ CheckSlotPermissions(void)
 						   "REPLICATION")));
 }
 
+/*
+ * Check whether the instance is in single-user mode.
+ */
+void
+CheckSlotIsInSingleUserMode(void)
+{
+	if (!IsUnderPostmaster)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("replication slots cannot be used in single-user mode")));
+}
+
 /*
  * Reserve WAL for the currently active slot.
  *
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 146eef5871..b669774c87 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,6 +17,7 @@
 #include "access/xlogrecovery.h"
 #include "access/xlogutils.h"
 #include "funcapi.h"
+#include "miscadmin.h"
 #include "replication/logical.h"
 #include "replication/slot.h"
 #include "replication/slotsync.h"
@@ -76,6 +77,8 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckSlotRequirements();
@@ -182,6 +185,8 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckLogicalDecodingRequirements();
@@ -515,6 +520,8 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS)
 
 	Assert(!MyReplicationSlot);
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	if (XLogRecPtrIsInvalid(moveto))
@@ -612,9 +619,12 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
 	TupleDesc	tupdesc;
 	HeapTuple	tuple;
 
+
 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 		elog(ERROR, "return type must be a row type");
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	if (logical_slot)
@@ -871,6 +881,8 @@ pg_sync_replication_slots(PG_FUNCTION_ARGS)
 	char	   *err;
 	StringInfoData app_name;
 
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	if (!RecoveryInProgress())
diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h
index f5a24ccfbf..24523b1001 100644
--- a/src/include/replication/slot.h
+++ b/src/include/replication/slot.h
@@ -306,6 +306,7 @@ extern void CheckPointReplicationSlots(bool is_shutdown);
 
 extern void CheckSlotRequirements(void);
 extern void CheckSlotPermissions(void);
+extern void CheckSlotIsInSingleUserMode(void);
 extern ReplicationSlotInvalidationCause
 			GetSlotInvalidationCause(const char *invalidation_reason);
 extern const char *GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause);
-- 
2.43.5

