]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix RI fast-path for domain-typed FK columns
authorAmit Langote <amitlan@postgresql.org>
Wed, 17 Jun 2026 02:15:53 +0000 (11:15 +0900)
committerAmit Langote <amitlan@postgresql.org>
Wed, 17 Jun 2026 02:15:53 +0000 (11:15 +0900)
The RI fast path is the first caller to pass a cross-type pf_eq_oprs
operator to ri_HashCompareOp().  Its test for whether a cast can be
skipped, "typeid == righttype", failed when the FK column was a domain,
since typeid is then the domain OID rather than its base type.  The code
concluded no usable conversion existed and threw "no conversion function
from <domain> to <type>" for every valid row.

Look through the domain to its base type.  When pfeqop comes directly
from the index opfamily its right-hand input is getBaseType(fktype), so
getBaseType(typeid) == righttype is the correct test; the PK = PK
fallback (right-hand input opcintype) still fails that test and falls
through to the existing cast lookup unchanged.

Oversight in commit 2da86c1.

Reported-by: Ewan Young <kdbase.hack@gmail.com>
Author: Ewan Young <kdbase.hack@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Discussion: https://postgr.es/m/CAON2xHNDFC4cX2atvTpMuC=cK9y7q4J+n3+15w4148AohXEc1w@mail.gmail.com

src/backend/utils/adt/ri_triggers.c
src/test/regress/expected/foreign_key.out
src/test/regress/sql/foreign_key.sql

index 06e7728c45d34f8ea473411d4669290d418f8081..44129a35c0895818d6434f3d05460e350edf86fd 100644 (file)
@@ -4113,10 +4113,11 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid)
                /*
                 * pf_eq_oprs (used by the fast path) can be cross-type when the FK
                 * and PK columns differ in type, e.g. int48eq for int4 PK / int8 FK.
-                * If the FK column's type already matches what the operator expects
-                * as its right-hand input, no cast is needed.
+                * If the FK column's type, or the base type of a domain over it,
+                * already matches what the operator expects as its right-hand input,
+                * no cast is needed.
                 */
-               if (typeid == righttype)
+               if (getBaseType(typeid) == righttype)
                        castfunc = InvalidOid;  /* simplest case */
                else
                {
index e1563144d4c85f6e1e04d416a597895e32e0c728..c334cce752c06627c40b907c2e8e9a13912ba357 100644 (file)
@@ -3705,6 +3705,21 @@ INSERT INTO fp_fk_cross VALUES (999);
 ERROR:  insert or update on table "fp_fk_cross" violates foreign key constraint "fp_fk_cross_a_fkey"
 DETAIL:  Key (a)=(999) is not present in table "fp_pk_cross".
 DROP TABLE fp_fk_cross, fp_pk_cross;
+-- Domain-typed FK column whose base type differs from the PK type: the
+-- fast path must look through the domain to its base type when deciding
+-- whether the cross-type comparison needs a cast.  Otherwise valid rows
+-- were wrongly rejected with "no conversion function from ... to ...".
+CREATE DOMAIN fp_int8dom AS int8;
+CREATE TABLE fp_pk_dom (a int4 PRIMARY KEY);
+INSERT INTO fp_pk_dom SELECT generate_series(1, 200);
+CREATE TABLE fp_fk_dom (a fp_int8dom REFERENCES fp_pk_dom);
+INSERT INTO fp_fk_dom SELECT generate_series(1, 200);
+INSERT INTO fp_fk_dom VALUES (999);
+ERROR:  insert or update on table "fp_fk_dom" violates foreign key constraint "fp_fk_dom_a_fkey"
+DETAIL:  Key (a)=(999) is not present in table "fp_pk_dom".
+INSERT INTO fp_fk_dom VALUES (NULL);
+DROP TABLE fp_fk_dom, fp_pk_dom;
+DROP DOMAIN fp_int8dom;
 -- Duplicate FK values: when using the batched SAOP path, every
 -- row must be recognized as satisfied, not just the first match
 CREATE TABLE fp_pk_dup (a int PRIMARY KEY);
index abeb85965b9975472a1321c5a837251c94e086c5..17eadc4bb5ad540f7a083ac9efbdad4d2839e98c 100644 (file)
@@ -2673,6 +2673,20 @@ INSERT INTO fp_fk_cross SELECT generate_series(1, 200);
 INSERT INTO fp_fk_cross VALUES (999);
 DROP TABLE fp_fk_cross, fp_pk_cross;
 
+-- Domain-typed FK column whose base type differs from the PK type: the
+-- fast path must look through the domain to its base type when deciding
+-- whether the cross-type comparison needs a cast.  Otherwise valid rows
+-- were wrongly rejected with "no conversion function from ... to ...".
+CREATE DOMAIN fp_int8dom AS int8;
+CREATE TABLE fp_pk_dom (a int4 PRIMARY KEY);
+INSERT INTO fp_pk_dom SELECT generate_series(1, 200);
+CREATE TABLE fp_fk_dom (a fp_int8dom REFERENCES fp_pk_dom);
+INSERT INTO fp_fk_dom SELECT generate_series(1, 200);
+INSERT INTO fp_fk_dom VALUES (999);
+INSERT INTO fp_fk_dom VALUES (NULL);
+DROP TABLE fp_fk_dom, fp_pk_dom;
+DROP DOMAIN fp_int8dom;
+
 -- Duplicate FK values: when using the batched SAOP path, every
 -- row must be recognized as satisfied, not just the first match
 CREATE TABLE fp_pk_dup (a int PRIMARY KEY);