Hi, 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. [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 Regards & Thanks, Nitin Motiani Google
From e435c2e31abe47935e74a3d0f547ba5476b5c1c7 Mon Sep 17 00:00:00 2001 From: Nitin Motiani <[email protected]> Date: Mon, 28 Sep 2026 12:49:33 +0000 Subject: [PATCH v1] Fix ALTER TYPE ... ALTER ATTRIBUTE on types used in domain constraints. Commit af20e2d72 updated ALTER TABLE / TYPE to rebuild domain constraints when an attribute of a composite type is altered. However, it assumed that the domain's base type was always the composite type being altered, calling get_typ_typrelid(getBaseType(con->contypid)). If the domain was defined over a scalar type (such as int or float8) whose CHECK expression referenced the composite type, get_typ_typrelid() returned InvalidOid, triggering an internal "could not identify relation associated with constraint" error. Fix by attaching the deferred domain constraint rebuild command to the table being altered (tab->relid) rather than attempting to derive a relation OID from the domain's base type. Domains do not have pg_class relations of their own, and the rebuild command (AlterDomainStmt) is self-contained. Reported-by: Alexander Lakhin Bug: #19724 --- src/backend/commands/tablecmds.c | 10 +++--- src/test/regress/expected/domain.out | 50 ++++++++++++++++++++++++++++ src/test/regress/sql/domain.sql | 33 ++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 0274d892f2e..c8bc193a2ab 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16141,10 +16141,12 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) relid = con->conrelid; else { - /* must be a domain constraint */ - relid = get_typ_typrelid(getBaseType(con->contypid)); - if (!OidIsValid(relid)) - elog(ERROR, "could not identify relation associated with constraint %u", oldId); + /* + * Must be a domain constraint. Domains don't have their own + * relations, so attach the rebuild step to the table being + * altered. + */ + relid = tab->relid; } confrelid = con->confrelid; conislocal = con->conislocal; diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 62a48a523a2..de60a90c045 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -432,6 +432,56 @@ select conname, obj_description(oid, 'pg_constraint') from pg_constraint drop type comptype cascade; NOTICE: drop cascades to type dcomptype +-- regression tests for bug #19724 +-- test scenario from bug report, plus failure when changing int to text +create type rt as (i int); +create domain dt as int check ((row(value)::rt).i > 0); +alter type rt alter attribute i type text; -- fail +ERROR: operator does not exist: text > integer +DETAIL: No operator of that name accepts the given argument types. +HINT: You might need to add explicit type casts. +alter type rt alter attribute i type bigint; +select 1::dt; + dt +---- + 1 +(1 row) + +select (-1)::dt; -- fail +ERROR: value for domain dt violates check constraint "dt_check" +drop domain dt; +drop type rt cascade; +-- test silly example from Tom Lane's 2017 email (domain over float8) +create type comptype as (r float8, i float8); +create domain silly as float8 check ((row(value, 0)::comptype).r > 0); +alter type comptype alter attribute r type bigint; +select 1.0::silly; + silly +------- + 1 +(1 row) + +select (-1.0)::silly; -- fail +ERROR: value for domain silly violates check constraint "silly_check" +drop domain silly; +drop type comptype cascade; +-- test domain constraint referencing multiple composite types +create type r1 as (a int); +create type r2 as (b int); +create domain dt_multi as int check ((row(value)::r1).a > 0 and (row(value)::r2).b > 0); +alter type r1 alter attribute a type bigint; +alter type r2 alter attribute b type bigint; +select 1::dt_multi; + dt_multi +---------- + 1 +(1 row) + +select (-1)::dt_multi; -- fail +ERROR: value for domain dt_multi violates check constraint "dt_multi_check" +drop domain dt_multi; +drop type r1 cascade; +drop type r2 cascade; -- Test domains over arrays of composite create type comptype as (r float8, i float8); create domain dcomptypea as comptype[]; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index b8f5a639712..1240f9422bd 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -219,6 +219,39 @@ select conname, obj_description(oid, 'pg_constraint') from pg_constraint drop type comptype cascade; +-- regression tests for bug #19724 + +-- test scenario from bug report, plus failure when changing int to text +create type rt as (i int); +create domain dt as int check ((row(value)::rt).i > 0); +alter type rt alter attribute i type text; -- fail +alter type rt alter attribute i type bigint; +select 1::dt; +select (-1)::dt; -- fail +drop domain dt; +drop type rt cascade; + +-- test silly example from Tom Lane's 2017 email (domain over float8) +create type comptype as (r float8, i float8); +create domain silly as float8 check ((row(value, 0)::comptype).r > 0); +alter type comptype alter attribute r type bigint; +select 1.0::silly; +select (-1.0)::silly; -- fail +drop domain silly; +drop type comptype cascade; + +-- test domain constraint referencing multiple composite types +create type r1 as (a int); +create type r2 as (b int); +create domain dt_multi as int check ((row(value)::r1).a > 0 and (row(value)::r2).b > 0); +alter type r1 alter attribute a type bigint; +alter type r2 alter attribute b type bigint; +select 1::dt_multi; +select (-1)::dt_multi; -- fail +drop domain dt_multi; +drop type r1 cascade; +drop type r2 cascade; + -- Test domains over arrays of composite --
