From 40b192716cbbfac67363f44329860a8cec85b598 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Mon, 13 Jul 2026 16:17:39 +0530
Subject: [PATCH v2 3/3] Allow logical replication workers to ignore
 default_transaction_read_only

Sequence synchronization updates sequence state via setval()/nextval(),
both of which explicitly call PreventCommandIfReadOnly().  If
default_transaction_read_only is enabled on the subscriber, this
causes sequencesync workers to fail with "cannot execute nextval()
in a read-only transaction".  Apply and tablesync workers are not
affected, since they write via direct heap access rather than
through these read-only-checked functions.

Rather than special-casing sequencesync, override
default_transaction_read_only to "off" for all logical replication
workers in InitializeLogRepWorker(), the same way
session_replication_role and search_path are already forced there.
This keeps the initialization uniform.
---
 src/backend/replication/logical/worker.c |  8 ++++
 src/test/subscription/t/036_sequences.pl | 51 ++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 7799266c614..df9c2e76d09 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -5809,6 +5809,14 @@ InitializeLogRepWorker(void)
 	 */
 	SetConfigOption("search_path", "", PGC_SUSET, PGC_S_OVERRIDE);
 
+	/*
+	 * Ignore default_transaction_read_only for logical replication workers,
+	 * as they need to be able to modify subscriber-side state (e.g. apply
+	 * changes, update sequences) regardless of that setting.
+	 */
+	SetConfigOption("default_transaction_read_only", "off", PGC_SUSET,
+					PGC_S_OVERRIDE);
+
 	ApplyContext = AllocSetContextCreate(TopMemoryContext,
 										 "ApplyContext",
 										 ALLOCSET_DEFAULT_SIZES);
diff --git a/src/test/subscription/t/036_sequences.pl b/src/test/subscription/t/036_sequences.pl
index 8b02b24a7e9..b72860add7f 100644
--- a/src/test/subscription/t/036_sequences.pl
+++ b/src/test/subscription/t/036_sequences.pl
@@ -188,6 +188,57 @@ is($result, '1|f',
 	'REFRESH PUBLICATION will not sync newly published sequence with copy_data as false'
 );
 
+##########
+# Ensure that ALTER SUBSCRIPTION ... REFRESH SEQUENCES can still update
+# sequence values and mark the sequence as ready even when
+# default_transaction_read_only is enabled on the subscriber.
+##########
+
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	ALTER SYSTEM SET default_transaction_read_only = on;
+	SELECT pg_reload_conf();
+));
+
+# Update the existing sequence 'regress_s3' on the publisher
+$node_publisher->safe_psql(
+	'postgres', qq(
+	INSERT INTO regress_seq_test SELECT nextval('regress_s3') FROM generate_series(1,100);
+));
+
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	set default_transaction_read_only = off;
+	ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES;
+));
+$node_subscriber->poll_query_until('postgres', $synced_query)
+  or die "Timed out while waiting for subscriber to synchronize data";
+
+# Check - sequence value is updated despite default_transaction_read_only
+# being enabled on the subscriber
+$result = $node_subscriber->safe_psql(
+	'postgres', qq(
+	SELECT last_value, is_called FROM regress_s3;
+));
+is($result, '200|t',
+	'REFRESH SEQUENCES updates sequence value with default_transaction_read_only enabled'
+);
+
+# Check - sequence is marked as ready ('r')
+$result = $node_subscriber->safe_psql(
+	'postgres', qq(
+	SELECT srsubstate FROM pg_subscription_rel WHERE srrelid = 'regress_s3'::regclass;
+));
+is($result, 'r',
+	'sequence is marked as ready after REFRESH SEQUENCES with default_transaction_read_only enabled'
+);
+
+$node_subscriber->safe_psql(
+	'postgres', qq(
+	ALTER SYSTEM SET default_transaction_read_only = off;
+	SELECT pg_reload_conf();
+));
+
 ##########
 # ALTER SUBSCRIPTION ... REFRESH PUBLICATION should report an error when:
 # a) sequence definitions differ between the publisher and subscriber, or
-- 
2.50.1 (Apple Git-155)

