From 2a808c27c6c1ed444e6667d0e8eb1f8dde24fa3b Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Mon, 13 Jul 2026 18:09:25 +0530
Subject: [PATCH v1] Fix sequence synchronization for concurrently dropped
 sequences

A sequence can be dropped concurrently after the sequence synchronization
worker has collected its metadata but before it reads the sequence data.
In this case, 'has_sequence_privilege()' returns NULL rather than false.

'get_and_validate_seq_info()' assumed the privilege value was never NULL,
causing an assertion failure in assert-enabled builds.

Treat a NULL privilege value as indicating that the sequence no longer
exists and report it using the existing "missing sequence on publisher"
path.
---
 .../replication/logical/sequencesync.c        | 11 ++--
 src/test/subscription/t/036_sequences.pl      | 50 ++++++++++++++++++-
 2 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c
index 770fa5de10b..0b34403569c 100644
--- a/src/backend/replication/logical/sequencesync.c
+++ b/src/backend/replication/logical/sequencesync.c
@@ -290,12 +290,15 @@ get_and_validate_seq_info(TupleTableSlot *slot, Relation *sequence_rel,
 		(LogicalRepSequenceInfo *) list_nth(seqinfos, *seqidx);
 
 	/*
-	 * The remote sequence state can be NULL if the publisher lacks the
-	 * required privileges or if the sequence was dropped concurrently after
-	 * it was identified in the catalog snapshot (see pg_get_sequence_data()).
+	 * has_sequence_privilege() itself returns NULL, rather than false, when
+	 * the sequence has been dropped concurrently after it was identified in
+	 * the catalog snapshot (see has_sequence_privilege_id()). Treat that the
+	 * same as the concurrent-drop case detected via the data columns below,
+	 * rather than misreporting it as an insufficient-privilege failure.
 	 */
 	remote_has_select_priv = DatumGetBool(slot_getattr(slot, ++col, &isnull));
-	Assert(!isnull);
+	if (isnull)
+		return COPYSEQ_SKIPPED;
 
 	datum = slot_getattr(slot, ++col, &isnull);
 	if (isnull)
diff --git a/src/test/subscription/t/036_sequences.pl b/src/test/subscription/t/036_sequences.pl
index 2a0819aaf01..58384d1f710 100644
--- a/src/test/subscription/t/036_sequences.pl
+++ b/src/test/subscription/t/036_sequences.pl
@@ -188,6 +188,54 @@ is($result, '1|f',
 	'REFRESH PUBLICATION will not sync newly published sequence with copy_data as false'
 );
 
+##########
+# A sequence dropped concurrently on the publisher, while the sequencesync
+# worker's batch query is executing, must be treated the same as any other
+# concurrently-dropped sequence (reported as "missing sequence on publisher").
+##########
+
+my $log_offset = -s $node_subscriber->logfile;
+
+# Block the sequencesync worker's batch query on the publisher: an
+# uncommitted DROP SEQUENCE holds AccessExclusiveLock, on which the
+# pg_get_sequence_data() call in the batch query will wait.
+my $pub_session = $node_publisher->background_psql('postgres');
+$pub_session->query_safe(
+	qq(
+	BEGIN;
+	DROP SEQUENCE regress_s3;
+));
+
+$node_subscriber->safe_psql('postgres',
+	"ALTER SUBSCRIPTION regress_seq_sub REFRESH SEQUENCES");
+
+# Wait until the worker's batch query is blocked on the still uncommitted
+# DROP.
+$node_publisher->poll_query_until(
+	'postgres', qq(
+	SELECT EXISTS (
+		SELECT 1 FROM pg_locks
+		WHERE relation = 'regress_s3'::regclass AND NOT granted);
+)) or die "timed out waiting for sequencesync worker to block on publisher";
+
+# Commit the DROP while the batch query is blocked inside it, so the query
+# resumes against a sequence that no longer exists.
+$pub_session->query_safe("COMMIT");
+$pub_session->quit;
+
+$node_subscriber->wait_for_log(
+	qr/WARNING: ( [A-Z0-9]+:)? missing sequence on publisher \("public.regress_s3"\)/,
+	$log_offset);
+
+$node_publisher->safe_psql(
+	'postgres', qq(
+	create sequence regress_s3;
+));
+
+# Wait for the missing sequence added to be synced
+$node_subscriber->poll_query_until('postgres', $synced_query)
+  or die "Timed out while waiting for subscriber to synchronize data";
+
 ##########
 # ALTER SUBSCRIPTION ... REFRESH PUBLICATION should report an error when:
 # a) sequence definitions differ between the publisher and subscriber, or
@@ -206,7 +254,7 @@ $node_subscriber->safe_psql(
 	CREATE SEQUENCE regress_s4 START 10 INCREMENT 2;
 ));
 
-my $log_offset = -s $node_subscriber->logfile;
+$log_offset = -s $node_subscriber->logfile;
 
 # Do ALTER SUBSCRIPTION ... REFRESH PUBLICATION
 $node_subscriber->safe_psql('postgres',
-- 
2.50.1 (Apple Git-155)

