Skip to content

Commit 0999b72

Browse files
HUUTFJCommitfest Bot
authored andcommitted
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.
1 parent b21aa3b commit 0999b72

File tree

7 files changed

+75
-11
lines changed

7 files changed

+75
-11
lines changed

doc/src/sgml/func/func-admin.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,11 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
10121012
are also relevant for replication.
10131013
</para>
10141014

1015+
<para>
1016+
Note that slot manipulation functions except <function>pg_drop_replication_slot</function>
1017+
cannot be used in single-user mode.
1018+
</para>
1019+
10151020
<table id="functions-replication-table">
10161021
<title>Replication Management Functions</title>
10171022
<tgroup cols="1">

src/backend/replication/logical/logicalfuncs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
113113
List *options = NIL;
114114
DecodingOutputState *p;
115115

116+
/* Slot manipulation is not allowed in single-user mode */
117+
CheckSlotIsInSingleUserMode();
118+
116119
CheckSlotPermissions();
117120

118121
CheckLogicalDecodingRequirements();

src/backend/replication/slot.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,18 @@ CheckSlotPermissions(void)
15291529
"REPLICATION")));
15301530
}
15311531

1532+
/*
1533+
* Check whether the instance is in single-user mode.
1534+
*/
1535+
void
1536+
CheckSlotIsInSingleUserMode(void)
1537+
{
1538+
if (!IsUnderPostmaster)
1539+
ereport(ERROR,
1540+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1541+
errmsg("replication slots cannot be used in single-user mode")));
1542+
}
1543+
15321544
/*
15331545
* Reserve WAL for the currently active slot.
15341546
*

src/backend/replication/slotfuncs.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "access/xlogrecovery.h"
1818
#include "access/xlogutils.h"
1919
#include "funcapi.h"
20+
#include "miscadmin.h"
2021
#include "replication/logical.h"
2122
#include "replication/slot.h"
2223
#include "replication/slotsync.h"
@@ -76,6 +77,9 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
7677
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
7778
elog(ERROR, "return type must be a row type");
7879

80+
/* Slot manipulation is not allowed in single-user mode */
81+
CheckSlotIsInSingleUserMode();
82+
7983
CheckSlotPermissions();
8084

8185
CheckSlotRequirements();
@@ -182,6 +186,9 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
182186
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
183187
elog(ERROR, "return type must be a row type");
184188

189+
/* Slot manipulation is not allowed in single-user mode */
190+
CheckSlotIsInSingleUserMode();
191+
185192
CheckSlotPermissions();
186193

187194
CheckLogicalDecodingRequirements();
@@ -521,6 +528,9 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS)
521528

522529
Assert(!MyReplicationSlot);
523530

531+
/* Slot manipulation is not allowed in single-user mode */
532+
CheckSlotIsInSingleUserMode();
533+
524534
CheckSlotPermissions();
525535

526536
if (XLogRecPtrIsInvalid(moveto))
@@ -618,9 +628,13 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
618628
TupleDesc tupdesc;
619629
HeapTuple tuple;
620630

631+
621632
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
622633
elog(ERROR, "return type must be a row type");
623634

635+
/* Slot manipulation is not allowed in single-user mode */
636+
CheckSlotIsInSingleUserMode();
637+
624638
CheckSlotPermissions();
625639

626640
if (logical_slot)

src/backend/utils/adt/pg_upgrade_support.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ binary_upgrade_logical_slot_has_caught_up(PG_FUNCTION_ARGS)
296296
*/
297297
Assert(has_rolreplication(GetUserId()));
298298

299+
/* Slot manipulation is not allowed in single-user mode */
300+
CheckSlotIsInSingleUserMode();
301+
299302
slot_name = PG_GETARG_NAME(0);
300303

301304
/* Acquire the given slot */

src/include/replication/slot.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ extern void CheckPointReplicationSlots(bool is_shutdown);
342342

343343
extern void CheckSlotRequirements(void);
344344
extern void CheckSlotPermissions(void);
345+
extern void CheckSlotIsInSingleUserMode(void);
345346
extern ReplicationSlotInvalidationCause
346347
GetSlotInvalidationCause(const char *cause_name);
347348
extern const char *GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause);

src/test/modules/test_misc/t/008_slots_in_single_user_mode.pl

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,31 @@ sub run_test_in_single_user_mode
2727
],
2828
\$commands);
2929

30+
return $result;
31+
}
32+
33+
# Wrapper function for run_test_in_single_user_mode. This would be used when
34+
# the input command succeeds.
35+
sub run_test_in_single_user_mode_success
36+
{
37+
my ($node, $commands, $testname) = @_;
38+
39+
my $result = run_test_in_single_user_mode($node, $commands, $testname);
40+
3041
ok($result, $testname);
3142
}
3243

44+
# Wrapper function for run_test_in_single_user_mode. This would be used when
45+
# the input command fails.
46+
sub run_test_in_single_user_mode_fail
47+
{
48+
my ($node, $commands, $testname) = @_;
49+
50+
my $result = run_test_in_single_user_mode($node, $commands, $testname);
51+
52+
ok(!$result, $testname);
53+
}
54+
3355
my $slotname = 'test_slot';
3456

3557
# Initialize a node
@@ -38,34 +60,38 @@ sub run_test_in_single_user_mode
3860
$node->start;
3961

4062
# Define initial table
41-
$node->safe_psql('postgres', "CREATE TABLE foo (id int)");
63+
$node->safe_psql(
64+
'postgres', qq(
65+
CREATE TABLE foo (id int);
66+
SELECT pg_create_logical_replication_slot('$slotname', 'test_decoding');
67+
));
4268

4369
# Stop the node to run and test in single-user mode
4470
$node->stop;
4571

46-
run_test_in_single_user_mode(
72+
run_test_in_single_user_mode_fail(
4773
$node,
48-
"SELECT pg_create_logical_replication_slot('$slotname', 'test_decoding')",
49-
"replication slot can be created in single-user mode");
74+
"SELECT pg_create_logical_replication_slot('another_slot', 'test_decoding')",
75+
"replication slot cannot be created in single-user mode");
5076

51-
run_test_in_single_user_mode(
77+
run_test_in_single_user_mode_fail(
5278
$node, qq(
5379
INSERT INTO foo VALUES (1);
5480
SELECT count(1) FROM pg_logical_slot_get_changes('$slotname', NULL, NULL);
5581
),
56-
"logical decoding be done in single-user mode");
82+
"logical decoding cannot be done in single-user mode");
5783

58-
run_test_in_single_user_mode(
84+
run_test_in_single_user_mode_fail(
5985
$node,
6086
"SELECT pg_replication_slot_advance('$slotname', pg_current_wal_lsn())",
61-
"replication slot can be advanced in single-user mode");
87+
"replication slot cannot be advanced in single-user mode");
6288

63-
run_test_in_single_user_mode(
89+
run_test_in_single_user_mode_fail(
6490
$node,
6591
"SELECT pg_copy_logical_replication_slot('$slotname', 'dest_slot')",
66-
"replication slot can be copied in single-user mode");
92+
"replication slot cannot be copied in single-user mode");
6793

68-
run_test_in_single_user_mode(
94+
run_test_in_single_user_mode_success(
6995
$node,
7096
"SELECT pg_drop_replication_slot('$slotname')",
7197
"replication slot can be dropped in single-user mode");

0 commit comments

Comments
 (0)