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
 
-- 

Reply via email to