Hi,
On Mon, 28 Sept 2026 at 18:59, Nitin Motiani <
nitinmotiani@google.com> wrote:
> The following bug was reported in [1]
>
> ```
> CREATE TYPE rt AS (i int);
> CREATE DOMAIN dt AS int CHECK ((ROW(value)::rt).i > 0);
> ALTER TYPE rt ALTER ATTRIBUTE i TYPE bigint;
>
>
>
> triggers an internal
> ERROR: XX000: could not identify relation associated with constraint 16390
> LOCATION: ATPostAlterTypeCleanup, tablecmds.c:16147
>
> Reproduced starting from af20e2d72.
>
> ```
>
> I am not sure how common this scenario is but I investigated the
> history of the commit af20e2d72. LLM pointed me to [2] from 2017.
>
> The thread mentions a similar case in the email but it isn't covered
> in the test cases.
>
> ```
> regression=# create type comptype as (r float8, i float8);
> CREATE TYPE
> regression=# create domain silly as float8 check
> ((row(value,0)::comptype).r > 0);
> CREATE DOMAIN
> regression=# alter type comptype alter attribute r type varchar;
> ERROR: cache lookup failed for relation 0
> ```
>
> Therefore I am attaching a patch file with a proposed fix. The issue
> stems from the fact that for a domain constraint, we currently look
> for the domain's base type and the corresponding relid. But if the
> domain is over a primitive type like int, there is no relid. And
> therefore it fails.
>
> My understanding of code is that this relid is only being used in
> ATPostAlterTypeParse to unqueue the entry corresponding to the type
> being altered.
>
> So in this patch instead of getting the relid from the domain, we use
> the relid of the type being altered.
>
> I tested changing int to bigint and text to ensure it passes in the
> first case and fails in the second.
>
> Please take a look and let me know what you think.
Thanks for working on this. Switching to tab->relid looks right to me,
since a domain constraint doesn't belong to any relation anyway.
I think there's still a problem when a table that uses the domain gets
rewritten by the same ALTER, though. The re-added constraint is
validated right away in phase 2, before phase 3 has rewritten those
tables, so their old rows are read with the new tuple descriptor. With
the patch this crashes for me:
create table dtab (i int);
create domain dtext as text check ((row(1)::dtab).i > 0 and md5(value) <> '');
create table dtab_child (t dtext) inherits (dtab);
insert into dtab_child values (1, repeat('x', 200));
alter table dtab alter column i type bigint;
On master it just fails with the elog. (FWIW master, and 14 too as far
as I checked, can already crash like this with a domain over the
parent's rowtype stored in the child, so it's not entirely new.)
Maybe the constraint could be re-added as NOT VALID in phase 2, and then
validated once phase 3 is done, e.g. with AlterDomainValidateConstraint()
next to the FK checks at the end of ATRewriteTables()?
Also, I believe the test cases can be trimmed.
For the tests, all three cases take the same path and none of them
stores the domain in a table. One test for the reported case plus one
like the above might be enough.
Regards,
Ayush
[1]
https://www.postgresql.org/message-id/flat/19724-58468097b5b17d10%40postgresql.org[2]
https://www.postgresql.org/message-id/flat/30656.1509128130%40sss.pgh.pa.us