agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
[PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
732+ messages / 1 participants
[nested] [flat]

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--HitlKQoyB8givWgr--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-08 07:05 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-08 07:05 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../t/035_standby_logical_decoding.pl         | 28 +++++++++++++++++--
 1 file changed, 25 insertions(+), 3 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index ce80123844d..146c33ce39d 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby);
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start(
 	'2>' => \$stderr2,
 	IPC::Run::timeout($default_timeout));
 
+# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+$decode_session->query_until(
+	qr/decoding_started/, qq(
+	\\echo decoding_started
+	SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+));
+
 # Wait for the walsender to acquire the slot.
 $node_cascading_standby->poll_query_until('testdb',
 	qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)]
@@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb',
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
 
-# Verify pg_recvlogical successfully decodes the data.
+# Verify pg_recvlogical successfully decodes the data (walsender path).
 $pump_timeout = IPC::Run::timer($default_timeout);
 ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s),
 	'pg_recvlogical works during promotion timeline switch');
 
+# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path).
+my $sql_timeout = IPC::Run::timer($default_timeout);
+ok( pump_until(
+		$decode_session->{run}, $sql_timeout,
+		\$decode_session->{stdout}, qr/\d+/m),
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
+$decode_session->quit;
+
 done_testing();
-- 
2.34.1


--KccnfG1O+OuiQe+b--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--






^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread

* [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race
@ 2026-06-09 09:56 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
  0 siblings, 0 replies; 732+ messages in thread

From: Bertrand Drouvot @ 2026-06-09 09:56 UTC (permalink / raw)

Extend the injection-point test in 035_standby_logical_decoding.pl to
also cover the SQL-callable decoding path (pg_logical_slot_peek_changes)
which exercises read_local_xlog_page_guts().

The test uses the same promotion and injection point as the walsender
test: a background psql session is connected before promotion, then
issues pg_logical_slot_peek_changes while startup is paused at the
injection point.

Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com>
Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com>
Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com
---
 .../recovery/t/035_standby_logical_decoding.pl  | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
 100.0% src/test/recovery/t/

diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl
index 9b45c819241..6ec338ebf32 100644
--- a/src/test/recovery/t/035_standby_logical_decoding.pl
+++ b/src/test/recovery/t/035_standby_logical_decoding.pl
@@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected,
 # timeline in this window.
 ##################################################
 
-# Create a logical slot on the cascading standby for this test.
+# Create logical slots on the cascading standby for this test.
 $node_cascading_standby->create_logical_slot_on_standby($node_standby,
 	'race_slot', 'testdb');
+$node_cascading_standby->create_logical_slot_on_standby($node_standby,
+	'race_slot_sql', 'testdb');
 
-# Insert data so the slot has WAL to decode.
+# Insert data so the slots have WAL to decode.
 $node_standby->safe_psql('testdb',
 	qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;]
 );
@@ -1089,6 +1091,9 @@ COMMIT};
 $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;');
 $node_standby->wait_for_replay_catchup($node_cascading_standby);
 
+# Open a background psql session BEFORE promotion for the SQL decoding test.
+my $decode_session = $node_cascading_standby->background_psql('testdb');
+
 # Attach injection point to pause startup after WAL segment cleanup
 # but before RecoveryInProgress() flips to false.
 $node_cascading_standby->safe_psql('testdb',
@@ -1130,6 +1135,14 @@ is($stdout2, $expected,
 	'got expected output from pg_recvlogical during promotion timeline switch'
 );
 
+# Verify SQL decoding (read_local_xlog_page_guts path) on the pre-connected
+# session.
+my $sql_out = $decode_session->query_safe(
+	"SELECT data FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')"
+);
+is($sql_out, $expected,
+	'pg_logical_slot_peek_changes works during promotion timeline switch');
+
 # Resume promotion.
 $node_cascading_standby->safe_psql('testdb',
 	"SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');");
-- 
2.34.1


--Wctt8FE9hhxDGrBZ--





^ permalink  raw  reply  [nested|flat] 732+ messages in thread


end of thread, other threads:[~2026-06-09 09:56 UTC | newest]

Thread overview: 732+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-08 07:05 [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-06-09 09:56 [PATCH v2 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Bertrand Drouvot <bertranddrouvot.pg@gmail.com>

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