On Tue, 20 Jan 2026 at 09:47, Amit Kapila <[email protected]> wrote: > > On Mon, Jan 19, 2026 at 6:36 PM vignesh C <[email protected]> wrote: > > > > pg_get_sequence_data() internally uses try_relation_open() rather than > > relation_open(). As a result, if the target sequence no longer exists > > at the time of access, the function does not raise an error and > > instead returns NULLs for the sequence state columns. The sequence > > sync worker code previously assumed these columns to be non NULL and > > asserted accordingly. This assumption does not hold in the presence of > > concurrent DDL. The patch updates the sequence sync logic to > > explicitly check for NULL values returned from pg_get_sequence_data(). > > If any of the required sequence state fields are NULL, the sequence > > sync worker skips processing that sequence to identify and report the > > missing sequences. The attached patch has the changes for the same. > > > > - seqinfo_local->last_value = DatumGetInt64(slot_getattr(slot, ++col, > &isnull)); > - Assert(!isnull); > + /* > + * If the sequence was dropped concurrently, pg_get_sequence_data() can > + * return NULLs. > + */ > + datum = slot_getattr(slot, ++col, &isnull); > + if (isnull) > + return COPYSEQ_SKIPPED; > + seqinfo_local->last_value = DatumGetInt64(datum); > > - seqinfo_local->is_called = DatumGetBool(slot_getattr(slot, ++col, &isnull)); > - Assert(!isnull); > + datum = slot_getattr(slot, ++col, &isnull); > + if (isnull) > + return COPYSEQ_SKIPPED; > + seqinfo_local->is_called = DatumGetBool(datum); > > Is there a case where the first one (last_value) is non-null but later > can be null? If not, then I think it is better to retain assert for > other cases.
That is not possible. Updated accordingly with slight change to comment. The attached patch has the changes for the same. Regards, Vignesh
From 16d1f24d30371da2a87025c98e2dae5d4954f482 Mon Sep 17 00:00:00 2001 From: Vignesh C <[email protected]> Date: Mon, 19 Jan 2026 18:25:23 +0530 Subject: [PATCH v2] Handle concurrent sequence drop during sequence sync pg_get_sequence_data() may return NULL values when a sequence is dropped concurrently, as it uses try_relation_open() internally. The sequence sync worker previously assumed non-NULL values for sequence state fields, leading to assertion failures when concurrent DDL occurred. Fix it by checking NULL sequence state values and skip the affected sequence gracefully. --- src/backend/replication/logical/sequencesync.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c index 509388d1bf4..2a18c24586c 100644 --- a/src/backend/replication/logical/sequencesync.c +++ b/src/backend/replication/logical/sequencesync.c @@ -233,6 +233,7 @@ get_and_validate_seq_info(TupleTableSlot *slot, Relation *sequence_rel, { bool isnull; int col = 0; + Datum datum; Oid remote_typid; int64 remote_start; int64 remote_increment; @@ -251,8 +252,14 @@ get_and_validate_seq_info(TupleTableSlot *slot, Relation *sequence_rel, *seqinfo = seqinfo_local = (LogicalRepSequenceInfo *) list_nth(seqinfos, *seqidx); - seqinfo_local->last_value = DatumGetInt64(slot_getattr(slot, ++col, &isnull)); - Assert(!isnull); + /* + * last_value can be NULL if the sequence was dropped concurrently (see + * pg_get_sequence_data()). + */ + datum = slot_getattr(slot, ++col, &isnull); + if (isnull) + return COPYSEQ_SKIPPED; + seqinfo_local->last_value = DatumGetInt64(datum); seqinfo_local->is_called = DatumGetBool(slot_getattr(slot, ++col, &isnull)); Assert(!isnull); @@ -400,7 +407,7 @@ copy_sequences(WalReceiverConn *conn) int batch_skipped_count = 0; int batch_insuffperm_count = 0; int batch_missing_count; - Relation sequence_rel; + Relation sequence_rel = NULL; WalRcvExecResult *res; TupleTableSlot *slot; -- 2.43.0
