| From: | Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com> |
|---|---|
| To: | Aleksander Alekseev <aleksander(at)tigerdata(dot)com> |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org, Ante Krešić <ante(at)tigerdata(dot)com> |
| Subject: | Re: [PATCH] REPLICA IDENTITY USING INDEX accepts column with invalid NOT NULL |
| Date: | 2026-06-11 18:48:46 |
| Message-ID: | CAD21AoBkPc8y6QCKrLAZVzmwbAK4U_h5_53udvEkOoqXRN503g@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Tue, May 26, 2026 at 2:36 AM Aleksander Alekseev
<aleksander(at)tigerdata(dot)com> wrote:
>
> Hi Ante,
>
> > ALTER TABLE ... REPLICA IDENTITY USING INDEX checks key columns by
> > reading attnotnull, but since a379061a22a8 (PG 18, NOT NULL NOT VALID)
> > attnotnull is set even when the constraint is unvalidated. An index on
> > a column that actually contains NULLs is therefore accepted as replica
> > identity:
> >
> > CREATE TABLE t (id int);
> > INSERT INTO t VALUES (1), (NULL), (2);
> > ALTER TABLE t ADD CONSTRAINT id_nn NOT NULL id NOT VALID;
> > CREATE UNIQUE INDEX t_idx ON t(id);
> > ALTER TABLE t REPLICA IDENTITY USING INDEX t_idx; -- accepted
> > -- relreplident => 'i'
> >
> > This would cause data divergence on UPDATE/DELETE targeting NULL-keyed rows.
> >
> > Patch follows same pattern as d9ffc27291f (the same bugfix for identity columns);
> > after the attnotnull check, look up the constraint and reject if !convalidated.
>
Good catch!
> Thanks for the patch. This clearly seems to be a bug.
>
> The patch is well written and has regression tests. The tests pass, I
> also tested under Valgrind. All in all the patch looks good to me.
Regarding the patch,
+ /*
+ * attnotnull is set even for invalid (NOT VALID) not-null
+ * constraints, which do not prove the column is
null-free, so verify
+ * that the underlying constraint is validated.
+ */
+ {
+ HeapTuple contup;
+ Form_pg_constraint conForm;
+
+ contup =
findNotNullConstraintAttnum(RelationGetRelid(rel), attno);
+ if (!HeapTupleIsValid(contup))
+ elog(ERROR, "cache lookup failed for
not-null constraint on column \"%s\" of relation \"%s\"",
+ NameStr(attr->attname),
RelationGetRelationName(rel));
+
+ conForm = (Form_pg_constraint) GETSTRUCT(contup);
+ if (!conForm->convalidated)
+ ereport(ERROR,
+
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("index \"%s\"
cannot be used as replica identity because column \"%s\" has an
invalid not-null constraint",
+
RelationGetRelationName(indexRel),
+
NameStr(attr->attname)),
+ /*- translator: second %s is a
constraint characteristic such as NOT VALID */
+ errdetail("The
constraint \"%s\" is marked %s.",
+
NameStr(conForm->conname), "NOT VALID"),
+ errhint("You might
need to validate it using %s.",
+ "ALTER
TABLE ... VALIDATE CONSTRAINT"));
+ heap_freetuple(contup);
+ }
It would be better to factor the into a common function that checks
whether the column has a valid NOT NULL constraint, to remove the code
duplication.
Regards,
--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Tom Lane | 2026-06-11 18:57:01 | Re: Make SPI_prepare argtypes argument const |
| Previous Message | Peter Eisentraut | 2026-06-11 18:42:05 | Re: Fix missing semicolon in pl_gram.y for option_value rule |