| From: | Nikhil Sontakke <nikhil(at)planetscale(dot)com> |
|---|---|
| To: | PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Subject: | Re: Dropping a composite attribute causes data integrity violations |
| Date: | 2026-08-19 17:41:17 |
| Message-ID: | CA+UBoq3D5SPuDGFQN5MVp09AYEV2SZz2twgyuGnuwigMrN7BHA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
I added this to the open commitfest at
https://commitfest.postgresql.org/patch/7169/
Also, please find attached v2, which addresses additional required test
changes.
Regards,
Nikhil
On Mon, Aug 17, 2026 at 12:33 PM Nikhil Sontakke <nikhil(at)planetscale(dot)com>
wrote:
> Hi,
>
> A composite datum records the OID and typmod of its row type and nothing
> about the shape it was built with, so every value is read back against
> whatever the type looks like currently at read time. Nothing prevents
> the below for example:
>
> CREATE TYPE ct AS (a int, b int);
> CREATE TABLE t (v ct);
> CREATE UNIQUE INDEX t_v ON t (v);
> INSERT INTO t VALUES (ROW(1, 2)::ct), (ROW(1, 3)::ct); -- accepted
>
> ALTER TYPE ct DROP ATTRIBUTE b; -- accepted today! this violates UNIQUE
>
> SELECT count(DISTINCT v) FROM t;
> 1
>
> The rows are on disk and were committed before the ALTER; no cursor or
> plan is involved, and the incorrect reading survives a restart.
>
> INSERT INTO t VALUES (ROW(1)::ct);
> ERROR: duplicate key value violates unique constraint "t_v"
> DETAIL: Key (v)=((1)) already exists.
>
> REINDEX INDEX t_v;
> ERROR: could not create unique index "t_v"
> DETAIL: Key (v)=((1)) is duplicated.
>
> Worse, a plain pg_dump of that database fails to restore for the
> same reason.
>
> The check for this exists. On the same table, before the drop, retyping
> the attribute is refused:
>
> ALTER TYPE ct ALTER ATTRIBUTE b TYPE bigint;
> ERROR: cannot alter type "ct" because column "t.v" uses it
>
> but the call is gated on a rewrite being queued,
>
> if (tab->newvals != NIL || tab->rewrite > 0)
> find_composite_type_dependencies(...);
>
> and a drop produces neither, so it walks past.
>
> The same applies to ALTER TABLE ... DROP COLUMN, since a table's row type
> is a composite type too, and there too the retyping form is already
> refused.
>
> The attached patch calls the existing function from the drop path, after
> the column is checked for droppability and before recursion to
> inheritance children, so both calls and every level are covered.
> Regression, isolation and pg_upgrade suites pass.
>
> I added new tests and had to modify a couple of existing tests because of
> this
> behavior change.
>
> Regards,
> Nikhil
> ---
> Nikhil Sontakke
> PlanetScale Postgres Core Team
>
| Attachment | Content-Type | Size |
|---|---|---|
| v2-0001-Refuse-to-drop-a-column-whose-row-type-is-stored-.patch | application/octet-stream | 11.8 KB |
| From | Date | Subject | |
|---|---|---|---|
| Previous Message | Tomas Vondra | 2026-08-19 17:31:18 | Re: hashjoins vs. Bloom filters (yet again) |