Skip to content

Commit ecc4294

Browse files
author
Commitfest Bot
committed
[CF 5626] v7 - ReplicationSlotRelease() crashes when the instance is in the single user mode
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5626 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/OSCPR01MB14966F06D84A7A9C26570A519F530A@OSCPR01MB14966.jpnprd01.prod.outlook.com Author(s): Hayato Kuroda
2 parents 38c5fbd + 0999b72 commit ecc4294

File tree

9 files changed

+140
-2
lines changed

9 files changed

+140
-2
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: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ ReplicationSlotAcquire(const char *name, bool nowait, bool error_if_invalid)
653653
}
654654
else
655655
{
656-
active_pid = MyProcPid;
656+
s->active_pid = active_pid = MyProcPid;
657657
ReplicationSlotSetInactiveSince(s, 0, true);
658658
}
659659
LWLockRelease(ReplicationSlotControlLock);
@@ -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/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tests += {
1616
't/005_timeouts.pl',
1717
't/006_signal_autovacuum.pl',
1818
't/007_catcache_inval.pl',
19+
't/008_slots_in_single_user_mode.pl',
1920
],
2021
},
2122
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use strict;
2+
use warnings;
3+
use PostgreSQL::Test::Cluster;
4+
use PostgreSQL::Test::Utils;
5+
use Test::More;
6+
7+
# Tests the slot manipulation in the single-user mode
8+
9+
# Skip the whole thing on the windows platform
10+
if ($windows_os)
11+
{
12+
plan skip_all => 'this test is not supported by this platform';
13+
}
14+
15+
# Run passed commands in single-user mode. The return value from the command is
16+
# passed through.
17+
sub run_test_in_single_user_mode
18+
{
19+
my ($node, $commands, $testname) = @_;
20+
21+
my $result = run_log(
22+
[
23+
'postgres', '--single', '-F',
24+
'-c' => 'exit_on_error=true',
25+
'-D' => $node->data_dir,
26+
'postgres'
27+
],
28+
\$commands);
29+
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+
41+
ok($result, $testname);
42+
}
43+
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+
55+
my $slotname = 'test_slot';
56+
57+
# Initialize a node
58+
my $node = PostgreSQL::Test::Cluster->new('node');
59+
$node->init(allows_streaming => "logical");
60+
$node->start;
61+
62+
# Define initial table
63+
$node->safe_psql(
64+
'postgres', qq(
65+
CREATE TABLE foo (id int);
66+
SELECT pg_create_logical_replication_slot('$slotname', 'test_decoding');
67+
));
68+
69+
# Stop the node to run and test in single-user mode
70+
$node->stop;
71+
72+
run_test_in_single_user_mode_fail(
73+
$node,
74+
"SELECT pg_create_logical_replication_slot('another_slot', 'test_decoding')",
75+
"replication slot cannot be created in single-user mode");
76+
77+
run_test_in_single_user_mode_fail(
78+
$node, qq(
79+
INSERT INTO foo VALUES (1);
80+
SELECT count(1) FROM pg_logical_slot_get_changes('$slotname', NULL, NULL);
81+
),
82+
"logical decoding cannot be done in single-user mode");
83+
84+
run_test_in_single_user_mode_fail(
85+
$node,
86+
"SELECT pg_replication_slot_advance('$slotname', pg_current_wal_lsn())",
87+
"replication slot cannot be advanced in single-user mode");
88+
89+
run_test_in_single_user_mode_fail(
90+
$node,
91+
"SELECT pg_copy_logical_replication_slot('$slotname', 'dest_slot')",
92+
"replication slot cannot be copied in single-user mode");
93+
94+
run_test_in_single_user_mode_success(
95+
$node,
96+
"SELECT pg_drop_replication_slot('$slotname')",
97+
"replication slot can be dropped in single-user mode");
98+
99+
done_testing();

src/test/recovery/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ tests += {
5656
't/045_archive_restartpoint.pl',
5757
't/046_checkpoint_logical_slot.pl',
5858
't/047_checkpoint_physical_slot.pl',
59-
't/048_vacuum_horizon_floor.pl'
59+
't/048_vacuum_horizon_floor.pl',
6060
],
6161
},
6262
}

0 commit comments

Comments
 (0)