public inbox for [email protected]  
help / color / mirror / Atom feed
From: Hayato Kuroda (Fujitsu) <[email protected]>
To: 'Michael Paquier' <[email protected]>
To: [email protected] <[email protected]>
Cc: 'Paul A Jungwirth' <[email protected]>
Cc: Mutaamba Maasha <[email protected]>
Cc: Amit Kapila <[email protected]>
Cc: David G. Johnston <[email protected]>
Cc: Zhijie Hou (Fujitsu) <[email protected]>
Cc: Tom Lane <[email protected]>
Cc: Bertrand Drouvot <[email protected]>
Subject: RE: ReplicationSlotRelease() crashes when the instance is in the single user mode
Date: Thu, 21 Aug 2025 06:39:47 +0000
Message-ID: <OSCPR01MB149668B67979F8F4C73B69E2EF532A@OSCPR01MB14966.jpnprd01.prod.outlook.com> (raw)
In-Reply-To: <[email protected]>
References: <CAA4eK1L8_QrYavkCy9b0Rrz8gqZRQE8kEmv3g7Tz6tsN8YuvhA@mail.gmail.com>
	<OSCPR01MB149660CD4D6648CFE7FE68D34F5CC2@OSCPR01MB14966.jpnprd01.prod.outlook.com>
	<CA+renyWi+bbD8M8CDAaOMGbUrXptKLCK8zQWEhXy00PJuJqNuQ@mail.gmail.com>
	<OSCPR01MB14966B39B3ED21229EAEFA10FF531A@OSCPR01MB14966.jpnprd01.prod.outlook.com>
	<[email protected]>
	<OSCPR01MB14966D87D8EB0D9CD32C6A43DF530A@OSCPR01MB14966.jpnprd01.prod.outlook.com>
	<[email protected]>
	<OSCPR01MB14966F06D84A7A9C26570A519F530A@OSCPR01MB14966.jpnprd01.prod.outlook.com>
	<[email protected]>
	<OSCPR01MB14966EEA6534F4A07A1F1722EF533A@OSCPR01MB14966.jpnprd01.prod.outlook.com>
	<[email protected]>

Dear Michael and hackers,

This is a wrap-up of this thread. I'm planning to close the thread once.

> I am saying that there is little point in having tests for the origin
> functions in single-user mode as these don't do anything really
> fancy with global states (there's a acquired_by of course, and no
> specific IsUnderPostmaster patch), contrary to the replication slots.
> I am not convinced that there is any need to restrict them, either.
> If somebody shows a reason to justify such a move, we could argue
> about it, of course.

OK, thanks for the clarification. Yes, there are no special path for
single-user mode for origins.

Now, senior members recently appeared here are not so motivated to restrict some
operations in single-user mode unless it cannot be done in principle. Based on
that, I decided to close the thread once until someone raised another point.
For the reference purpose, I attached a patch which prohibits slot manipulation
in single-user mode. Anyone can update and use the patch for any purposes.

Best regards,
Hayato Kuroda
FUJITSU LIMITED



Attachments:

  [application/octet-stream] v8-0001-Prohibit-slot-manipulation-while-in-single-user-m.patch (8.6K, ../OSCPR01MB149668B67979F8F4C73B69E2EF532A@OSCPR01MB14966.jpnprd01.prod.outlook.com/2-v8-0001-Prohibit-slot-manipulation-while-in-single-user-m.patch)
  download | inline diff:
From 2e5c5aeab9eadcc386dec00e7f2c67bbaa900667 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 19 Feb 2025 11:37:26 +0900
Subject: [PATCH v8] Prohibit slot manipulation while in single-user mode

Replication-related commands are rarely used in single-user mode and have been
broken for years. This commit prohibits calling slot manipulation SQL functions
to avoid additional risk of failures. One exception is pg_drop_replication_slot.
It is still allowed because users may want to clean up their mistakes in the mode.
---
 doc/src/sgml/func/func-admin.sgml             |  5 ++
 .../replication/logical/logicalfuncs.c        |  3 ++
 src/backend/replication/slot.c                | 12 +++++
 src/backend/replication/slotfuncs.c           | 14 +++++
 src/backend/utils/adt/pg_upgrade_support.c    |  3 ++
 src/include/replication/slot.h                |  1 +
 .../test_misc/t/008_replslot_single_user.pl   | 53 ++++++++++++++-----
 7 files changed, 78 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/func/func-admin.sgml b/doc/src/sgml/func/func-admin.sgml
index 6347fe60b0c..6363084bc94 100644
--- a/doc/src/sgml/func/func-admin.sgml
+++ b/doc/src/sgml/func/func-admin.sgml
@@ -1012,6 +1012,11 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
     are also relevant for replication.
    </para>
 
+   <para>
+    Note that slot manipulation functions except <function>pg_drop_replication_slot</function>
+    cannot be used in single-user mode.
+   </para>
+
    <table id="functions-replication-table">
     <title>Replication Management Functions</title>
     <tgroup cols="1">
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index ca53caac2f2..30877fdc3ab 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -113,6 +113,9 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 	List	   *options = NIL;
 	DecodingOutputState *p;
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckLogicalDecodingRequirements();
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index fd0fdb96d42..4ca1227cc94 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -1529,6 +1529,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 69f4c6157c5..90fe63283a1 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,9 @@ 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");
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckSlotRequirements();
@@ -182,6 +186,9 @@ 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");
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	CheckLogicalDecodingRequirements();
@@ -521,6 +528,9 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS)
 
 	Assert(!MyReplicationSlot);
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	if (XLogRecPtrIsInvalid(moveto))
@@ -618,9 +628,13 @@ 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");
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	CheckSlotPermissions();
 
 	if (logical_slot)
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index a4f8b4faa90..337f705b34b 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -296,6 +296,9 @@ binary_upgrade_logical_slot_has_caught_up(PG_FUNCTION_ARGS)
 	 */
 	Assert(has_rolreplication(GetUserId()));
 
+	/* Slot manipulation is not allowed in single-user mode */
+	CheckSlotIsInSingleUserMode();
+
 	slot_name = PG_GETARG_NAME(0);
 
 	/* Acquire the given slot */
diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h
index e8fc342d1a9..b4d89760f96 100644
--- a/src/include/replication/slot.h
+++ b/src/include/replication/slot.h
@@ -342,6 +342,7 @@ extern void CheckPointReplicationSlots(bool is_shutdown);
 
 extern void CheckSlotRequirements(void);
 extern void CheckSlotPermissions(void);
+extern void CheckSlotIsInSingleUserMode(void);
 extern ReplicationSlotInvalidationCause
 			GetSlotInvalidationCause(const char *cause_name);
 extern const char *GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause);
diff --git a/src/test/modules/test_misc/t/008_replslot_single_user.pl b/src/test/modules/test_misc/t/008_replslot_single_user.pl
index 796700d621f..4a93ed585d3 100644
--- a/src/test/modules/test_misc/t/008_replslot_single_user.pl
+++ b/src/test/modules/test_misc/t/008_replslot_single_user.pl
@@ -29,8 +29,30 @@ sub test_single_mode
 		],
 		'<' => \$queries);
 
+	return $result;
+}
+
+# Wrapper function for test_single_mode. This would be used when the input
+# command succeeds.
+sub test_single_mode_ok
+{
+	my ($node, $queries, $testname) = @_;
+
+	my $result = test_single_mode($node, $queries, $testname);
+
 	ok($result, $testname);
 }
+ 
+# Wrapper function for test_single_mode. This would be used when the input
+# command fails.
+sub test_single_mode_fail
+{
+	my ($node, $queries, $testname) = @_;
+
+	my $result = test_single_mode($node, $queries, $testname);
+
+	ok(!$result, $testname);
+}
 
 my $slot_logical = 'slot_logical';
 my $slot_physical = 'slot_physical';
@@ -41,53 +63,58 @@ $node->init(allows_streaming => "logical");
 $node->start;
 
 # Define initial table
-$node->safe_psql('postgres', "CREATE TABLE foo (id int)");
+$node->safe_psql(
+	'postgres', qq(
+CREATE TABLE foo (id int);
+SELECT pg_create_logical_replication_slot('$slot_logical', 'test_decoding');
+SELECT pg_create_physical_replication_slot('$slot_physical');
+));
 
 $node->stop;
 
-test_single_mode(
+test_single_mode_fail(
 	$node,
-	"SELECT pg_create_logical_replication_slot('$slot_logical', 'test_decoding')",
+	"SELECT pg_create_logical_replication_slot('another_slot', 'test_decoding')",
 	"logical slot creation");
-test_single_mode(
+test_single_mode_fail(
 	$node,
-	"SELECT pg_create_physical_replication_slot('$slot_physical', true)",
+	"SELECT pg_create_physical_replication_slot('another_slot', true)",
 	"physical slot creation");
-test_single_mode(
+test_single_mode_fail(
 	$node,
 	"SELECT pg_create_physical_replication_slot('slot_tmp', true, true)",
 	"temporary physical slot creation");
 
-test_single_mode(
+test_single_mode_fail(
 	$node, qq(
 INSERT INTO foo VALUES (1);
 SELECT pg_logical_slot_get_changes('$slot_logical', NULL, NULL);
 ),
 	"logical decoding");
 
-test_single_mode(
+test_single_mode_fail(
 	$node,
 	"SELECT pg_replication_slot_advance('$slot_logical', pg_current_wal_lsn())",
 	"logical slot advance");
-test_single_mode(
+test_single_mode_fail(
 	$node,
 	"SELECT pg_replication_slot_advance('$slot_physical', pg_current_wal_lsn())",
 	"physical slot advance");
 
-test_single_mode(
+test_single_mode_fail(
 	$node,
 	"SELECT pg_copy_logical_replication_slot('$slot_logical', 'slot_log_copy')",
 	"logical slot copy");
-test_single_mode(
+test_single_mode_fail(
 	$node,
 	"SELECT pg_copy_physical_replication_slot('$slot_physical', 'slot_phy_copy')",
 	"physical slot copy");
 
-test_single_mode(
+test_single_mode_ok(
 	$node,
 	"SELECT pg_drop_replication_slot('$slot_logical')",
 	"logical slot drop");
-test_single_mode(
+test_single_mode_ok(
 	$node,
 	"SELECT pg_drop_replication_slot('$slot_physical')",
 	"physical slot drop");
-- 
2.47.1



view thread (31+ messages)

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: RE: ReplicationSlotRelease() crashes when the instance is in the single user mode
  In-Reply-To: <OSCPR01MB149668B67979F8F4C73B69E2EF532A@OSCPR01MB14966.jpnprd01.prod.outlook.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox