postgres.git / summary / log / commit / refs

commit    8ed079cad8241ae8ba6bf8ffd1212d0ca487cb19
Author:   Michael Paquier <michael@paquier.xyz>
Date:     Wed Aug 20 06:00:17 2025 +0000
First released in: 13.23, 14.20, 15.15, 16.11, 17.7, 18.0

    Fix assertion failure with replication slot release in single-user mode
    
    Some replication slot manipulations (logical decoding via SQL,
    advancing) were failing an assertion when releasing a slot in
    single-user mode, because active_pid was not set in a ReplicationSlot
    when its slot is acquired.
    
    ReplicationSlotAcquire() has some logic to be able to work with the
    single-user mode.  This commit sets ReplicationSlot->active_pid to
    MyProcPid, to let the slot-related logic fall-through, considering the
    single process as the one holding the slot.
    
    Some TAP tests are added for various replication slot functions with the
    single-user mode, while on it, for slot creation, drop, advancing, copy
    and logical decoding with multiple slot types (temporary, physical vs
    logical).  These tests are skipped on Windows, as direct calls of
    postgres --single would fail on permission failures.  There is no
    platform-specific behavior that needs to be checked, so living with this
    restriction should be fine.  The CI is OK with that, now let's see what
    the buildfarm tells.
    
    Author: Hayato Kuroda <kuroda.hayato@fujitsu.com>
    Reviewed-by: Paul A. Jungwirth <pj@illuminatedcomputing.com>
    Reviewed-by: Mutaamba Maasha <maasha@gmail.com>
    Discussion: https://postgr.es/m/OSCPR01MB14966ED588A0328DAEBE8CB25F5FA2@OSCPR01MB14966.jpnprd01.prod.outlook.com
    Backpatch-through: 13


src/backend/replication/slot.c | 2 +- src/test/modules/test_misc/Makefile | 2 + .../test_misc/t/008_replslot_single_user.pl | 95 ++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 219d126d36d..00a35b954d5 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -429,7 +429,7 @@ retry: SpinLockRelease(&s->mutex); } else - active_pid = MyProcPid; + s->active_pid = active_pid = MyProcPid; LWLockRelease(ReplicationSlotControlLock); /* diff --git a/src/test/modules/test_misc/Makefile b/src/test/modules/test_misc/Makefile index 39c6c2014a0..06a8ee9d03a 100644 --- a/src/test/modules/test_misc/Makefile +++ b/src/test/modules/test_misc/Makefile @@ -2,6 +2,8 @@ TAP_TESTS = 1 +EXTRA_INSTALL = contrib/test_decoding + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) 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 new file mode 100644 index 00000000000..796700d621f --- /dev/null +++ b/src/test/modules/test_misc/t/008_replslot_single_user.pl @@ -0,0 +1,95 @@ +# Copyright (c) 2025, PostgreSQL Global Development Group + +# Test manipulations of replication slots with the single-user mode. + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Skip the tests on Windows, as single-user mode would fail on permission +# failure with privileged accounts. +if ($windows_os) +{ + plan skip_all => 'this test is not supported by this platform'; +} + +# Run set of queries in single-user mode. +sub test_single_mode +{ + my ($node, $queries, $testname) = @_; + + my $result = run_log( + [ + 'postgres', '--single', '-F', + '-c' => 'exit_on_error=true', + '-D' => $node->data_dir, + 'postgres' + ], + '<' => \$queries); + + ok($result, $testname); +} + +my $slot_logical = 'slot_logical'; +my $slot_physical = 'slot_physical'; + +# Initialize a node +my $node = PostgreSQL::Test::Cluster->new('node'); +$node->init(allows_streaming => "logical"); +$node->start; + +# Define initial table +$node->safe_psql('postgres', "CREATE TABLE foo (id int)"); + +$node->stop; + +test_single_mode( + $node, + "SELECT pg_create_logical_replication_slot('$slot_logical', 'test_decoding')", + "logical slot creation"); +test_single_mode( + $node, + "SELECT pg_create_physical_replication_slot('$slot_physical', true)", + "physical slot creation"); +test_single_mode( + $node, + "SELECT pg_create_physical_replication_slot('slot_tmp', true, true)", + "temporary physical slot creation"); + +test_single_mode( + $node, qq( +INSERT INTO foo VALUES (1); +SELECT pg_logical_slot_get_changes('$slot_logical', NULL, NULL); +), + "logical decoding"); + +test_single_mode( + $node, + "SELECT pg_replication_slot_advance('$slot_logical', pg_current_wal_lsn())", + "logical slot advance"); +test_single_mode( + $node, + "SELECT pg_replication_slot_advance('$slot_physical', pg_current_wal_lsn())", + "physical slot advance"); + +test_single_mode( + $node, + "SELECT pg_copy_logical_replication_slot('$slot_logical', 'slot_log_copy')", + "logical slot copy"); +test_single_mode( + $node, + "SELECT pg_copy_physical_replication_slot('$slot_physical', 'slot_phy_copy')", + "physical slot copy"); + +test_single_mode( + $node, + "SELECT pg_drop_replication_slot('$slot_logical')", + "logical slot drop"); +test_single_mode( + $node, + "SELECT pg_drop_replication_slot('$slot_physical')", + "physical slot drop"); + +done_testing(); [parent: 7d8d36289f6a]