Re: Dropping a composite attribute causes data integrity violations

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

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2026-08-19 18:09:50 Re: Residual cleanups for tied objects in PL/Perl
Previous Message Tomas Vondra 2026-08-19 17:31:18 Re: hashjoins vs. Bloom filters (yet again)