stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types) - Mailing list pgsql-patches

From Joe Conway
Subject stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types)
Date
Msg-id 3D51F2DB.3070204@joeconway.com
Whole thread Raw
Responses Re: stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types)
List pgsql-patches
Joe Conway wrote:
 > Based on Tom's suggestion, I propose the following:
 >
 > 1. Define a new pg_class relkind as 'c' for composite. Currently relkind
 >    can be: 'S' sequence, 'i' index, 'r' relation, 's' special, 't'
 >    toast, and 'v' view.
 >
 > 2. Borrow the needed parts from CREATE and DROP VIEW to implement a new
 >    form of the CREATE TYPE command, with syntax something like:
 >
 >    CREATE TYPE typename AS ( column_name data_type [, ... ] )
 >
 >    This would add a pg_class entry of relkind 'c', and add a new
 >    pg_type entry of typtype 'c', with typrelid pointing to the
 >    pg_class entry. Essentially, this new stand-alone composite type
 >    looks a lot like a view without any rules.

Items 1 and 2 from the proposal above are implemented in the attached
patch. I was able to get rid of the reduce/reduce conflict with Tom's
help (thanks Tom!).

test=# CREATE TYPE compfoo AS (f1 int, f2 int);
CREATE TYPE
test=# CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid,
foosubid FROM foo' LANGUAGE SQL;
CREATE FUNCTION
test=# SELECT * FROM getfoo();
   f1 | f2
----+----
    1 |  1
    1 |  2
(2 rows)

test=# DROP TYPE compfoo;
NOTICE:  function getfoo() depends on type compfoo
ERROR:  Cannot drop relation compfoo because other objects depend on it
          Use DROP ... CASCADE to drop the dependent objects too
test=# DROP TYPE compfoo CASCADE;
NOTICE:  Drop cascades to function getfoo()
DROP TYPE

Passes all regression tests (well, I'm on RedHat 7.3, so there are three
"expected" failures). Doc and regression adjustments included. If there
are no objections, please apply.

Thanks,

Joe

Index: doc/src/sgml/ref/create_type.sgml
===================================================================
RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/ref/create_type.sgml,v
retrieving revision 1.30
diff -c -r1.30 create_type.sgml
*** doc/src/sgml/ref/create_type.sgml    24 Jul 2002 19:11:07 -0000    1.30
--- doc/src/sgml/ref/create_type.sgml    8 Aug 2002 03:54:32 -0000
***************
*** 30,35 ****
--- 30,42 ----
      [ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ]
      [ , STORAGE = <replaceable class="parameter">storage</replaceable> ]
  )
+
+ CREATE TYPE <replaceable class="parameter">typename</replaceable> AS
+       ( <replaceable class="PARAMETER">column_definition_list</replaceable> )
+
+ where <replaceable class="PARAMETER">column_definition_list</replaceable> can be:
+
+ ( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [,
...] ) 
    </synopsis>

    <refsect2 id="R2-SQL-CREATETYPE-1">
***************
*** 138,143 ****
--- 145,169 ----
         </para>
        </listitem>
       </varlistentry>
+
+      <varlistentry>
+       <term><replaceable class="PARAMETER">column_name</replaceable></term>
+       <listitem>
+        <para>
+         The name of a column of the composite type.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><replaceable class="PARAMETER">data_type</replaceable></term>
+       <listitem>
+        <para>
+         The name of an existing data type.
+        </para>
+       </listitem>
+      </varlistentry>
+
      </variablelist>
     </para>
    </refsect2>
***************
*** 191,199 ****
    </para>

    <para>
!    <command>CREATE TYPE</command>  requires  the  registration of two functions
!    (using CREATE FUNCTION) before defining the type.   The
!    representation  of  a  new  base  type  is  determined  by
     <replaceable class="parameter">input_function</replaceable>, which
     converts the type's external  representation  to  an  internal
     representation  usable by the
--- 217,225 ----
    </para>

    <para>
!    The first form of <command>CREATE TYPE</command>  requires  the
!    registration of two functions (using CREATE FUNCTION) before defining the
!    type. The representation of a new base type is determined by
     <replaceable class="parameter">input_function</replaceable>, which
     converts the type's external  representation  to  an  internal
     representation  usable by the
***************
*** 288,293 ****
--- 314,327 ----
     <literal>extended</literal> and <literal>external</literal> items.)
    </para>

+   <para>
+    The second form of <command>CREATE TYPE</command> requires a column
+    definition list in the form ( <replaceable class="PARAMETER">column_name</replaceable>
+    <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ). This
+    creates a composite type, similar to that of a TABLE or VIEW relation.
+    A stand-alone composite type is useful as the return type of FUNCTION.
+   </para>
+
    <refsect2>
     <title>Array Types</title>

***************
*** 370,375 ****
--- 404,418 ----
  CREATE TYPE bigobj (INPUT = lo_filein, OUTPUT = lo_fileout,
      INTERNALLENGTH = VARIABLE);
  CREATE TABLE big_objs (id int4, obj bigobj);
+ </programlisting>
+   </para>
+
+   <para>
+    This example creates a composite type and uses it in
+    a table function definition:
+ <programlisting>
+ CREATE TYPE compfoo AS (f1 int, f2 int);
+ CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, foorefid FROM foo' LANGUAGE SQL;
  </programlisting>
    </para>
   </refsect1>
Index: src/backend/catalog/heap.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/heap.c,v
retrieving revision 1.219
diff -c -r1.219 heap.c
*** src/backend/catalog/heap.c    6 Aug 2002 02:36:33 -0000    1.219
--- src/backend/catalog/heap.c    8 Aug 2002 02:49:47 -0000
***************
*** 764,770 ****
      /*
       * We create the disk file for this relation here
       */
!     if (relkind != RELKIND_VIEW)
          heap_storage_create(new_rel_desc);

      /*
--- 764,770 ----
      /*
       * We create the disk file for this relation here
       */
!     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
          heap_storage_create(new_rel_desc);

      /*
***************
*** 1135,1141 ****
      /*
       * unlink the relation's physical file and finish up.
       */
!     if (rel->rd_rel->relkind != RELKIND_VIEW)
          smgrunlink(DEFAULT_SMGR, rel);

      /*
--- 1135,1142 ----
      /*
       * unlink the relation's physical file and finish up.
       */
!     if (rel->rd_rel->relkind != RELKIND_VIEW &&
!             rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
          smgrunlink(DEFAULT_SMGR, rel);

      /*
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/typecmds.c,v
retrieving revision 1.8
diff -c -r1.8 typecmds.c
*** src/backend/commands/typecmds.c    24 Jul 2002 19:11:09 -0000    1.8
--- src/backend/commands/typecmds.c    8 Aug 2002 02:49:47 -0000
***************
*** 38,43 ****
--- 38,44 ----
  #include "catalog/namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
  #include "miscadmin.h"
  #include "parser/parse_func.h"
  #include "parser/parse_type.h"
***************
*** 49,55 ****


  static Oid findTypeIOFunction(List *procname, bool isOutput);
!

  /*
   * DefineType
--- 50,57 ----


  static Oid findTypeIOFunction(List *procname, bool isOutput);
! static void DefineCompositeTypeRelation(const RangeVar *typevar,
!                                             List *coldeflist);

  /*
   * DefineType
***************
*** 251,256 ****
--- 253,259 ----
      Oid            typeoid;
      HeapTuple    tup;
      ObjectAddress object;
+     char        typtype;

      /* Make a TypeName so we can use standard type lookup machinery */
      typename = makeNode(TypeName);
***************
*** 277,289 ****
                                   GetUserId()))
          aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));

      ReleaseSysCache(tup);

      /*
       * Do the deletion
       */
!     object.classId = RelOid_pg_type;
!     object.objectId = typeoid;
      object.objectSubId = 0;

      performDeletion(&object, behavior);
--- 280,317 ----
                                   GetUserId()))
          aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));

+     typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
+
      ReleaseSysCache(tup);

      /*
       * Do the deletion
       */
!     if (typtype != 'c')
!     {
!         object.classId = RelOid_pg_type;
!         object.objectId = typeoid;
!     }
!     else
!     {
!         Oid            relid = typeidTypeRelid(typeoid);
!         Relation    rel = relation_open(relid, AccessShareLock);
!         char        relkind = rel->rd_rel->relkind;
!
!         relation_close(rel, AccessShareLock);
!
!         if (relkind == RELKIND_COMPOSITE_TYPE)
!         {
!             object.classId = RelOid_pg_class;
!             object.objectId = relid;
!         }
!         else
!         {
!             object.classId = RelOid_pg_type;
!             object.objectId = typeoid;
!         }
!
!     }
      object.objectSubId = 0;

      performDeletion(&object, behavior);
***************
*** 665,668 ****
--- 693,758 ----
      }

      return procOid;
+ }
+
+ /*---------------------------------------------------------------------
+  * DefineCompositeTypeRelation
+  *
+  * Create a Composite Type relation.
+  * `DefineRelation' does all the work, we just provide the correct
+  * arguments!
+  *
+  * If the relation already exists, then 'DefineRelation' will abort
+  * the xact...
+  *---------------------------------------------------------------------
+  */
+ static void
+ DefineCompositeTypeRelation(const RangeVar *typevar, List *coldeflist)
+ {
+     Oid            relid;
+     CreateStmt *createStmt = makeNode(CreateStmt);
+
+     if (coldeflist == NIL)
+         elog(ERROR, "attempted to define composite type relation with"
+                     " no attrs");
+
+     /*
+      * now create the parameters for keys/inheritance etc. All of them are
+      * nil...
+      */
+     createStmt->relation = (RangeVar *) typevar;
+     createStmt->tableElts = coldeflist;
+     createStmt->inhRelations = NIL;
+     createStmt->constraints = NIL;
+     createStmt->hasoids = false;
+
+     /*
+      * finally create the relation...
+      */
+     relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
+ }
+
+ /*-------------------------------------------------------------------
+  * DefineCompositeType
+  *
+  *        - takes a "typevar", "coldeflist" pair and then
+  *        constructs the "virtual" relation
+  *-------------------------------------------------------------------
+  */
+ void
+ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
+ {
+     /*
+      * Create the composite type relation
+      *
+      * NOTE: if it already exists, the xact will be aborted.
+      */
+     DefineCompositeTypeRelation(typevar, coldeflist);
+
+     /*
+      * The relation we have just created is not visible to any other
+      * commands running with the same transaction & command id. So,
+      * increment the command id counter (but do NOT pfree any memory!!!!)
+      */
+     CommandCounterIncrement();
  }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.200
diff -c -r1.200 copyfuncs.c
*** src/backend/nodes/copyfuncs.c    4 Aug 2002 19:48:09 -0000    1.200
--- src/backend/nodes/copyfuncs.c    8 Aug 2002 02:49:47 -0000
***************
*** 2233,2238 ****
--- 2233,2249 ----
      return newnode;
  }

+ static CompositeTypeStmt *
+ _copyCompositeTypeStmt(CompositeTypeStmt *from)
+ {
+     CompositeTypeStmt   *newnode = makeNode(CompositeTypeStmt);
+
+     Node_Copy(from, newnode, typevar);
+     Node_Copy(from, newnode, coldeflist);
+
+     return newnode;
+ }
+
  static ViewStmt *
  _copyViewStmt(ViewStmt *from)
  {
***************
*** 2938,2943 ****
--- 2949,2957 ----
              break;
          case T_TransactionStmt:
              retval = _copyTransactionStmt(from);
+             break;
+         case T_CompositeTypeStmt:
+             retval = _copyCompositeTypeStmt(from);
              break;
          case T_ViewStmt:
              retval = _copyViewStmt(from);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.149
diff -c -r1.149 equalfuncs.c
*** src/backend/nodes/equalfuncs.c    4 Aug 2002 23:49:59 -0000    1.149
--- src/backend/nodes/equalfuncs.c    8 Aug 2002 02:49:47 -0000
***************
*** 1062,1067 ****
--- 1062,1078 ----
  }

  static bool
+ _equalCompositeTypeStmt(CompositeTypeStmt *a, CompositeTypeStmt *b)
+ {
+     if (!equal(a->typevar, b->typevar))
+         return false;
+     if (!equal(a->coldeflist, b->coldeflist))
+         return false;
+
+     return true;
+ }
+
+ static bool
  _equalViewStmt(ViewStmt *a, ViewStmt *b)
  {
      if (!equal(a->view, b->view))
***************
*** 2110,2115 ****
--- 2121,2129 ----
              break;
          case T_TransactionStmt:
              retval = _equalTransactionStmt(a, b);
+             break;
+         case T_CompositeTypeStmt:
+             retval = _equalCompositeTypeStmt(a, b);
              break;
          case T_ViewStmt:
              retval = _equalViewStmt(a, b);
Index: src/backend/parser/gram.y
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.357
diff -c -r2.357 gram.y
*** src/backend/parser/gram.y    6 Aug 2002 05:40:45 -0000    2.357
--- src/backend/parser/gram.y    8 Aug 2002 03:38:46 -0000
***************
*** 205,211 ****

  %type <list>    stmtblock, stmtmulti,
                  OptTableElementList, TableElementList, OptInherit, definition,
!                 opt_distinct, opt_definition, func_args,
                  func_args_list, func_as, createfunc_opt_list
                  oper_argtypes, RuleActionList, RuleActionMulti,
                  opt_column_list, columnList, opt_name_list,
--- 205,211 ----

  %type <list>    stmtblock, stmtmulti,
                  OptTableElementList, TableElementList, OptInherit, definition,
!                 opt_distinct, opt_definition, func_args, rowdefinition
                  func_args_list, func_as, createfunc_opt_list
                  oper_argtypes, RuleActionList, RuleActionMulti,
                  opt_column_list, columnList, opt_name_list,
***************
*** 2247,2252 ****
--- 2247,2285 ----
                      n->definition = $4;
                      $$ = (Node *)n;
                  }
+             | CREATE TYPE_P any_name AS rowdefinition
+                 {
+                     CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
+                     RangeVar *r = makeNode(RangeVar);
+
+                     switch (length($3))
+                     {
+                         case 1:
+                             r->catalogname = NULL;
+                             r->schemaname = NULL;
+                             r->relname = strVal(lfirst($3));
+                             break;
+                         case 2:
+                             r->catalogname = NULL;
+                             r->schemaname = strVal(lfirst($3));
+                             r->relname = strVal(lsecond($3));
+                             break;
+                         case 3:
+                             r->catalogname = strVal(lfirst($3));
+                             r->schemaname = strVal(lsecond($3));
+                             r->relname = strVal(lfirst(lnext(lnext($3))));
+                             break;
+                         default:
+                             elog(ERROR,
+                             "Improper qualified name "
+                             "(too many dotted names): %s",
+                                  NameListToString($3));
+                             break;
+                     }
+                     n->typevar = r;
+                     n->coldeflist = $5;
+                     $$ = (Node *)n;
+                 }
              | CREATE CHARACTER SET opt_as any_name GET definition opt_collate
                  {
                      DefineStmt *n = makeNode(DefineStmt);
***************
*** 2255,2260 ****
--- 2288,2296 ----
                      n->definition = $7;
                      $$ = (Node *)n;
                  }
+         ;
+
+ rowdefinition: '(' TableFuncElementList ')'            { $$ = $2; }
          ;

  definition: '(' def_list ')'                        { $$ = $2; }
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.280
diff -c -r1.280 postgres.c
*** src/backend/tcop/postgres.c    6 Aug 2002 05:24:04 -0000    1.280
--- src/backend/tcop/postgres.c    8 Aug 2002 02:49:47 -0000
***************
*** 2264,2269 ****
--- 2264,2273 ----
              }
              break;

+         case T_CompositeTypeStmt:
+             tag = "CREATE TYPE";
+             break;
+
          case T_ViewStmt:
              tag = "CREATE VIEW";
              break;
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/utility.c,v
retrieving revision 1.169
diff -c -r1.169 utility.c
*** src/backend/tcop/utility.c    7 Aug 2002 21:45:02 -0000    1.169
--- src/backend/tcop/utility.c    8 Aug 2002 02:49:47 -0000
***************
*** 573,578 ****
--- 573,586 ----
              }
              break;

+         case T_CompositeTypeStmt:        /* CREATE TYPE (composite) */
+             {
+                 CompositeTypeStmt   *stmt = (CompositeTypeStmt *) parsetree;
+
+                 DefineCompositeType(stmt->typevar, stmt->coldeflist);
+             }
+             break;
+
          case T_ViewStmt:        /* CREATE VIEW */
              {
                  ViewStmt   *stmt = (ViewStmt *) parsetree;
Index: src/include/catalog/pg_class.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_class.h,v
retrieving revision 1.70
diff -c -r1.70 pg_class.h
*** src/include/catalog/pg_class.h    2 Aug 2002 18:15:09 -0000    1.70
--- src/include/catalog/pg_class.h    8 Aug 2002 02:49:47 -0000
***************
*** 169,173 ****
--- 169,174 ----
  #define          RELKIND_UNCATALOGED      'u'        /* temporary heap */
  #define          RELKIND_TOASTVALUE      't'        /* moved off huge values */
  #define          RELKIND_VIEW              'v'        /* view */
+ #define          RELKIND_COMPOSITE_TYPE  'c'        /* composite type */

  #endif   /* PG_CLASS_H */
Index: src/include/commands/defrem.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/commands/defrem.h,v
retrieving revision 1.43
diff -c -r1.43 defrem.h
*** src/include/commands/defrem.h    29 Jul 2002 22:14:11 -0000    1.43
--- src/include/commands/defrem.h    8 Aug 2002 02:49:47 -0000
***************
*** 58,63 ****
--- 58,64 ----
  extern void RemoveTypeById(Oid typeOid);
  extern void DefineDomain(CreateDomainStmt *stmt);
  extern void RemoveDomain(List *names, DropBehavior behavior);
+ extern void DefineCompositeType(const RangeVar *typevar, List *coldeflist);

  extern void DefineOpClass(CreateOpClassStmt *stmt);
  extern void RemoveOpClass(RemoveOpClassStmt *stmt);
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/nodes.h,v
retrieving revision 1.114
diff -c -r1.114 nodes.h
*** src/include/nodes/nodes.h    29 Jul 2002 22:14:11 -0000    1.114
--- src/include/nodes/nodes.h    8 Aug 2002 02:49:47 -0000
***************
*** 238,243 ****
--- 238,244 ----
      T_PrivTarget,
      T_InsertDefault,
      T_CreateOpClassItem,
+     T_CompositeTypeStmt,

      /*
       * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/parsenodes.h,v
retrieving revision 1.198
diff -c -r1.198 parsenodes.h
*** src/include/nodes/parsenodes.h    4 Aug 2002 19:48:10 -0000    1.198
--- src/include/nodes/parsenodes.h    8 Aug 2002 02:49:47 -0000
***************
*** 1402,1407 ****
--- 1402,1419 ----
  } TransactionStmt;

  /* ----------------------
+  *        Create Type Statement, composite types
+  * ----------------------
+  */
+ typedef struct CompositeTypeStmt
+ {
+     NodeTag        type;
+     RangeVar   *typevar;        /* the composite type to be created */
+     List       *coldeflist;        /* list of ColumnDef nodes */
+ } CompositeTypeStmt;
+
+
+ /* ----------------------
   *        Create View Statement
   * ----------------------
   */
Index: src/test/regress/expected/create_type.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/create_type.out,v
retrieving revision 1.4
diff -c -r1.4 create_type.out
*** src/test/regress/expected/create_type.out    6 Sep 2001 02:07:42 -0000    1.4
--- src/test/regress/expected/create_type.out    8 Aug 2002 03:41:27 -0000
***************
*** 37,40 ****
--- 37,53 ----
   zippo | 42
  (1 row)

+ -- Test stand-alone composite type
+ CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
+ CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
+   SELECT * FROM default_test;
+ ' LANGUAGE SQL;
+ SELECT * FROM get_default_test();
+   f1   | f2
+ -------+----
+  zippo | 42
+ (1 row)
+
+ DROP TYPE default_test_row CASCADE;
+ NOTICE:  Drop cascades to function get_default_test()
  DROP TABLE default_test;
Index: src/test/regress/sql/create_type.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/create_type.sql,v
retrieving revision 1.4
diff -c -r1.4 create_type.sql
*** src/test/regress/sql/create_type.sql    6 Sep 2001 02:07:42 -0000    1.4
--- src/test/regress/sql/create_type.sql    8 Aug 2002 03:09:48 -0000
***************
*** 41,44 ****
--- 41,56 ----

  SELECT * FROM default_test;

+ -- Test stand-alone composite type
+
+ CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
+
+ CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
+   SELECT * FROM default_test;
+ ' LANGUAGE SQL;
+
+ SELECT * FROM get_default_test();
+
+ DROP TYPE default_test_row CASCADE;
+
  DROP TABLE default_test;

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: Polygon contrib
Next
From: Tom Lane
Date:
Subject: Re: stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types)