Allow casting a table's row type to the table's supertype if it's a typed table
authorPeter Eisentraut <peter_e@gmx.net>
Sat, 1 Jan 2011 20:44:32 +0000 (22:44 +0200)
committerPeter Eisentraut <peter_e@gmx.net>
Sat, 1 Jan 2011 21:04:14 +0000 (23:04 +0200)
This is analogous to the existing facility that allows casting a row type to a
supertable's row type.

src/backend/parser/parse_coerce.c
src/test/regress/expected/typed_table.out
src/test/regress/sql/typed_table.sql

index 33a60a1edb4a1fe320f90be0ec92d241ecd9fbaa..5b0dc1420d0476c78c1565c4ee129c07003adeae 100644 (file)
@@ -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;
+}
index 6db8d4c803158a97e83022147dfd1a06e2fa3e9f..0874a64d55463c0c723613eee17a6a31ed8af309 100644 (file)
@@ -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)
index 1afede14b1040a838196ffb4adaaedb038ca9148..b0d452c387ed0ec152493dd60ed727fe1df67a0b 100644 (file)
@@ -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;