diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 33a60a1edb4a1fe320f90be0ec92d241ecd9fbaa..5b0dc1420d0476c78c1565c4ee129c07003adeae 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "catalog/pg_cast.h" +#include "catalog/pg_class.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -48,6 +49,7 @@ static Node *coerce_record_to_complex(ParseState *pstate, Node *node, CoercionForm cformat, int location); static bool is_complex_array(Oid typid); +static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId); /* @@ -371,7 +373,8 @@ coerce_type(ParseState *pstate, Node *node, /* NB: we do NOT want a RelabelType here */ return node; } - if (typeInheritsFrom(inputTypeId, targetTypeId)) + if (typeInheritsFrom(inputTypeId, targetTypeId) + || typeIsOfTypedTable(inputTypeId, targetTypeId)) { /* * Input class type is a subclass of target, so generate an @@ -482,7 +485,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, /* * If input is a class type that inherits from target, accept */ - if (typeInheritsFrom(inputTypeId, targetTypeId)) + if (typeInheritsFrom(inputTypeId, targetTypeId) + || typeIsOfTypedTable(inputTypeId, targetTypeId)) continue; /* @@ -2046,3 +2050,34 @@ is_complex_array(Oid typid) return (OidIsValid(elemtype) && ISCOMPLEX(elemtype)); } + + +/* + * Check whether reltypeId is the row type of a typed table of type + * reloftypeId. (This is conceptually similar to the subtype + * relationship checked by typeInheritsFrom().) + */ +static bool +typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId) +{ + Oid relid = typeidTypeRelid(reltypeId); + bool result = false; + + if (relid) + { + HeapTuple tp; + Form_pg_class reltup; + + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + reltup = (Form_pg_class) GETSTRUCT(tp); + if (reltup->reloftype == reloftypeId) + result = true; + + ReleaseSysCache(tp); + } + + return result; +} diff --git a/src/test/regress/expected/typed_table.out b/src/test/regress/expected/typed_table.out index 6db8d4c803158a97e83022147dfd1a06e2fa3e9f..0874a64d55463c0c723613eee17a6a31ed8af309 100644 --- a/src/test/regress/expected/typed_table.out +++ b/src/test/regress/expected/typed_table.out @@ -92,3 +92,18 @@ drop cascades to function get_all_persons() drop cascades to table persons2 drop cascades to table persons3 DROP TABLE stuff; +-- implicit casting +CREATE TYPE person_type AS (id int, name text); +CREATE TABLE persons OF person_type; +INSERT INTO persons VALUES (1, 'test'); +CREATE FUNCTION namelen(person_type) RETURNS int LANGUAGE SQL AS $$ SELECT length($1.name) $$; +SELECT id, namelen(persons) FROM persons; + id | namelen +----+--------- + 1 | 4 +(1 row) + +DROP TYPE person_type CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table persons +drop cascades to function namelen(person_type) diff --git a/src/test/regress/sql/typed_table.sql b/src/test/regress/sql/typed_table.sql index 1afede14b1040a838196ffb4adaaedb038ca9148..b0d452c387ed0ec152493dd60ed727fe1df67a0b 100644 --- a/src/test/regress/sql/typed_table.sql +++ b/src/test/regress/sql/typed_table.sql @@ -47,3 +47,15 @@ DROP TYPE person_type RESTRICT; DROP TYPE person_type CASCADE; DROP TABLE stuff; + + +-- implicit casting + +CREATE TYPE person_type AS (id int, name text); +CREATE TABLE persons OF person_type; +INSERT INTO persons VALUES (1, 'test'); + +CREATE FUNCTION namelen(person_type) RETURNS int LANGUAGE SQL AS $$ SELECT length($1.name) $$; +SELECT id, namelen(persons) FROM persons; + +DROP TYPE person_type CASCADE;