Thread: stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types)

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;

Joe Conway <mail@joeconway.com> writes:
> Items 1 and 2 from the proposal above are implemented in the attached
> patch.

Good first cut, but...

I don't like the way you did the dependencies.  In a normal relation,
the type row is internally dependent on the class row.  This causes the
type to automatically go away if the relation is dropped, and it also
prevents an attempt to manually drop the type directly (without dropping
the relation).  For a composite type, of course we want exactly the
opposite behavior: the class row should internally depend on the type,
not vice versa.

If you did it that way then you'd not need that ugly kluge in
RemoveType.  What you'd need instead is some smarts (a kluge!?) in
setting up the dependency.  Currently that dependency is made in
TypeCreate which doesn't know what sort of relation it's creating
a type for.  Probably the best answer is to pull that particular
dependency out of TypeCreate, and make it (in the proper direction)
in AddNewRelationType.

Also, I'm not following the point of the separation between
DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
for a CommandCounterIncrement call in there.

You have missed a number of places where this new relkind ought to
be special-cased the same way RELKIND_VIEW is --- for example
CheckAttributeNames and AddNewAttributeTuples, since a composite type
presumably shouldn't have system columns associated.  I'd counsel
looking at all references to RELKIND_VIEW to see which places also need
to check for RELKIND_COMPOSITE_TYPE.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> If you did it that way then you'd not need that ugly kluge in
> RemoveType.  What you'd need instead is some smarts (a kluge!?) in
> setting up the dependency.  Currently that dependency is made in
> TypeCreate which doesn't know what sort of relation it's creating
> a type for.  Probably the best answer is to pull that particular
> dependency out of TypeCreate, and make it (in the proper direction)
> in AddNewRelationType.

OK -- I'll take a look.

> Also, I'm not following the point of the separation between
> DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
> for a CommandCounterIncrement call in there.

Well the next thing I was going to work on after this was an implicitly
created composite type when creating a function. I thought maybe the
CommandCounterIncrement would be needed so that the type could be
created and then immediately used by the function. In any case, I'll
combine the two functions.


> You have missed a number of places where this new relkind ought to
> be special-cased the same way RELKIND_VIEW is --- for example
> CheckAttributeNames and AddNewAttributeTuples, since a composite type
> presumably shouldn't have system columns associated.  I'd counsel
> looking at all references to RELKIND_VIEW to see which places also need
> to check for RELKIND_COMPOSITE_TYPE.

Yeah, after I fired off the post it occurred to me that I had neglected
to do that. I was just going through that exercise now.

Thanks for the (quick!) review. Round two will be probably sometime
tomorrow.

Joe



Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
>> Also, I'm not following the point of the separation between
>> DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
>> for a CommandCounterIncrement call in there.

> Well the next thing I was going to work on after this was an implicitly
> created composite type when creating a function. I thought maybe the
> CommandCounterIncrement would be needed so that the type could be
> created and then immediately used by the function.

Hm.  Maybe --- it would depend on whether the function-creating code
actually tried to look at the type definition, as opposed to just using
its OID.  (You'll probably want DefineCompositeType to return the type
OID, btw.)  In any case, I'd be inclined to put the CCI call in the
caller not the callee, so it's only done when actually needed.  It's
surely not needed for a standalone CREATE TYPE command.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> You have missed a number of places where this new relkind ought to
> be special-cased the same way RELKIND_VIEW is --- for example
> CheckAttributeNames and AddNewAttributeTuples, since a composite type
> presumably shouldn't have system columns associated.  I'd counsel
> looking at all references to RELKIND_VIEW to see which places also need
> to check for RELKIND_COMPOSITE_TYPE.

One of the places I missed was pg_dump.c. In working on pg_dump support,
I ran across a problem:

test=# CREATE TYPE "MyInt42" (internallength = 4,input = int4in,output =
int4out,alignment = int4,default = 42,passedbyvalue);
CREATE TYPE
test=# CREATE TYPE "compfoo" AS (f1 "MyInt42", f2 integer);
CREATE TYPE
test=# drop type compfoo;
DROP TYPE
test=# CREATE TYPE "compfoo" AS (f1 "MyInt42", f2 "integer");
ERROR:  Type "integer" does not exist
test=# create table tbl_0 (f1 "integer");
ERROR:  Type "integer" does not exist
test=# create table tbl_0 (f1 "MyInt42");
CREATE TABLE
test=# drop table tbl_0 ;
DROP TABLE
test=# create table tbl_0 (f1 integer);
CREATE TABLE

Shouldn't "integer" be recognized as a valid type?

Thanks,

Joe


Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> If you did it that way then you'd not need that ugly kluge in
> RemoveType.  What you'd need instead is some smarts (a kluge!?) in
> setting up the dependency.  Currently that dependency is made in
> TypeCreate which doesn't know what sort of relation it's creating
> a type for.  Probably the best answer is to pull that particular
> dependency out of TypeCreate, and make it (in the proper direction)
> in AddNewRelationType.

Fixed.

> Also, I'm not following the point of the separation between
> DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
> for a CommandCounterIncrement call in there.

Fixed.


> You have missed a number of places where this new relkind ought to
> be special-cased the same way RELKIND_VIEW is --- for example
> CheckAttributeNames and AddNewAttributeTuples, since a composite type
> presumably shouldn't have system columns associated.  I'd counsel
> looking at all references to RELKIND_VIEW to see which places also need
> to check for RELKIND_COMPOSITE_TYPE.

Yup, I had missed lots of things, not the least of which was pg_dump.
New patch attached includes pg_dump, psql (\dT), docs, and regression
support.

There is also a small adjustment to the expected output file for
select-having. I was getting a regression failure based on ordering of
the results, so I added ORDER BY clauses.

Passes all regression tests. If no more 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 14:49:57 -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 14:49:57 -0000
***************
*** 358,364 ****
       *
       * Skip this for a view, since it doesn't have system attributes.
       */
!     if (relkind != RELKIND_VIEW)
      {
          for (i = 0; i < natts; i++)
          {
--- 358,364 ----
       *
       * Skip this for a view, since it doesn't have system attributes.
       */
!     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
      {
          for (i = 0; i < natts; i++)
          {
***************
*** 475,481 ****
       * Skip all for a view.  We don't bother with making datatype
       * dependencies here, since presumably all these types are pinned.
       */
!     if (relkind != RELKIND_VIEW)
      {
          dpp = SysAtt;
          for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
--- 475,481 ----
       * Skip all for a view.  We don't bother with making datatype
       * dependencies here, since presumably all these types are pinned.
       */
!     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
      {
          dpp = SysAtt;
          for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
***************
*** 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/catalog/namespace.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/namespace.c,v
retrieving revision 1.29
diff -c -r1.29 namespace.c
*** src/backend/catalog/namespace.c    8 Aug 2002 01:44:30 -0000    1.29
--- src/backend/catalog/namespace.c    8 Aug 2002 14:49:57 -0000
***************
*** 1578,1583 ****
--- 1578,1584 ----
              case RELKIND_RELATION:
              case RELKIND_SEQUENCE:
              case RELKIND_VIEW:
+             case RELKIND_COMPOSITE_TYPE:
                  AssertTupleDescHasOid(pgclass->rd_att);
                  object.classId = RelOid_pg_class;
                  object.objectId = HeapTupleGetOid(tuple);
Index: src/backend/catalog/pg_type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_type.c,v
retrieving revision 1.77
diff -c -r1.77 pg_type.c
*** src/backend/catalog/pg_type.c    5 Aug 2002 03:29:16 -0000    1.77
--- src/backend/catalog/pg_type.c    8 Aug 2002 17:30:27 -0000
***************
*** 311,325 ****

          /*
           * If the type is a rowtype for a relation, mark it as internally
!          * dependent on the relation.  This allows it to be auto-dropped
!          * when the relation is, and not otherwise.
           */
          if (OidIsValid(relationOid))
          {
              referenced.classId = RelOid_pg_class;
              referenced.objectId = relationOid;
              referenced.objectSubId = 0;
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
          }

          /*
--- 311,338 ----

          /*
           * If the type is a rowtype for a relation, mark it as internally
!          * dependent on the relation, *unless* it is a stand-alone composite
!          * type relation. For the latter case, we have to reverse the
!          * dependency.
!          *
!          * In the former case, this allows the type to be auto-dropped
!          * when the relation is, and not otherwise. And in the latter,
!          * of course we get the opposite effect.
           */
          if (OidIsValid(relationOid))
          {
+             Relation    rel = relation_open(relationOid, AccessShareLock);
+             char        relkind = rel->rd_rel->relkind;
+             relation_close(rel, AccessShareLock);
+
              referenced.classId = RelOid_pg_class;
              referenced.objectId = relationOid;
              referenced.objectSubId = 0;
!
!             if (relkind != RELKIND_COMPOSITE_TYPE)
!                 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
!             else
!                 recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
          }

          /*
Index: src/backend/commands/copy.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/copy.c,v
retrieving revision 1.162
diff -c -r1.162 copy.c
*** src/backend/commands/copy.c    2 Aug 2002 18:15:06 -0000    1.162
--- src/backend/commands/copy.c    8 Aug 2002 14:49:57 -0000
***************
*** 398,403 ****
--- 398,406 ----
              if (rel->rd_rel->relkind == RELKIND_VIEW)
                  elog(ERROR, "You cannot copy view %s",
                       RelationGetRelationName(rel));
+             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+                 elog(ERROR, "You cannot copy type relation %s",
+                      RelationGetRelationName(rel));
              else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
                  elog(ERROR, "You cannot change sequence relation %s",
                       RelationGetRelationName(rel));
***************
*** 442,447 ****
--- 445,453 ----
          {
              if (rel->rd_rel->relkind == RELKIND_VIEW)
                  elog(ERROR, "You cannot copy view %s",
+                      RelationGetRelationName(rel));
+             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+                 elog(ERROR, "You cannot copy type relation %s",
                       RelationGetRelationName(rel));
              else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
                  elog(ERROR, "You cannot copy sequence %s",
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/tablecmds.c,v
retrieving revision 1.28
diff -c -r1.28 tablecmds.c
*** src/backend/commands/tablecmds.c    7 Aug 2002 21:45:01 -0000    1.28
--- src/backend/commands/tablecmds.c    8 Aug 2002 18:04:58 -0000
***************
*** 345,350 ****
--- 345,354 ----
          elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
               RelationGetRelationName(rel));

+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "TRUNCATE cannot be used on type relations. '%s' is a type",
+              RelationGetRelationName(rel));
+
      if (!allowSystemTableMods && IsSystemRelation(rel))
          elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
               RelationGetRelationName(rel));
***************
*** 3210,3221 ****
          case RELKIND_RELATION:
          case RELKIND_INDEX:
          case RELKIND_VIEW:
          case RELKIND_SEQUENCE:
          case RELKIND_TOASTVALUE:
              /* ok to change owner */
              break;
          default:
!             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
                   NameStr(tuple_class->relname));
      }
  }
--- 3214,3226 ----
          case RELKIND_RELATION:
          case RELKIND_INDEX:
          case RELKIND_VIEW:
+         case RELKIND_COMPOSITE_TYPE:
          case RELKIND_SEQUENCE:
          case RELKIND_TOASTVALUE:
              /* ok to change owner */
              break;
          default:
!             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, type, or sequence",
                   NameStr(tuple_class->relname));
      }
  }
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 17:33:43 -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"
***************
*** 50,56 ****

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

-
  /*
   * DefineType
   *        Registers a new type.
--- 51,56 ----
***************
*** 665,668 ****
--- 665,707 ----
      }

      return procOid;
+ }
+
+ /*-------------------------------------------------------------------
+  * DefineCompositeType
+  *
+  * 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...
+  *
+  * DefineCompositeType returns relid for use when creating
+  * an implicit composite type during function creation
+  *-------------------------------------------------------------------
+  */
+ Oid
+ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
+ {
+     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...
+      */
+     return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
  }
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execMain.c,v
retrieving revision 1.173
diff -c -r1.173 execMain.c
*** src/backend/executor/execMain.c    7 Aug 2002 21:45:02 -0000    1.173
--- src/backend/executor/execMain.c    8 Aug 2002 14:49:57 -0000
***************
*** 786,791 ****
--- 786,795 ----
              elog(ERROR, "You can't change view relation %s",
                   RelationGetRelationName(resultRelationDesc));
              break;
+         case RELKIND_COMPOSITE_TYPE:
+             elog(ERROR, "You can't change type relation %s",
+                  RelationGetRelationName(resultRelationDesc));
+             break;
      }

      MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
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 14:49:58 -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 14:49:58 -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 14:49:58 -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/storage/buffer/bufmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/buffer/bufmgr.c,v
retrieving revision 1.128
diff -c -r1.128 bufmgr.c
*** src/backend/storage/buffer/bufmgr.c    6 Aug 2002 02:36:34 -0000    1.128
--- src/backend/storage/buffer/bufmgr.c    8 Aug 2002 14:49:58 -0000
***************
*** 1056,1061 ****
--- 1056,1063 ----
       */
      if (relation->rd_rel->relkind == RELKIND_VIEW)
          relation->rd_nblocks = 0;
+     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         relation->rd_nblocks = 0;
      else if (!relation->rd_isnew && !relation->rd_istemp)
          relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
      return relation->rd_nblocks;
Index: src/backend/storage/smgr/smgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v
retrieving revision 1.58
diff -c -r1.58 smgr.c
*** src/backend/storage/smgr/smgr.c    6 Aug 2002 02:36:34 -0000    1.58
--- src/backend/storage/smgr/smgr.c    8 Aug 2002 14:49:59 -0000
***************
*** 263,268 ****
--- 263,270 ----

      if (reln->rd_rel->relkind == RELKIND_VIEW)
          return -1;
+     if (reln->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         return -1;
      if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0)
          if (!failOK)
              elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
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 14:49:59 -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 14:49:59 -0000
***************
*** 70,75 ****
--- 70,76 ----
      {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
      {RELKIND_VIEW, "a", "view", "VIEW"},
      {RELKIND_INDEX, "an", "index", "INDEX"},
+     {RELKIND_COMPOSITE_TYPE, "a", "type", "TYPE"},
      {'\0', "a", "???", "???"}
  };

***************
*** 570,575 ****
--- 571,589 ----
                          DefineAggregate(stmt->defnames, stmt->definition);
                          break;
                  }
+             }
+             break;
+
+         case T_CompositeTypeStmt:        /* CREATE TYPE (composite) */
+             {
+                 Oid    relid;
+                 CompositeTypeStmt   *stmt = (CompositeTypeStmt *) parsetree;
+
+                 /*
+                  * DefineCompositeType returns relid for use when creating
+                  * an implicit composite type during function creation
+                  */
+                 relid = DefineCompositeType(stmt->typevar, stmt->coldeflist);
              }
              break;

Index: src/backend/utils/adt/tid.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/tid.c,v
retrieving revision 1.32
diff -c -r1.32 tid.c
*** src/backend/utils/adt/tid.c    16 Jul 2002 17:55:25 -0000    1.32
--- src/backend/utils/adt/tid.c    8 Aug 2002 14:49:59 -0000
***************
*** 226,231 ****
--- 226,234 ----
      if (rel->rd_rel->relkind == RELKIND_VIEW)
          return currtid_for_view(rel, tid);

+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "currtid can't handle type relations");
+
      ItemPointerCopy(tid, result);
      heap_get_latest_tid(rel, SnapshotNow, result);

***************
*** 248,253 ****
--- 251,259 ----
      rel = heap_openrv(relrv, AccessShareLock);
      if (rel->rd_rel->relkind == RELKIND_VIEW)
          return currtid_for_view(rel, tid);
+
+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "currtid can't handle type relations");

      result = (ItemPointer) palloc(sizeof(ItemPointerData));
      ItemPointerCopy(tid, result);
Index: src/bin/pg_dump/common.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/common.c,v
retrieving revision 1.67
diff -c -r1.67 common.c
*** src/bin/pg_dump/common.c    30 Jul 2002 21:56:04 -0000    1.67
--- src/bin/pg_dump/common.c    8 Aug 2002 14:49:59 -0000
***************
*** 215,223 ****

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences and views never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW)
              continue;

          /* Don't bother computing anything for non-target tables, either */
--- 215,224 ----

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences, views, and types never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW ||
!             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              continue;

          /* Don't bother computing anything for non-target tables, either */
***************
*** 269,277 ****

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences and views never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW)
              continue;

          /* Don't bother computing anything for non-target tables, either */
--- 270,279 ----

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences, views, and types never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW ||
!             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              continue;

          /* Don't bother computing anything for non-target tables, either */
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.280
diff -c -r1.280 pg_dump.c
*** src/bin/pg_dump/pg_dump.c    4 Aug 2002 05:03:29 -0000    1.280
--- src/bin/pg_dump/pg_dump.c    8 Aug 2002 22:42:58 -0000
***************
*** 95,100 ****
--- 95,101 ----
                              FuncInfo *g_finfo, int numFuncs,
                              TypeInfo *g_tinfo, int numTypes);
  static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
+ static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
  static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
                           TableInfo *g_tblinfo);
  static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
***************
*** 1178,1183 ****
--- 1179,1188 ----
          if (tblinfo[i].relkind == RELKIND_VIEW)
              continue;

+         /* Skip TYPE relations */
+         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+             continue;
+
          if (tblinfo[i].relkind == RELKIND_SEQUENCE)        /* already dumped */
              continue;

***************
*** 1582,1587 ****
--- 1587,1593 ----
      int            i_usename;
      int            i_typelem;
      int            i_typrelid;
+     int            i_typrelkind;
      int            i_typtype;
      int            i_typisdefined;

***************
*** 1602,1608 ****
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, typtype, typisdefined "
                            "FROM pg_type");
      }
      else
--- 1608,1616 ----
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, "
!                           "(select relkind from pg_class where oid = typrelid) as typrelkind, "
!                           "typtype, typisdefined "
                            "FROM pg_type");
      }
      else
***************
*** 1610,1616 ****
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "0::oid as typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, typtype, typisdefined "
                            "FROM pg_type");
      }

--- 1618,1626 ----
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "0::oid as typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, "
!                           "''::char as typrelkind, "
!                           "typtype, typisdefined "
                            "FROM pg_type");
      }

***************
*** 1632,1637 ****
--- 1642,1648 ----
      i_usename = PQfnumber(res, "usename");
      i_typelem = PQfnumber(res, "typelem");
      i_typrelid = PQfnumber(res, "typrelid");
+     i_typrelkind = PQfnumber(res, "typrelkind");
      i_typtype = PQfnumber(res, "typtype");
      i_typisdefined = PQfnumber(res, "typisdefined");

***************
*** 1644,1649 ****
--- 1655,1661 ----
          tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
          tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
          tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
+         tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
          tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);

          /*
***************
*** 2109,2115 ****
          appendPQExpBuffer(query,
                            "SELECT pg_class.oid, relname, relacl, relkind, "
                            "relnamespace, "
-
                            "(select usename from pg_user where relowner = usesysid) as usename, "
                            "relchecks, reltriggers, "
                            "relhasindex, relhasrules, relhasoids "
--- 2121,2126 ----
***************
*** 2120,2125 ****
--- 2131,2137 ----
      }
      else if (g_fout->remoteVersion >= 70200)
      {
+         /* before 7.3 there were no type relations with relkind 'c' */
          appendPQExpBuffer(query,
                            "SELECT pg_class.oid, relname, relacl, relkind, "
                            "0::oid as relnamespace, "
***************
*** 2363,2368 ****
--- 2375,2384 ----
          if (tblinfo[i].relkind == RELKIND_SEQUENCE)
              continue;

+         /* Don't bother to collect info for type relations */
+         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+             continue;
+
          /* Don't bother with uninteresting tables, either */
          if (!tblinfo[i].interesting)
              continue;
***************
*** 3180,3185 ****
--- 3196,3300 ----
  }

  /*
+  * dumpOneCompositeType
+  *    writes out to fout the queries to recreate a user-defined stand-alone
+  *    composite type as requested by dumpTypes
+  */
+ static void
+ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
+ {
+     PQExpBuffer q = createPQExpBuffer();
+     PQExpBuffer delq = createPQExpBuffer();
+     PQExpBuffer query = createPQExpBuffer();
+     PGresult   *res;
+     int            ntups;
+     char       *attname;
+     char       *atttypdefn;
+     char       *attbasetype;
+     const char *((*deps)[]);
+     int            depIdx = 0;
+     int            i;
+
+     deps = malloc(sizeof(char *) * 10);
+
+     /* Set proper schema search path so type references list correctly */
+     selectSourceSchema(tinfo->typnamespace->nspname);
+
+     /* Fetch type specific details */
+     /* We assume here that remoteVersion must be at least 70300 */
+
+     appendPQExpBuffer(query, "SELECT a.attname, "
+                       "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn, "
+                       "a.atttypid as attbasetype "
+                       "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
+                       "WHERE t.oid = '%s'::pg_catalog.oid "
+                       "AND a.attrelid = t.typrelid",
+                       tinfo->oid);
+
+     res = PQexec(g_conn, query->data);
+     if (!res ||
+         PQresultStatus(res) != PGRES_TUPLES_OK)
+     {
+         write_msg(NULL, "query to obtain type information failed: %s", PQerrorMessage(g_conn));
+         exit_nicely();
+     }
+
+     /* Expecting at least a single result */
+     ntups = PQntuples(res);
+     if (ntups < 1)
+     {
+         write_msg(NULL, "Got no rows from: %s", query->data);
+         exit_nicely();
+     }
+
+     /* DROP must be fully qualified in case same name appears in pg_catalog */
+     appendPQExpBuffer(delq, "DROP TYPE %s.",
+                       fmtId(tinfo->typnamespace->nspname, force_quotes));
+     appendPQExpBuffer(delq, "%s RESTRICT;\n",
+                       fmtId(tinfo->typname, force_quotes));
+
+     appendPQExpBuffer(q,
+                       "CREATE TYPE %s AS (",
+                       fmtId(tinfo->typname, force_quotes));
+
+     for (i = 0; i < ntups; i++)
+     {
+         attname = PQgetvalue(res, i, PQfnumber(res, "attname"));
+         atttypdefn = PQgetvalue(res, i, PQfnumber(res, "atttypdefn"));
+         attbasetype = PQgetvalue(res, i, PQfnumber(res, "attbasetype"));
+
+         if (i > 0)
+             appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
+         else
+             appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
+
+         /* Depends on the base type */
+         (*deps)[depIdx++] = strdup(attbasetype);
+     }
+     appendPQExpBuffer(q, ");\n");
+
+     (*deps)[depIdx++] = NULL;        /* End of List */
+
+     ArchiveEntry(fout, tinfo->oid, tinfo->typname,
+                  tinfo->typnamespace->nspname,
+                  tinfo->usename, "TYPE", deps,
+                  q->data, delq->data, NULL, NULL, NULL);
+
+     /*** Dump Type Comments ***/
+     resetPQExpBuffer(q);
+
+     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname, force_quotes));
+     dumpComment(fout, q->data,
+                 tinfo->typnamespace->nspname, tinfo->usename,
+                 tinfo->oid, "pg_type", 0, NULL);
+
+     PQclear(res);
+     destroyPQExpBuffer(q);
+     destroyPQExpBuffer(delq);
+     destroyPQExpBuffer(query);
+ }
+
+ /*
   * dumpTypes
   *      writes out to fout the queries to recreate all the user-defined types
   */
***************
*** 3195,3202 ****
          if (!tinfo[i].typnamespace->dump)
              continue;

!         /* skip relation types */
!         if (atooid(tinfo[i].typrelid) != 0)
              continue;

          /* skip undefined placeholder types */
--- 3310,3317 ----
          if (!tinfo[i].typnamespace->dump)
              continue;

!         /* skip relation types for non-stand-alone type relations*/
!         if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
              continue;

          /* skip undefined placeholder types */
***************
*** 3214,3219 ****
--- 3329,3336 ----
                              finfo, numFuncs, tinfo, numTypes);
          else if (tinfo[i].typtype == 'd')
              dumpOneDomain(fout, &tinfo[i]);
+         else if (tinfo[i].typtype == 'c')
+             dumpOneCompositeType(fout, &tinfo[i]);
      }
  }

***************
*** 4839,4844 ****
--- 4956,4962 ----

          if (tbinfo->relkind != RELKIND_SEQUENCE)
              continue;
+
          if (tbinfo->dump)
          {
              dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
***************
*** 4854,4859 ****
--- 4972,4979 ----
              TableInfo       *tbinfo = &tblinfo[i];

              if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
+                 continue;
+             if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
                  continue;

              if (tbinfo->dump)
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.94
diff -c -r1.94 pg_dump.h
*** src/bin/pg_dump/pg_dump.h    2 Aug 2002 18:15:08 -0000    1.94
--- src/bin/pg_dump/pg_dump.h    8 Aug 2002 18:26:11 -0000
***************
*** 47,52 ****
--- 47,53 ----
      char       *usename;        /* name of owner, or empty string */
      char       *typelem;        /* OID */
      char       *typrelid;        /* OID */
+     char        typrelkind;        /* 'r', 'v', 'c', etc */
      char        typtype;        /* 'b', 'c', etc */
      bool        isArray;        /* true if user-defined array type */
      bool        isDefined;        /* true if typisdefined */
Index: src/bin/psql/describe.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/psql/describe.c,v
retrieving revision 1.57
diff -c -r1.57 describe.c
*** src/bin/psql/describe.c    2 Aug 2002 18:15:08 -0000    1.57
--- src/bin/psql/describe.c    8 Aug 2002 22:26:38 -0000
***************
*** 168,176 ****

      /*
       * do not include array types (start with underscore), do not include
!      * user relations (typrelid!=0)
       */
!     appendPQExpBuffer(&buf, "FROM pg_type t\nWHERE t.typrelid = 0 AND t.typname !~ '^_.*'\n");

      if (name)
          /* accept either internal or external type name */
--- 168,179 ----

      /*
       * do not include array types (start with underscore), do not include
!      * user relations (typrelid!=0) unless they are type relations
       */
!     appendPQExpBuffer(&buf, "FROM pg_type t\nWHERE (t.typrelid = 0 ");
!     appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
!                               "where c.oid = t.typrelid)) ");
!     appendPQExpBuffer(&buf, "AND t.typname !~ '^_.*'\n");

      if (name)
          /* accept either internal or external type name */
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 14:49:59 -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 14:49:59 -0000
***************
*** 58,63 ****
--- 58,64 ----
  extern void RemoveTypeById(Oid typeOid);
  extern void DefineDomain(CreateDomainStmt *stmt);
  extern void RemoveDomain(List *names, DropBehavior behavior);
+ extern Oid 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 14:49:59 -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 14:49:59 -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/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.44
diff -c -r1.44 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c    8 Aug 2002 01:36:04 -0000    1.44
--- src/pl/plpgsql/src/pl_comp.c    8 Aug 2002 14:50:00 -0000
***************
*** 1030,1041 ****
      }

      /*
!      * It must be a relation, sequence or view
       */
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW)
      {
          ReleaseSysCache(classtup);
          pfree(cp[0]);
--- 1030,1042 ----
      }

      /*
!      * It must be a relation, sequence, view, or type
       */
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW &&
!         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
      {
          ReleaseSysCache(classtup);
          pfree(cp[0]);
***************
*** 1130,1139 ****
      if (!HeapTupleIsValid(classtup))
          elog(ERROR, "%s: no such class", cp[0]);
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
!     /* accept relation, sequence, or view pg_class entries */
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW)
          elog(ERROR, "%s isn't a table", cp[0]);

      /*
--- 1131,1141 ----
      if (!HeapTupleIsValid(classtup))
          elog(ERROR, "%s: no such class", cp[0]);
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
!     /* accept relation, sequence, view, or type pg_class entries */
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW &&
!         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
          elog(ERROR, "%s isn't a table", cp[0]);

      /*
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 14:50:00 -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/expected/select_having.out
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/select_having.out,v
retrieving revision 1.7
diff -c -r1.7 select_having.out
*** src/test/regress/expected/select_having.out    26 Jun 2002 21:58:56 -0000    1.7
--- src/test/regress/expected/select_having.out    8 Aug 2002 17:48:10 -0000
***************
*** 14,20 ****
  INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I');
  INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING count(*) = 1;
   b |    c
  ---+----------
   1 | XXXX
--- 14,20 ----
  INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I');
  INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c;
   b |    c
  ---+----------
   1 | XXXX
***************
*** 23,37 ****

  -- HAVING is equivalent to WHERE in this case
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING b = 3;
   b |    c
  ---+----------
-  3 | BBBB
   3 | bbbb
  (2 rows)

  SELECT lower(c), count(c) FROM test_having
!     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a);
    lower   | count
  ----------+-------
   bbbb     |     3
--- 23,37 ----

  -- HAVING is equivalent to WHERE in this case
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING b = 3 ORDER BY b, c;
   b |    c
  ---+----------
   3 | bbbb
+  3 | BBBB
  (2 rows)

  SELECT lower(c), count(c) FROM test_having
!     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c);
    lower   | count
  ----------+-------
   bbbb     |     3
***************
*** 40,50 ****
  (3 rows)

  SELECT c, max(a) FROM test_having
!     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a);
      c     | max
  ----------+-----
-  XXXX     |   0
   bbbb     |   5
  (2 rows)

  DROP TABLE test_having;
--- 40,50 ----
  (3 rows)

  SELECT c, max(a) FROM test_having
!     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c;
      c     | max
  ----------+-----
   bbbb     |   5
+  XXXX     |   0
  (2 rows)

  DROP TABLE test_having;
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 14:50:00 -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;
Index: src/test/regress/sql/select_having.sql
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/select_having.sql,v
retrieving revision 1.7
diff -c -r1.7 select_having.sql
*** src/test/regress/sql/select_having.sql    26 Jun 2002 21:58:56 -0000    1.7
--- src/test/regress/sql/select_having.sql    8 Aug 2002 17:43:02 -0000
***************
*** 16,32 ****
  INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');

  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING count(*) = 1;

  -- HAVING is equivalent to WHERE in this case
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING b = 3;

  SELECT lower(c), count(c) FROM test_having
!     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a);

  SELECT c, max(a) FROM test_having
!     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a);

  DROP TABLE test_having;

--- 16,32 ----
  INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');

  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c;

  -- HAVING is equivalent to WHERE in this case
  SELECT b, c FROM test_having
!     GROUP BY b, c HAVING b = 3 ORDER BY b, c;

  SELECT lower(c), count(c) FROM test_having
!     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c);

  SELECT c, max(a) FROM test_having
!     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c;

  DROP TABLE test_having;


Joe Conway <mail@joeconway.com> writes:
> Shouldn't "integer" be recognized as a valid type?

Not when double-quoted; you are being overzealous about quoting.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>>Shouldn't "integer" be recognized as a valid type?
> Not when double-quoted; you are being overzealous about quoting.

I figured that out (new patch works "correctly"), but it does seem
inconsistent:

test=# create table tbl_0 (f1 "integer");
ERROR:  Type "integer" does not exist
test=# create table "tbl_0" ("f1" integer);
CREATE TABLE
test=# select * from tbl_0 ;
  f1
----
(0 rows)

test=# select f1 from tbl_0 ;
  f1
----
(0 rows)

test=# select "f1" from tbl_0 ;
  f1
----
(0 rows)


For table and column identifiers, if they were defined in all lowercase
I can either quote them, or not -- it works either way. Same for *user*
defined data types:


test=# CREATE TYPE myint42 ( internallength = 4, input = int4in, output
= int4out, default = '42', alignment = int4, storage = plain,
passedbyvalue);
CREATE TYPE
test=# create table "tbl_1" ("f1" myint42);
CREATE TABLE
test=# create table "tbl_2" ("f1" "myint42");
CREATE TABLE
test=# \d tbl_2
         Table "tbl_2"
  Column |  Type   | Modifiers
--------+---------+-----------
  f1     | myint42 |


But *internal* data types only work unquoted.

Joe


Joe Conway <mail@joeconway.com> writes:
> Tom Lane wrote:
> Shouldn't "integer" be recognized as a valid type?
>> Not when double-quoted; you are being overzealous about quoting.

> I figured that out (new patch works "correctly"), but it does seem
> inconsistent:

Would you expect "point[]" and point[] to be the same?

The actual type name is int4.  int4 and "int4" act the same.  Aliases
like int and integer are special keywords and so are not recognized
when quoted.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Joe Conway wrote:
> There is also a small adjustment to the expected output file for
> select-having. I was getting a regression failure based on ordering of
> the results, so I added ORDER BY clauses.

I now see that the select-having fails differently on my PC at home from
the one at work. At home I see in postgresql.conf:

LC_MESSAGES = 'C'
LC_MONETARY = 'C'
LC_NUMERIC = 'C'
LC_TIME = 'C'

and at work:

LC_MESSAGES = 'en_US'
LC_MONETARY = 'en_US'
LC_NUMERIC = 'en_US'
LC_TIME = 'en_US'

I have been running `make installcheck` instead of `make check`. I
gather that `make installcheck` does not set LOCALE to 'C' (as make
check does -- I think). Should it?

Please rip the select-having pieces out of this patch when it is
applied, or let me know and I'll submit another patch.

Thanks,

Joe


Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Tom Lane
Date:
Joe Conway <mail@joeconway.com> writes:
> Joe Conway wrote:
>> There is also a small adjustment to the expected output file for
>> select-having. I was getting a regression failure based on ordering of
>> the results, so I added ORDER BY clauses.

> I now see that the select-having fails differently on my PC at home from
> the one at work. At home I see in postgresql.conf:

> LC_MESSAGES = 'C'
> LC_MONETARY = 'C'
> LC_NUMERIC = 'C'
> LC_TIME = 'C'

> and at work:

> LC_MESSAGES = 'en_US'
> LC_MONETARY = 'en_US'
> LC_NUMERIC = 'en_US'
> LC_TIME = 'en_US'

> I have been running `make installcheck` instead of `make check`. I
> gather that `make installcheck` does not set LOCALE to 'C' (as make
> check does -- I think). Should it?

The problem is that LC_COLLATE is (presumably) also en_US at work;
"make installcheck" hasn't got any way of changing the collation of
the preinstalled server, because that was frozen at initdb.  See
the discussion in the regression-test documentation.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Tom Lane wrote:
> > If you did it that way then you'd not need that ugly kluge in
> > RemoveType.  What you'd need instead is some smarts (a kluge!?) in
> > setting up the dependency.  Currently that dependency is made in
> > TypeCreate which doesn't know what sort of relation it's creating
> > a type for.  Probably the best answer is to pull that particular
> > dependency out of TypeCreate, and make it (in the proper direction)
> > in AddNewRelationType.
>
> Fixed.
>
> > Also, I'm not following the point of the separation between
> > DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
> > for a CommandCounterIncrement call in there.
>
> Fixed.
>
>
> > You have missed a number of places where this new relkind ought to
> > be special-cased the same way RELKIND_VIEW is --- for example
> > CheckAttributeNames and AddNewAttributeTuples, since a composite type
> > presumably shouldn't have system columns associated.  I'd counsel
> > looking at all references to RELKIND_VIEW to see which places also need
> > to check for RELKIND_COMPOSITE_TYPE.
>
> Yup, I had missed lots of things, not the least of which was pg_dump.
> New patch attached includes pg_dump, psql (\dT), docs, and regression
> support.
>
> There is also a small adjustment to the expected output file for
> select-having. I was getting a regression failure based on ordering of
> the results, so I added ORDER BY clauses.
>
> Passes all regression tests. If no more 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 14:49:57 -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 14:49:57 -0000
> ***************
> *** 358,364 ****
>        *
>        * Skip this for a view, since it doesn't have system attributes.
>        */
> !     if (relkind != RELKIND_VIEW)
>       {
>           for (i = 0; i < natts; i++)
>           {
> --- 358,364 ----
>        *
>        * Skip this for a view, since it doesn't have system attributes.
>        */
> !     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           for (i = 0; i < natts; i++)
>           {
> ***************
> *** 475,481 ****
>        * Skip all for a view.  We don't bother with making datatype
>        * dependencies here, since presumably all these types are pinned.
>        */
> !     if (relkind != RELKIND_VIEW)
>       {
>           dpp = SysAtt;
>           for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
> --- 475,481 ----
>        * Skip all for a view.  We don't bother with making datatype
>        * dependencies here, since presumably all these types are pinned.
>        */
> !     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           dpp = SysAtt;
>           for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
> ***************
> *** 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/catalog/namespace.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/namespace.c,v
> retrieving revision 1.29
> diff -c -r1.29 namespace.c
> *** src/backend/catalog/namespace.c    8 Aug 2002 01:44:30 -0000    1.29
> --- src/backend/catalog/namespace.c    8 Aug 2002 14:49:57 -0000
> ***************
> *** 1578,1583 ****
> --- 1578,1584 ----
>               case RELKIND_RELATION:
>               case RELKIND_SEQUENCE:
>               case RELKIND_VIEW:
> +             case RELKIND_COMPOSITE_TYPE:
>                   AssertTupleDescHasOid(pgclass->rd_att);
>                   object.classId = RelOid_pg_class;
>                   object.objectId = HeapTupleGetOid(tuple);
> Index: src/backend/catalog/pg_type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_type.c,v
> retrieving revision 1.77
> diff -c -r1.77 pg_type.c
> *** src/backend/catalog/pg_type.c    5 Aug 2002 03:29:16 -0000    1.77
> --- src/backend/catalog/pg_type.c    8 Aug 2002 17:30:27 -0000
> ***************
> *** 311,325 ****
>
>           /*
>            * If the type is a rowtype for a relation, mark it as internally
> !          * dependent on the relation.  This allows it to be auto-dropped
> !          * when the relation is, and not otherwise.
>            */
>           if (OidIsValid(relationOid))
>           {
>               referenced.classId = RelOid_pg_class;
>               referenced.objectId = relationOid;
>               referenced.objectSubId = 0;
> !             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
>           }
>
>           /*
> --- 311,338 ----
>
>           /*
>            * If the type is a rowtype for a relation, mark it as internally
> !          * dependent on the relation, *unless* it is a stand-alone composite
> !          * type relation. For the latter case, we have to reverse the
> !          * dependency.
> !          *
> !          * In the former case, this allows the type to be auto-dropped
> !          * when the relation is, and not otherwise. And in the latter,
> !          * of course we get the opposite effect.
>            */
>           if (OidIsValid(relationOid))
>           {
> +             Relation    rel = relation_open(relationOid, AccessShareLock);
> +             char        relkind = rel->rd_rel->relkind;
> +             relation_close(rel, AccessShareLock);
> +
>               referenced.classId = RelOid_pg_class;
>               referenced.objectId = relationOid;
>               referenced.objectSubId = 0;
> !
> !             if (relkind != RELKIND_COMPOSITE_TYPE)
> !                 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
> !             else
> !                 recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
>           }
>
>           /*
> Index: src/backend/commands/copy.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/copy.c,v
> retrieving revision 1.162
> diff -c -r1.162 copy.c
> *** src/backend/commands/copy.c    2 Aug 2002 18:15:06 -0000    1.162
> --- src/backend/commands/copy.c    8 Aug 2002 14:49:57 -0000
> ***************
> *** 398,403 ****
> --- 398,406 ----
>               if (rel->rd_rel->relkind == RELKIND_VIEW)
>                   elog(ERROR, "You cannot copy view %s",
>                        RelationGetRelationName(rel));
> +             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +                 elog(ERROR, "You cannot copy type relation %s",
> +                      RelationGetRelationName(rel));
>               else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
>                   elog(ERROR, "You cannot change sequence relation %s",
>                        RelationGetRelationName(rel));
> ***************
> *** 442,447 ****
> --- 445,453 ----
>           {
>               if (rel->rd_rel->relkind == RELKIND_VIEW)
>                   elog(ERROR, "You cannot copy view %s",
> +                      RelationGetRelationName(rel));
> +             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +                 elog(ERROR, "You cannot copy type relation %s",
>                        RelationGetRelationName(rel));
>               else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
>                   elog(ERROR, "You cannot copy sequence %s",
> Index: src/backend/commands/tablecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/tablecmds.c,v
> retrieving revision 1.28
> diff -c -r1.28 tablecmds.c
> *** src/backend/commands/tablecmds.c    7 Aug 2002 21:45:01 -0000    1.28
> --- src/backend/commands/tablecmds.c    8 Aug 2002 18:04:58 -0000
> ***************
> *** 345,350 ****
> --- 345,354 ----
>           elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
>                RelationGetRelationName(rel));
>
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "TRUNCATE cannot be used on type relations. '%s' is a type",
> +              RelationGetRelationName(rel));
> +
>       if (!allowSystemTableMods && IsSystemRelation(rel))
>           elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
>                RelationGetRelationName(rel));
> ***************
> *** 3210,3221 ****
>           case RELKIND_RELATION:
>           case RELKIND_INDEX:
>           case RELKIND_VIEW:
>           case RELKIND_SEQUENCE:
>           case RELKIND_TOASTVALUE:
>               /* ok to change owner */
>               break;
>           default:
> !             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
>                    NameStr(tuple_class->relname));
>       }
>   }
> --- 3214,3226 ----
>           case RELKIND_RELATION:
>           case RELKIND_INDEX:
>           case RELKIND_VIEW:
> +         case RELKIND_COMPOSITE_TYPE:
>           case RELKIND_SEQUENCE:
>           case RELKIND_TOASTVALUE:
>               /* ok to change owner */
>               break;
>           default:
> !             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, type, or sequence",
>                    NameStr(tuple_class->relname));
>       }
>   }
> 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 17:33:43 -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"
> ***************
> *** 50,56 ****
>
>   static Oid findTypeIOFunction(List *procname, bool isOutput);
>
> -
>   /*
>    * DefineType
>    *        Registers a new type.
> --- 51,56 ----
> ***************
> *** 665,668 ****
> --- 665,707 ----
>       }
>
>       return procOid;
> + }
> +
> + /*-------------------------------------------------------------------
> +  * DefineCompositeType
> +  *
> +  * 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...
> +  *
> +  * DefineCompositeType returns relid for use when creating
> +  * an implicit composite type during function creation
> +  *-------------------------------------------------------------------
> +  */
> + Oid
> + DefineCompositeType(const RangeVar *typevar, List *coldeflist)
> + {
> +     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...
> +      */
> +     return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
>   }
> Index: src/backend/executor/execMain.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execMain.c,v
> retrieving revision 1.173
> diff -c -r1.173 execMain.c
> *** src/backend/executor/execMain.c    7 Aug 2002 21:45:02 -0000    1.173
> --- src/backend/executor/execMain.c    8 Aug 2002 14:49:57 -0000
> ***************
> *** 786,791 ****
> --- 786,795 ----
>               elog(ERROR, "You can't change view relation %s",
>                    RelationGetRelationName(resultRelationDesc));
>               break;
> +         case RELKIND_COMPOSITE_TYPE:
> +             elog(ERROR, "You can't change type relation %s",
> +                  RelationGetRelationName(resultRelationDesc));
> +             break;
>       }
>
>       MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
> 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 14:49:58 -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 14:49:58 -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 14:49:58 -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/storage/buffer/bufmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/buffer/bufmgr.c,v
> retrieving revision 1.128
> diff -c -r1.128 bufmgr.c
> *** src/backend/storage/buffer/bufmgr.c    6 Aug 2002 02:36:34 -0000    1.128
> --- src/backend/storage/buffer/bufmgr.c    8 Aug 2002 14:49:58 -0000
> ***************
> *** 1056,1061 ****
> --- 1056,1063 ----
>        */
>       if (relation->rd_rel->relkind == RELKIND_VIEW)
>           relation->rd_nblocks = 0;
> +     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         relation->rd_nblocks = 0;
>       else if (!relation->rd_isnew && !relation->rd_istemp)
>           relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
>       return relation->rd_nblocks;
> Index: src/backend/storage/smgr/smgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v
> retrieving revision 1.58
> diff -c -r1.58 smgr.c
> *** src/backend/storage/smgr/smgr.c    6 Aug 2002 02:36:34 -0000    1.58
> --- src/backend/storage/smgr/smgr.c    8 Aug 2002 14:49:59 -0000
> ***************
> *** 263,268 ****
> --- 263,270 ----
>
>       if (reln->rd_rel->relkind == RELKIND_VIEW)
>           return -1;
> +     if (reln->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         return -1;
>       if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0)
>           if (!failOK)
>               elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
> 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 14:49:59 -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 14:49:59 -0000
> ***************
> *** 70,75 ****
> --- 70,76 ----
>       {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
>       {RELKIND_VIEW, "a", "view", "VIEW"},
>       {RELKIND_INDEX, "an", "index", "INDEX"},
> +     {RELKIND_COMPOSITE_TYPE, "a", "type", "TYPE"},
>       {'\0', "a", "???", "???"}
>   };
>
> ***************
> *** 570,575 ****
> --- 571,589 ----
>                           DefineAggregate(stmt->defnames, stmt->definition);
>                           break;
>                   }
> +             }
> +             break;
> +
> +         case T_CompositeTypeStmt:        /* CREATE TYPE (composite) */
> +             {
> +                 Oid    relid;
> +                 CompositeTypeStmt   *stmt = (CompositeTypeStmt *) parsetree;
> +
> +                 /*
> +                  * DefineCompositeType returns relid for use when creating
> +                  * an implicit composite type during function creation
> +                  */
> +                 relid = DefineCompositeType(stmt->typevar, stmt->coldeflist);
>               }
>               break;
>
> Index: src/backend/utils/adt/tid.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/tid.c,v
> retrieving revision 1.32
> diff -c -r1.32 tid.c
> *** src/backend/utils/adt/tid.c    16 Jul 2002 17:55:25 -0000    1.32
> --- src/backend/utils/adt/tid.c    8 Aug 2002 14:49:59 -0000
> ***************
> *** 226,231 ****
> --- 226,234 ----
>       if (rel->rd_rel->relkind == RELKIND_VIEW)
>           return currtid_for_view(rel, tid);
>
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "currtid can't handle type relations");
> +
>       ItemPointerCopy(tid, result);
>       heap_get_latest_tid(rel, SnapshotNow, result);
>
> ***************
> *** 248,253 ****
> --- 251,259 ----
>       rel = heap_openrv(relrv, AccessShareLock);
>       if (rel->rd_rel->relkind == RELKIND_VIEW)
>           return currtid_for_view(rel, tid);
> +
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "currtid can't handle type relations");
>
>       result = (ItemPointer) palloc(sizeof(ItemPointerData));
>       ItemPointerCopy(tid, result);
> Index: src/bin/pg_dump/common.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/common.c,v
> retrieving revision 1.67
> diff -c -r1.67 common.c
> *** src/bin/pg_dump/common.c    30 Jul 2002 21:56:04 -0000    1.67
> --- src/bin/pg_dump/common.c    8 Aug 2002 14:49:59 -0000
> ***************
> *** 215,223 ****
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences and views never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> --- 215,224 ----
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences, views, and types never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW ||
> !             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> ***************
> *** 269,277 ****
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences and views never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> --- 270,279 ----
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences, views, and types never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW ||
> !             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> Index: src/bin/pg_dump/pg_dump.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.c,v
> retrieving revision 1.280
> diff -c -r1.280 pg_dump.c
> *** src/bin/pg_dump/pg_dump.c    4 Aug 2002 05:03:29 -0000    1.280
> --- src/bin/pg_dump/pg_dump.c    8 Aug 2002 22:42:58 -0000
> ***************
> *** 95,100 ****
> --- 95,101 ----
>                               FuncInfo *g_finfo, int numFuncs,
>                               TypeInfo *g_tinfo, int numTypes);
>   static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
> + static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
>   static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
>                            TableInfo *g_tblinfo);
>   static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
> ***************
> *** 1178,1183 ****
> --- 1179,1188 ----
>           if (tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
> +         /* Skip TYPE relations */
> +         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
> +             continue;
> +
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE)        /* already dumped */
>               continue;
>
> ***************
> *** 1582,1587 ****
> --- 1587,1593 ----
>       int            i_usename;
>       int            i_typelem;
>       int            i_typrelid;
> +     int            i_typrelkind;
>       int            i_typtype;
>       int            i_typisdefined;
>
> ***************
> *** 1602,1608 ****
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, typtype, typisdefined "
>                             "FROM pg_type");
>       }
>       else
> --- 1608,1616 ----
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, "
> !                           "(select relkind from pg_class where oid = typrelid) as typrelkind, "
> !                           "typtype, typisdefined "
>                             "FROM pg_type");
>       }
>       else
> ***************
> *** 1610,1616 ****
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "0::oid as typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, typtype, typisdefined "
>                             "FROM pg_type");
>       }
>
> --- 1618,1626 ----
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "0::oid as typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, "
> !                           "''::char as typrelkind, "
> !                           "typtype, typisdefined "
>                             "FROM pg_type");
>       }
>
> ***************
> *** 1632,1637 ****
> --- 1642,1648 ----
>       i_usename = PQfnumber(res, "usename");
>       i_typelem = PQfnumber(res, "typelem");
>       i_typrelid = PQfnumber(res, "typrelid");
> +     i_typrelkind = PQfnumber(res, "typrelkind");
>       i_typtype = PQfnumber(res, "typtype");
>       i_typisdefined = PQfnumber(res, "typisdefined");
>
> ***************
> *** 1644,1649 ****
> --- 1655,1661 ----
>           tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
>           tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
>           tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
> +         tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
>           tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
>
>           /*
> ***************
> *** 2109,2115 ****
>           appendPQExpBuffer(query,
>                             "SELECT pg_class.oid, relname, relacl, relkind, "
>                             "relnamespace, "
> -
>                             "(select usename from pg_user where relowner = usesysid) as usename, "
>                             "relchecks, reltriggers, "
>                             "relhasindex, relhasrules, relhasoids "
> --- 2121,2126 ----
> ***************
> *** 2120,2125 ****
> --- 2131,2137 ----
>       }
>       else if (g_fout->remoteVersion >= 70200)
>       {
> +         /* before 7.3 there were no type relations with relkind 'c' */
>           appendPQExpBuffer(query,
>                             "SELECT pg_class.oid, relname, relacl, relkind, "
>                             "0::oid as relnamespace, "
> ***************
> *** 2363,2368 ****
> --- 2375,2384 ----
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE)
>               continue;
>
> +         /* Don't bother to collect info for type relations */
> +         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
> +             continue;
> +
>           /* Don't bother with uninteresting tables, either */
>           if (!tblinfo[i].interesting)
>               continue;
> ***************
> *** 3180,3185 ****
> --- 3196,3300 ----
>   }
>
>   /*
> +  * dumpOneCompositeType
> +  *    writes out to fout the queries to recreate a user-defined stand-alone
> +  *    composite type as requested by dumpTypes
> +  */
> + static void
> + dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
> + {
> +     PQExpBuffer q = createPQExpBuffer();
> +     PQExpBuffer delq = createPQExpBuffer();
> +     PQExpBuffer query = createPQExpBuffer();
> +     PGresult   *res;
> +     int            ntups;
> +     char       *attname;
> +     char       *atttypdefn;
> +     char       *attbasetype;
> +     const char *((*deps)[]);
> +     int            depIdx = 0;
> +     int            i;
> +
> +     deps = malloc(sizeof(char *) * 10);
> +
> +     /* Set proper schema search path so type references list correctly */
> +     selectSourceSchema(tinfo->typnamespace->nspname);
> +
> +     /* Fetch type specific details */
> +     /* We assume here that remoteVersion must be at least 70300 */
> +
> +     appendPQExpBuffer(query, "SELECT a.attname, "
> +                       "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn, "
> +                       "a.atttypid as attbasetype "
> +                       "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
> +                       "WHERE t.oid = '%s'::pg_catalog.oid "
> +                       "AND a.attrelid = t.typrelid",
> +                       tinfo->oid);
> +
> +     res = PQexec(g_conn, query->data);
> +     if (!res ||
> +         PQresultStatus(res) != PGRES_TUPLES_OK)
> +     {
> +         write_msg(NULL, "query to obtain type information failed: %s", PQerrorMessage(g_conn));
> +         exit_nicely();
> +     }
> +
> +     /* Expecting at least a single result */
> +     ntups = PQntuples(res);
> +     if (ntups < 1)
> +     {
> +         write_msg(NULL, "Got no rows from: %s", query->data);
> +         exit_nicely();
> +     }
> +
> +     /* DROP must be fully qualified in case same name appears in pg_catalog */
> +     appendPQExpBuffer(delq, "DROP TYPE %s.",
> +                       fmtId(tinfo->typnamespace->nspname, force_quotes));
> +     appendPQExpBuffer(delq, "%s RESTRICT;\n",
> +                       fmtId(tinfo->typname, force_quotes));
> +
> +     appendPQExpBuffer(q,
> +                       "CREATE TYPE %s AS (",
> +                       fmtId(tinfo->typname, force_quotes));
> +
> +     for (i = 0; i < ntups; i++)
> +     {
> +         attname = PQgetvalue(res, i, PQfnumber(res, "attname"));
> +         atttypdefn = PQgetvalue(res, i, PQfnumber(res, "atttypdefn"));
> +         attbasetype = PQgetvalue(res, i, PQfnumber(res, "attbasetype"));
> +
> +         if (i > 0)
> +             appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
> +         else
> +             appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
> +
> +         /* Depends on the base type */
> +         (*deps)[depIdx++] = strdup(attbasetype);
> +     }
> +     appendPQExpBuffer(q, ");\n");
> +
> +     (*deps)[depIdx++] = NULL;        /* End of List */
> +
> +     ArchiveEntry(fout, tinfo->oid, tinfo->typname,
> +                  tinfo->typnamespace->nspname,
> +                  tinfo->usename, "TYPE", deps,
> +                  q->data, delq->data, NULL, NULL, NULL);
> +
> +     /*** Dump Type Comments ***/
> +     resetPQExpBuffer(q);
> +
> +     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname, force_quotes));
> +     dumpComment(fout, q->data,
> +                 tinfo->typnamespace->nspname, tinfo->usename,
> +                 tinfo->oid, "pg_type", 0, NULL);
> +
> +     PQclear(res);
> +     destroyPQExpBuffer(q);
> +     destroyPQExpBuffer(delq);
> +     destroyPQExpBuffer(query);
> + }
> +
> + /*
>    * dumpTypes
>    *      writes out to fout the queries to recreate all the user-defined types
>    */
> ***************
> *** 3195,3202 ****
>           if (!tinfo[i].typnamespace->dump)
>               continue;
>
> !         /* skip relation types */
> !         if (atooid(tinfo[i].typrelid) != 0)
>               continue;
>
>           /* skip undefined placeholder types */
> --- 3310,3317 ----
>           if (!tinfo[i].typnamespace->dump)
>               continue;
>
> !         /* skip relation types for non-stand-alone type relations*/
> !         if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
>               continue;
>
>           /* skip undefined placeholder types */
> ***************
> *** 3214,3219 ****
> --- 3329,3336 ----
>                               finfo, numFuncs, tinfo, numTypes);
>           else if (tinfo[i].typtype == 'd')
>               dumpOneDomain(fout, &tinfo[i]);
> +         else if (tinfo[i].typtype == 'c')
> +             dumpOneCompositeType(fout, &tinfo[i]);
>       }
>   }
>
> ***************
> *** 4839,4844 ****
> --- 4956,4962 ----
>
>           if (tbinfo->relkind != RELKIND_SEQUENCE)
>               continue;
> +
>           if (tbinfo->dump)
>           {
>               dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
> ***************
> *** 4854,4859 ****
> --- 4972,4979 ----
>               TableInfo       *tbinfo = &tblinfo[i];
>
>               if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
> +                 continue;
> +             if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
>                   continue;
>
>               if (tbinfo->dump)
> Index: src/bin/pg_dump/pg_dump.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.h,v
> retrieving revision 1.94
> diff -c -r1.94 pg_dump.h
> *** src/bin/pg_dump/pg_dump.h    2 Aug 2002 18:15:08 -0000    1.94
> --- src/bin/pg_dump/pg_dump.h    8 Aug 2002 18:26:11 -0000
> ***************
> *** 47,52 ****
> --- 47,53 ----
>       char       *usename;        /* name of owner, or empty string */
>       char       *typelem;        /* OID */
>       char       *typrelid;        /* OID */
> +     char        typrelkind;        /* 'r', 'v', 'c', etc */
>       char        typtype;        /* 'b', 'c', etc */
>       bool        isArray;        /* true if user-defined array type */
>       bool        isDefined;        /* true if typisdefined */
> Index: src/bin/psql/describe.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/psql/describe.c,v
> retrieving revision 1.57
> diff -c -r1.57 describe.c
> *** src/bin/psql/describe.c    2 Aug 2002 18:15:08 -0000    1.57
> --- src/bin/psql/describe.c    8 Aug 2002 22:26:38 -0000
> ***************
> *** 168,176 ****
>
>       /*
>        * do not include array types (start with underscore), do not include
> !      * user relations (typrelid!=0)
>        */
> !     appendPQExpBuffer(&buf, "FROM pg_type t\nWHERE t.typrelid = 0 AND t.typname !~ '^_.*'\n");
>
>       if (name)
>           /* accept either internal or external type name */
> --- 168,179 ----
>
>       /*
>        * do not include array types (start with underscore), do not include
> !      * user relations (typrelid!=0) unless they are type relations
>        */
> !     appendPQExpBuffer(&buf, "FROM pg_type t\nWHERE (t.typrelid = 0 ");
> !     appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
> !                               "where c.oid = t.typrelid)) ");
> !     appendPQExpBuffer(&buf, "AND t.typname !~ '^_.*'\n");
>
>       if (name)
>           /* accept either internal or external type name */
> 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 14:49:59 -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 14:49:59 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   extern void RemoveTypeById(Oid typeOid);
>   extern void DefineDomain(CreateDomainStmt *stmt);
>   extern void RemoveDomain(List *names, DropBehavior behavior);
> + extern Oid 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 14:49:59 -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 14:49:59 -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/pl/plpgsql/src/pl_comp.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
> retrieving revision 1.44
> diff -c -r1.44 pl_comp.c
> *** src/pl/plpgsql/src/pl_comp.c    8 Aug 2002 01:36:04 -0000    1.44
> --- src/pl/plpgsql/src/pl_comp.c    8 Aug 2002 14:50:00 -0000
> ***************
> *** 1030,1041 ****
>       }
>
>       /*
> !      * It must be a relation, sequence or view
>        */
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW)
>       {
>           ReleaseSysCache(classtup);
>           pfree(cp[0]);
> --- 1030,1042 ----
>       }
>
>       /*
> !      * It must be a relation, sequence, view, or type
>        */
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW &&
> !         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           ReleaseSysCache(classtup);
>           pfree(cp[0]);
> ***************
> *** 1130,1139 ****
>       if (!HeapTupleIsValid(classtup))
>           elog(ERROR, "%s: no such class", cp[0]);
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
> !     /* accept relation, sequence, or view pg_class entries */
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW)
>           elog(ERROR, "%s isn't a table", cp[0]);
>
>       /*
> --- 1131,1141 ----
>       if (!HeapTupleIsValid(classtup))
>           elog(ERROR, "%s: no such class", cp[0]);
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
> !     /* accept relation, sequence, view, or type pg_class entries */
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW &&
> !         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
>           elog(ERROR, "%s isn't a table", cp[0]);
>
>       /*
> 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 14:50:00 -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/expected/select_having.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/select_having.out,v
> retrieving revision 1.7
> diff -c -r1.7 select_having.out
> *** src/test/regress/expected/select_having.out    26 Jun 2002 21:58:56 -0000    1.7
> --- src/test/regress/expected/select_having.out    8 Aug 2002 17:48:10 -0000
> ***************
> *** 14,20 ****
>   INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I');
>   INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING count(*) = 1;
>    b |    c
>   ---+----------
>    1 | XXXX
> --- 14,20 ----
>   INSERT INTO test_having VALUES (8, 4, 'CCCC', 'I');
>   INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c;
>    b |    c
>   ---+----------
>    1 | XXXX
> ***************
> *** 23,37 ****
>
>   -- HAVING is equivalent to WHERE in this case
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING b = 3;
>    b |    c
>   ---+----------
> -  3 | BBBB
>    3 | bbbb
>   (2 rows)
>
>   SELECT lower(c), count(c) FROM test_having
> !     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a);
>     lower   | count
>   ----------+-------
>    bbbb     |     3
> --- 23,37 ----
>
>   -- HAVING is equivalent to WHERE in this case
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING b = 3 ORDER BY b, c;
>    b |    c
>   ---+----------
>    3 | bbbb
> +  3 | BBBB
>   (2 rows)
>
>   SELECT lower(c), count(c) FROM test_having
> !     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c);
>     lower   | count
>   ----------+-------
>    bbbb     |     3
> ***************
> *** 40,50 ****
>   (3 rows)
>
>   SELECT c, max(a) FROM test_having
> !     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a);
>       c     | max
>   ----------+-----
> -  XXXX     |   0
>    bbbb     |   5
>   (2 rows)
>
>   DROP TABLE test_having;
> --- 40,50 ----
>   (3 rows)
>
>   SELECT c, max(a) FROM test_having
> !     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c;
>       c     | max
>   ----------+-----
>    bbbb     |   5
> +  XXXX     |   0
>   (2 rows)
>
>   DROP TABLE test_having;
> 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 14:50:00 -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;
> Index: src/test/regress/sql/select_having.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/select_having.sql,v
> retrieving revision 1.7
> diff -c -r1.7 select_having.sql
> *** src/test/regress/sql/select_having.sql    26 Jun 2002 21:58:56 -0000    1.7
> --- src/test/regress/sql/select_having.sql    8 Aug 2002 17:43:02 -0000
> ***************
> *** 16,32 ****
>   INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
>
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING count(*) = 1;
>
>   -- HAVING is equivalent to WHERE in this case
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING b = 3;
>
>   SELECT lower(c), count(c) FROM test_having
> !     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a);
>
>   SELECT c, max(a) FROM test_having
> !     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a);
>
>   DROP TABLE test_having;
>
> --- 16,32 ----
>   INSERT INTO test_having VALUES (9, 4, 'CCCC', 'j');
>
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING count(*) = 1 ORDER BY b, c;
>
>   -- HAVING is equivalent to WHERE in this case
>   SELECT b, c FROM test_having
> !     GROUP BY b, c HAVING b = 3 ORDER BY b, c;
>
>   SELECT lower(c), count(c) FROM test_having
> !     GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a) ORDER BY lower(c);
>
>   SELECT c, max(a) FROM test_having
> !     GROUP BY c HAVING count(*) > 2 OR min(a) = max(a) ORDER BY c;
>
>   DROP TABLE test_having;
>

>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Your patch has been added to the PostgreSQL unapplied patches list at:

>> There is also a small adjustment to the expected output file for
>> select-having. I was getting a regression failure based on ordering of
>> the results, so I added ORDER BY clauses.

Don't forget that that part of the patch was retracted.

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
>
>>Your patch has been added to the PostgreSQL unapplied patches list at:
>
>
>>>There is also a small adjustment to the expected output file for
>>>select-having. I was getting a regression failure based on ordering of
>>>the results, so I added ORDER BY clauses.
>>
>
> Don't forget that that part of the patch was retracted.

You should be able to simply remove the changes to:
   Index: src/test/regress/expected/select_having.out
   Index: src/test/regress/sql/select_having.sql
from the patch.

Or let me know if you want me to do it and resubmit.

Thanks,

Joe



Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Bruce Momjian
Date:
Retracted part added.  [ I hadn't noticed the retraction.]

Your patch has been added to the PostgreSQL unapplied patches list at:

    http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Joe Conway wrote:
> Tom Lane wrote:
> > Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >
> >>Your patch has been added to the PostgreSQL unapplied patches list at:
> >
> >
> >>>There is also a small adjustment to the expected output file for
> >>>select-having. I was getting a regression failure based on ordering of
> >>>the results, so I added ORDER BY clauses.
> >>
> >
> > Don't forget that that part of the patch was retracted.
>
> You should be able to simply remove the changes to:
>    Index: src/test/regress/expected/select_having.out
>    Index: src/test/regress/sql/select_having.sql
> from the patch.
>
> Or let me know if you want me to do it and resubmit.
>
> Thanks,
>
> Joe
>
>
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Bruce Momjian
Date:
Sorry. this patch fails to apply.  A change to catalog/heap.c doesn't
seem to fit anywhere.  I have attached the reject.


---------------------------------------------------------------------------

Joe Conway wrote:
> Tom Lane wrote:
> > If you did it that way then you'd not need that ugly kluge in
> > RemoveType.  What you'd need instead is some smarts (a kluge!?) in
> > setting up the dependency.  Currently that dependency is made in
> > TypeCreate which doesn't know what sort of relation it's creating
> > a type for.  Probably the best answer is to pull that particular
> > dependency out of TypeCreate, and make it (in the proper direction)
> > in AddNewRelationType.
>
> Fixed.
>
> > Also, I'm not following the point of the separation between
> > DefineCompositeType and DefineCompositeTypeRelation; nor do I see a need
> > for a CommandCounterIncrement call in there.
>
> Fixed.
>
>
> > You have missed a number of places where this new relkind ought to
> > be special-cased the same way RELKIND_VIEW is --- for example
> > CheckAttributeNames and AddNewAttributeTuples, since a composite type
> > presumably shouldn't have system columns associated.  I'd counsel
> > looking at all references to RELKIND_VIEW to see which places also need
> > to check for RELKIND_COMPOSITE_TYPE.
>
> Yup, I had missed lots of things, not the least of which was pg_dump.
> New patch attached includes pg_dump, psql (\dT), docs, and regression
> support.
>
> There is also a small adjustment to the expected output file for
> select-having. I was getting a regression failure based on ordering of
> the results, so I added ORDER BY clauses.
>
> Passes all regression tests. If no more objections, please apply.
>
> Thanks,
>
> Joe
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
***************
*** 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);

      /*

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Bruce Momjian wrote:
> Sorry. this patch fails to apply.  A change to catalog/heap.c doesn't
> seem to fit anywhere.  I have attached the reject.
>

Lots of code drift in just one week! I'll rework the patch and resend.

Joe




Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> [ this doesn't apply: ]

>       /*
>        * 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);

>       /*


There's no longer a separate call to heap_storage_create in that routine
--- the right place to make the test is now in the storage_create
boolean parameter being passed to heap_create.  A simple change, but
it passeth patch's understanding ...

            regards, tom lane

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Joe Conway
Date:
Tom Lane wrote:
> There's no longer a separate call to heap_storage_create in that routine
> --- the right place to make the test is now in the storage_create
> boolean parameter being passed to heap_create.  A simple change, but
> it passeth patch's understanding ...

Thanks.

Attached is a patch against cvs tip as of 8:30 PM PST or so. Turned out
that even after fixing the failed hunks, there was a new spot in
bufmgr.c which needed to be fixed (related to temp relations;
RelationUpdateNumberOfBlocks). But thankfully the regression test code
caught it :-)

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    15 Aug 2002 03:06:23 -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.220
diff -c -r1.220 heap.c
*** src/backend/catalog/heap.c    11 Aug 2002 21:17:34 -0000    1.220
--- src/backend/catalog/heap.c    15 Aug 2002 04:21:03 -0000
***************
*** 357,365 ****
      /*
       * first check for collision with system attribute names
       *
!      * Skip this for a view, since it doesn't have system attributes.
       */
!     if (relkind != RELKIND_VIEW)
      {
          for (i = 0; i < natts; i++)
          {
--- 357,366 ----
      /*
       * first check for collision with system attribute names
       *
!      * Skip this for a view and type relation, since it doesn't have system
!      * attributes.
       */
!     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
      {
          for (i = 0; i < natts; i++)
          {
***************
*** 473,482 ****

      /*
       * Next we add the system attributes.  Skip OID if rel has no OIDs.
!      * Skip all for a view.  We don't bother with making datatype
!      * dependencies here, since presumably all these types are pinned.
       */
!     if (relkind != RELKIND_VIEW)
      {
          dpp = SysAtt;
          for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
--- 474,483 ----

      /*
       * Next we add the system attributes.  Skip OID if rel has no OIDs.
!      * Skip all for a view or type relation.  We don't bother with making
!      * datatype dependencies here, since presumably all these types are pinned.
       */
!     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
      {
          dpp = SysAtt;
          for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
***************
*** 689,701 ****
       * physical disk file.  (If we fail further down, it's the smgr's
       * responsibility to remove the disk file again.)
       *
!      * NB: create a physical file only if it's not a view.
       */
      new_rel_desc = heap_create(relname,
                                 relnamespace,
                                 tupdesc,
                                 shared_relation,
!                                (relkind != RELKIND_VIEW),
                                 allow_system_table_mods);

      /* Fetch the relation OID assigned by heap_create */
--- 690,703 ----
       * physical disk file.  (If we fail further down, it's the smgr's
       * responsibility to remove the disk file again.)
       *
!      * NB: create a physical file only if it's not a view or type relation.
       */
      new_rel_desc = heap_create(relname,
                                 relnamespace,
                                 tupdesc,
                                 shared_relation,
!                                (relkind != RELKIND_VIEW &&
!                                 relkind != RELKIND_COMPOSITE_TYPE),
                                 allow_system_table_mods);

      /* Fetch the relation OID assigned by heap_create */
***************
*** 1131,1137 ****
      /*
       * unlink the relation's physical file and finish up.
       */
!     if (rel->rd_rel->relkind != RELKIND_VIEW)
          smgrunlink(DEFAULT_SMGR, rel);

      /*
--- 1133,1140 ----
      /*
       * 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/catalog/namespace.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/namespace.c,v
retrieving revision 1.30
diff -c -r1.30 namespace.c
*** src/backend/catalog/namespace.c    9 Aug 2002 16:45:14 -0000    1.30
--- src/backend/catalog/namespace.c    15 Aug 2002 03:06:23 -0000
***************
*** 1585,1590 ****
--- 1585,1591 ----
              case RELKIND_RELATION:
              case RELKIND_SEQUENCE:
              case RELKIND_VIEW:
+             case RELKIND_COMPOSITE_TYPE:
                  AssertTupleDescHasOid(pgclass->rd_att);
                  object.classId = RelOid_pg_class;
                  object.objectId = HeapTupleGetOid(tuple);
Index: src/backend/catalog/pg_type.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_type.c,v
retrieving revision 1.77
diff -c -r1.77 pg_type.c
*** src/backend/catalog/pg_type.c    5 Aug 2002 03:29:16 -0000    1.77
--- src/backend/catalog/pg_type.c    15 Aug 2002 03:06:23 -0000
***************
*** 311,325 ****

          /*
           * If the type is a rowtype for a relation, mark it as internally
!          * dependent on the relation.  This allows it to be auto-dropped
!          * when the relation is, and not otherwise.
           */
          if (OidIsValid(relationOid))
          {
              referenced.classId = RelOid_pg_class;
              referenced.objectId = relationOid;
              referenced.objectSubId = 0;
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
          }

          /*
--- 311,338 ----

          /*
           * If the type is a rowtype for a relation, mark it as internally
!          * dependent on the relation, *unless* it is a stand-alone composite
!          * type relation. For the latter case, we have to reverse the
!          * dependency.
!          *
!          * In the former case, this allows the type to be auto-dropped
!          * when the relation is, and not otherwise. And in the latter,
!          * of course we get the opposite effect.
           */
          if (OidIsValid(relationOid))
          {
+             Relation    rel = relation_open(relationOid, AccessShareLock);
+             char        relkind = rel->rd_rel->relkind;
+             relation_close(rel, AccessShareLock);
+
              referenced.classId = RelOid_pg_class;
              referenced.objectId = relationOid;
              referenced.objectSubId = 0;
!
!             if (relkind != RELKIND_COMPOSITE_TYPE)
!                 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
!             else
!                 recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
          }

          /*
Index: src/backend/commands/copy.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/copy.c,v
retrieving revision 1.162
diff -c -r1.162 copy.c
*** src/backend/commands/copy.c    2 Aug 2002 18:15:06 -0000    1.162
--- src/backend/commands/copy.c    15 Aug 2002 03:06:23 -0000
***************
*** 398,403 ****
--- 398,406 ----
              if (rel->rd_rel->relkind == RELKIND_VIEW)
                  elog(ERROR, "You cannot copy view %s",
                       RelationGetRelationName(rel));
+             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+                 elog(ERROR, "You cannot copy type relation %s",
+                      RelationGetRelationName(rel));
              else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
                  elog(ERROR, "You cannot change sequence relation %s",
                       RelationGetRelationName(rel));
***************
*** 442,447 ****
--- 445,453 ----
          {
              if (rel->rd_rel->relkind == RELKIND_VIEW)
                  elog(ERROR, "You cannot copy view %s",
+                      RelationGetRelationName(rel));
+             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+                 elog(ERROR, "You cannot copy type relation %s",
                       RelationGetRelationName(rel));
              else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
                  elog(ERROR, "You cannot copy sequence %s",
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/tablecmds.c,v
retrieving revision 1.28
diff -c -r1.28 tablecmds.c
*** src/backend/commands/tablecmds.c    7 Aug 2002 21:45:01 -0000    1.28
--- src/backend/commands/tablecmds.c    15 Aug 2002 03:06:23 -0000
***************
*** 345,350 ****
--- 345,354 ----
          elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
               RelationGetRelationName(rel));

+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "TRUNCATE cannot be used on type relations. '%s' is a type",
+              RelationGetRelationName(rel));
+
      if (!allowSystemTableMods && IsSystemRelation(rel))
          elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
               RelationGetRelationName(rel));
***************
*** 3210,3221 ****
          case RELKIND_RELATION:
          case RELKIND_INDEX:
          case RELKIND_VIEW:
          case RELKIND_SEQUENCE:
          case RELKIND_TOASTVALUE:
              /* ok to change owner */
              break;
          default:
!             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
                   NameStr(tuple_class->relname));
      }
  }
--- 3214,3226 ----
          case RELKIND_RELATION:
          case RELKIND_INDEX:
          case RELKIND_VIEW:
+         case RELKIND_COMPOSITE_TYPE:
          case RELKIND_SEQUENCE:
          case RELKIND_TOASTVALUE:
              /* ok to change owner */
              break;
          default:
!             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, type, or sequence",
                   NameStr(tuple_class->relname));
      }
  }
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    15 Aug 2002 03:06:23 -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"
***************
*** 50,56 ****

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

-
  /*
   * DefineType
   *        Registers a new type.
--- 51,56 ----
***************
*** 665,668 ****
--- 665,707 ----
      }

      return procOid;
+ }
+
+ /*-------------------------------------------------------------------
+  * DefineCompositeType
+  *
+  * 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...
+  *
+  * DefineCompositeType returns relid for use when creating
+  * an implicit composite type during function creation
+  *-------------------------------------------------------------------
+  */
+ Oid
+ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
+ {
+     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...
+      */
+     return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
  }
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execMain.c,v
retrieving revision 1.173
diff -c -r1.173 execMain.c
*** src/backend/executor/execMain.c    7 Aug 2002 21:45:02 -0000    1.173
--- src/backend/executor/execMain.c    15 Aug 2002 03:06:23 -0000
***************
*** 786,791 ****
--- 786,795 ----
              elog(ERROR, "You can't change view relation %s",
                   RelationGetRelationName(resultRelationDesc));
              break;
+         case RELKIND_COMPOSITE_TYPE:
+             elog(ERROR, "You can't change type relation %s",
+                  RelationGetRelationName(resultRelationDesc));
+             break;
      }

      MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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.358
diff -c -r2.358 gram.y
*** src/backend/parser/gram.y    10 Aug 2002 19:01:53 -0000    2.358
--- src/backend/parser/gram.y    15 Aug 2002 03:06:23 -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,
***************
*** 2233,2238 ****
--- 2233,2271 ----
                      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);
***************
*** 2241,2246 ****
--- 2274,2282 ----
                      n->definition = $7;
                      $$ = (Node *)n;
                  }
+         ;
+
+ rowdefinition: '(' TableFuncElementList ')'            { $$ = $2; }
          ;

  definition: '(' def_list ')'                        { $$ = $2; }
Index: src/backend/storage/buffer/bufmgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/buffer/bufmgr.c,v
retrieving revision 1.129
diff -c -r1.129 bufmgr.c
*** src/backend/storage/buffer/bufmgr.c    11 Aug 2002 21:17:34 -0000    1.129
--- src/backend/storage/buffer/bufmgr.c    15 Aug 2002 04:47:07 -0000
***************
*** 1051,1056 ****
--- 1051,1058 ----
       */
      if (relation->rd_rel->relkind == RELKIND_VIEW)
          relation->rd_nblocks = 0;
+     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         relation->rd_nblocks = 0;
      else if (!relation->rd_isnew && !relation->rd_istemp)
          relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
      return relation->rd_nblocks;
***************
*** 1068,1073 ****
--- 1070,1077 ----
  RelationUpdateNumberOfBlocks(Relation relation)
  {
      if (relation->rd_rel->relkind == RELKIND_VIEW)
+         relation->rd_nblocks = 0;
+     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
          relation->rd_nblocks = 0;
      else
          relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
Index: src/backend/storage/smgr/smgr.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v
retrieving revision 1.58
diff -c -r1.58 smgr.c
*** src/backend/storage/smgr/smgr.c    6 Aug 2002 02:36:34 -0000    1.58
--- src/backend/storage/smgr/smgr.c    15 Aug 2002 03:06:23 -0000
***************
*** 263,268 ****
--- 263,270 ----

      if (reln->rd_rel->relkind == RELKIND_VIEW)
          return -1;
+     if (reln->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         return -1;
      if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0)
          if (!failOK)
              elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.281
diff -c -r1.281 postgres.c
*** src/backend/tcop/postgres.c    10 Aug 2002 20:29:18 -0000    1.281
--- src/backend/tcop/postgres.c    15 Aug 2002 03:06:23 -0000
***************
*** 2233,2238 ****
--- 2233,2242 ----
              }
              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    15 Aug 2002 03:06:23 -0000
***************
*** 70,75 ****
--- 70,76 ----
      {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
      {RELKIND_VIEW, "a", "view", "VIEW"},
      {RELKIND_INDEX, "an", "index", "INDEX"},
+     {RELKIND_COMPOSITE_TYPE, "a", "type", "TYPE"},
      {'\0', "a", "???", "???"}
  };

***************
*** 570,575 ****
--- 571,589 ----
                          DefineAggregate(stmt->defnames, stmt->definition);
                          break;
                  }
+             }
+             break;
+
+         case T_CompositeTypeStmt:        /* CREATE TYPE (composite) */
+             {
+                 Oid    relid;
+                 CompositeTypeStmt   *stmt = (CompositeTypeStmt *) parsetree;
+
+                 /*
+                  * DefineCompositeType returns relid for use when creating
+                  * an implicit composite type during function creation
+                  */
+                 relid = DefineCompositeType(stmt->typevar, stmt->coldeflist);
              }
              break;

Index: src/backend/utils/adt/tid.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/tid.c,v
retrieving revision 1.32
diff -c -r1.32 tid.c
*** src/backend/utils/adt/tid.c    16 Jul 2002 17:55:25 -0000    1.32
--- src/backend/utils/adt/tid.c    15 Aug 2002 03:06:23 -0000
***************
*** 226,231 ****
--- 226,234 ----
      if (rel->rd_rel->relkind == RELKIND_VIEW)
          return currtid_for_view(rel, tid);

+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "currtid can't handle type relations");
+
      ItemPointerCopy(tid, result);
      heap_get_latest_tid(rel, SnapshotNow, result);

***************
*** 248,253 ****
--- 251,259 ----
      rel = heap_openrv(relrv, AccessShareLock);
      if (rel->rd_rel->relkind == RELKIND_VIEW)
          return currtid_for_view(rel, tid);
+
+     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+         elog(ERROR, "currtid can't handle type relations");

      result = (ItemPointer) palloc(sizeof(ItemPointerData));
      ItemPointerCopy(tid, result);
Index: src/bin/pg_dump/common.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/common.c,v
retrieving revision 1.67
diff -c -r1.67 common.c
*** src/bin/pg_dump/common.c    30 Jul 2002 21:56:04 -0000    1.67
--- src/bin/pg_dump/common.c    15 Aug 2002 03:06:23 -0000
***************
*** 215,223 ****

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences and views never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW)
              continue;

          /* Don't bother computing anything for non-target tables, either */
--- 215,224 ----

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences, views, and types never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW ||
!             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              continue;

          /* Don't bother computing anything for non-target tables, either */
***************
*** 269,277 ****

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences and views never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW)
              continue;

          /* Don't bother computing anything for non-target tables, either */
--- 270,279 ----

      for (i = 0; i < numTables; i++)
      {
!         /* Sequences, views, and types never have parents */
          if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
!             tblinfo[i].relkind == RELKIND_VIEW ||
!             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              continue;

          /* Don't bother computing anything for non-target tables, either */
Index: src/bin/pg_dump/pg_dump.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.c,v
retrieving revision 1.281
diff -c -r1.281 pg_dump.c
*** src/bin/pg_dump/pg_dump.c    10 Aug 2002 16:57:31 -0000    1.281
--- src/bin/pg_dump/pg_dump.c    15 Aug 2002 03:06:23 -0000
***************
*** 95,100 ****
--- 95,101 ----
                              FuncInfo *g_finfo, int numFuncs,
                              TypeInfo *g_tinfo, int numTypes);
  static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
+ static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
  static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
                           TableInfo *g_tblinfo);
  static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
***************
*** 1171,1176 ****
--- 1172,1181 ----
          if (tblinfo[i].relkind == RELKIND_VIEW)
              continue;

+         /* Skip TYPE relations */
+         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+             continue;
+
          if (tblinfo[i].relkind == RELKIND_SEQUENCE)        /* already dumped */
              continue;

***************
*** 1575,1580 ****
--- 1580,1586 ----
      int            i_usename;
      int            i_typelem;
      int            i_typrelid;
+     int            i_typrelkind;
      int            i_typtype;
      int            i_typisdefined;

***************
*** 1595,1601 ****
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, typtype, typisdefined "
                            "FROM pg_type");
      }
      else
--- 1601,1609 ----
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, "
!                           "(select relkind from pg_class where oid = typrelid) as typrelkind, "
!                           "typtype, typisdefined "
                            "FROM pg_type");
      }
      else
***************
*** 1603,1609 ****
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "0::oid as typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, typtype, typisdefined "
                            "FROM pg_type");
      }

--- 1611,1619 ----
          appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
                            "0::oid as typnamespace, "
                            "(select usename from pg_user where typowner = usesysid) as usename, "
!                           "typelem, typrelid, "
!                           "''::char as typrelkind, "
!                           "typtype, typisdefined "
                            "FROM pg_type");
      }

***************
*** 1625,1630 ****
--- 1635,1641 ----
      i_usename = PQfnumber(res, "usename");
      i_typelem = PQfnumber(res, "typelem");
      i_typrelid = PQfnumber(res, "typrelid");
+     i_typrelkind = PQfnumber(res, "typrelkind");
      i_typtype = PQfnumber(res, "typtype");
      i_typisdefined = PQfnumber(res, "typisdefined");

***************
*** 1637,1642 ****
--- 1648,1654 ----
          tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
          tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
          tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
+         tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
          tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);

          /*
***************
*** 2102,2108 ****
          appendPQExpBuffer(query,
                            "SELECT pg_class.oid, relname, relacl, relkind, "
                            "relnamespace, "
-
                            "(select usename from pg_user where relowner = usesysid) as usename, "
                            "relchecks, reltriggers, "
                            "relhasindex, relhasrules, relhasoids "
--- 2114,2119 ----
***************
*** 2113,2118 ****
--- 2124,2130 ----
      }
      else if (g_fout->remoteVersion >= 70200)
      {
+         /* before 7.3 there were no type relations with relkind 'c' */
          appendPQExpBuffer(query,
                            "SELECT pg_class.oid, relname, relacl, relkind, "
                            "0::oid as relnamespace, "
***************
*** 2356,2361 ****
--- 2368,2377 ----
          if (tblinfo[i].relkind == RELKIND_SEQUENCE)
              continue;

+         /* Don't bother to collect info for type relations */
+         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+             continue;
+
          /* Don't bother with uninteresting tables, either */
          if (!tblinfo[i].interesting)
              continue;
***************
*** 3173,3178 ****
--- 3189,3293 ----
  }

  /*
+  * dumpOneCompositeType
+  *    writes out to fout the queries to recreate a user-defined stand-alone
+  *    composite type as requested by dumpTypes
+  */
+ static void
+ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
+ {
+     PQExpBuffer q = createPQExpBuffer();
+     PQExpBuffer delq = createPQExpBuffer();
+     PQExpBuffer query = createPQExpBuffer();
+     PGresult   *res;
+     int            ntups;
+     char       *attname;
+     char       *atttypdefn;
+     char       *attbasetype;
+     const char *((*deps)[]);
+     int            depIdx = 0;
+     int            i;
+
+     deps = malloc(sizeof(char *) * 10);
+
+     /* Set proper schema search path so type references list correctly */
+     selectSourceSchema(tinfo->typnamespace->nspname);
+
+     /* Fetch type specific details */
+     /* We assume here that remoteVersion must be at least 70300 */
+
+     appendPQExpBuffer(query, "SELECT a.attname, "
+                       "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn, "
+                       "a.atttypid as attbasetype "
+                       "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
+                       "WHERE t.oid = '%s'::pg_catalog.oid "
+                       "AND a.attrelid = t.typrelid",
+                       tinfo->oid);
+
+     res = PQexec(g_conn, query->data);
+     if (!res ||
+         PQresultStatus(res) != PGRES_TUPLES_OK)
+     {
+         write_msg(NULL, "query to obtain type information failed: %s", PQerrorMessage(g_conn));
+         exit_nicely();
+     }
+
+     /* Expecting at least a single result */
+     ntups = PQntuples(res);
+     if (ntups < 1)
+     {
+         write_msg(NULL, "Got no rows from: %s", query->data);
+         exit_nicely();
+     }
+
+     /* DROP must be fully qualified in case same name appears in pg_catalog */
+     appendPQExpBuffer(delq, "DROP TYPE %s.",
+                       fmtId(tinfo->typnamespace->nspname, force_quotes));
+     appendPQExpBuffer(delq, "%s RESTRICT;\n",
+                       fmtId(tinfo->typname, force_quotes));
+
+     appendPQExpBuffer(q,
+                       "CREATE TYPE %s AS (",
+                       fmtId(tinfo->typname, force_quotes));
+
+     for (i = 0; i < ntups; i++)
+     {
+         attname = PQgetvalue(res, i, PQfnumber(res, "attname"));
+         atttypdefn = PQgetvalue(res, i, PQfnumber(res, "atttypdefn"));
+         attbasetype = PQgetvalue(res, i, PQfnumber(res, "attbasetype"));
+
+         if (i > 0)
+             appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
+         else
+             appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
+
+         /* Depends on the base type */
+         (*deps)[depIdx++] = strdup(attbasetype);
+     }
+     appendPQExpBuffer(q, ");\n");
+
+     (*deps)[depIdx++] = NULL;        /* End of List */
+
+     ArchiveEntry(fout, tinfo->oid, tinfo->typname,
+                  tinfo->typnamespace->nspname,
+                  tinfo->usename, "TYPE", deps,
+                  q->data, delq->data, NULL, NULL, NULL);
+
+     /*** Dump Type Comments ***/
+     resetPQExpBuffer(q);
+
+     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname, force_quotes));
+     dumpComment(fout, q->data,
+                 tinfo->typnamespace->nspname, tinfo->usename,
+                 tinfo->oid, "pg_type", 0, NULL);
+
+     PQclear(res);
+     destroyPQExpBuffer(q);
+     destroyPQExpBuffer(delq);
+     destroyPQExpBuffer(query);
+ }
+
+ /*
   * dumpTypes
   *      writes out to fout the queries to recreate all the user-defined types
   */
***************
*** 3188,3195 ****
          if (!tinfo[i].typnamespace->dump)
              continue;

!         /* skip relation types */
!         if (atooid(tinfo[i].typrelid) != 0)
              continue;

          /* skip undefined placeholder types */
--- 3303,3310 ----
          if (!tinfo[i].typnamespace->dump)
              continue;

!         /* skip relation types for non-stand-alone type relations*/
!         if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
              continue;

          /* skip undefined placeholder types */
***************
*** 3207,3212 ****
--- 3322,3329 ----
                              finfo, numFuncs, tinfo, numTypes);
          else if (tinfo[i].typtype == 'd')
              dumpOneDomain(fout, &tinfo[i]);
+         else if (tinfo[i].typtype == 'c')
+             dumpOneCompositeType(fout, &tinfo[i]);
      }
  }

***************
*** 4832,4837 ****
--- 4949,4955 ----

          if (tbinfo->relkind != RELKIND_SEQUENCE)
              continue;
+
          if (tbinfo->dump)
          {
              dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
***************
*** 4847,4852 ****
--- 4965,4972 ----
              TableInfo       *tbinfo = &tblinfo[i];

              if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
+                 continue;
+             if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
                  continue;

              if (tbinfo->dump)
Index: src/bin/pg_dump/pg_dump.h
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.h,v
retrieving revision 1.94
diff -c -r1.94 pg_dump.h
*** src/bin/pg_dump/pg_dump.h    2 Aug 2002 18:15:08 -0000    1.94
--- src/bin/pg_dump/pg_dump.h    15 Aug 2002 03:06:23 -0000
***************
*** 47,52 ****
--- 47,53 ----
      char       *usename;        /* name of owner, or empty string */
      char       *typelem;        /* OID */
      char       *typrelid;        /* OID */
+     char        typrelkind;        /* 'r', 'v', 'c', etc */
      char        typtype;        /* 'b', 'c', etc */
      bool        isArray;        /* true if user-defined array type */
      bool        isDefined;        /* true if typisdefined */
Index: src/bin/psql/describe.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/bin/psql/describe.c,v
retrieving revision 1.60
diff -c -r1.60 describe.c
*** src/bin/psql/describe.c    10 Aug 2002 16:01:16 -0000    1.60
--- src/bin/psql/describe.c    15 Aug 2002 04:14:09 -0000
***************
*** 210,218 ****

      /*
       * do not include array types (start with underscore), do not include
!      * user relations (typrelid!=0)
       */
!     appendPQExpBuffer(&buf, "WHERE t.typrelid = 0 AND t.typname !~ '^_'\n");

      /* Match name pattern against either internal or external name */
      processNamePattern(&buf, pattern, true, false,
--- 210,221 ----

      /*
       * do not include array types (start with underscore), do not include
!      * user relations (typrelid!=0) unless they are type relations
       */
!     appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
!     appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
!                               "where c.oid = t.typrelid)) ");
!     appendPQExpBuffer(&buf, "AND t.typname !~ '^_'\n");

      /* Match name pattern against either internal or external name */
      processNamePattern(&buf, pattern, true, false,
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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -0000
***************
*** 58,63 ****
--- 58,64 ----
  extern void RemoveTypeById(Oid typeOid);
  extern void DefineDomain(CreateDomainStmt *stmt);
  extern void RemoveDomain(List *names, DropBehavior behavior);
+ extern Oid 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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.45
diff -c -r1.45 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c    12 Aug 2002 14:25:07 -0000    1.45
--- src/pl/plpgsql/src/pl_comp.c    15 Aug 2002 04:02:53 -0000
***************
*** 1028,1039 ****
      }

      /*
!      * It must be a relation, sequence or view
       */
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW)
      {
          ReleaseSysCache(classtup);
          pfree(cp[0]);
--- 1028,1040 ----
      }

      /*
!      * It must be a relation, sequence, view, or type
       */
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW &&
!         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
      {
          ReleaseSysCache(classtup);
          pfree(cp[0]);
***************
*** 1145,1154 ****
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      relname = NameStr(classStruct->relname);

!     /* accept relation, sequence, or view pg_class entries */
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW)
          elog(ERROR, "%s isn't a table", relname);

      /*
--- 1146,1156 ----
      classStruct = (Form_pg_class) GETSTRUCT(classtup);
      relname = NameStr(classStruct->relname);

!     /* accept relation, sequence, view, or type pg_class entries */
      if (classStruct->relkind != RELKIND_RELATION &&
          classStruct->relkind != RELKIND_SEQUENCE &&
!         classStruct->relkind != RELKIND_VIEW &&
!         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
          elog(ERROR, "%s isn't a table", relname);

      /*
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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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;

Re: stand-alone composite types patch (was [HACKERS] Proposal:

From
Bruce Momjian
Date:
Patch applied.  Thanks.

---------------------------------------------------------------------------


Joe Conway wrote:
> Tom Lane wrote:
> > There's no longer a separate call to heap_storage_create in that routine
> > --- the right place to make the test is now in the storage_create
> > boolean parameter being passed to heap_create.  A simple change, but
> > it passeth patch's understanding ...
>
> Thanks.
>
> Attached is a patch against cvs tip as of 8:30 PM PST or so. Turned out
> that even after fixing the failed hunks, there was a new spot in
> bufmgr.c which needed to be fixed (related to temp relations;
> RelationUpdateNumberOfBlocks). But thankfully the regression test code
> caught it :-)
>
> 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    15 Aug 2002 03:06:23 -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.220
> diff -c -r1.220 heap.c
> *** src/backend/catalog/heap.c    11 Aug 2002 21:17:34 -0000    1.220
> --- src/backend/catalog/heap.c    15 Aug 2002 04:21:03 -0000
> ***************
> *** 357,365 ****
>       /*
>        * first check for collision with system attribute names
>        *
> !      * Skip this for a view, since it doesn't have system attributes.
>        */
> !     if (relkind != RELKIND_VIEW)
>       {
>           for (i = 0; i < natts; i++)
>           {
> --- 357,366 ----
>       /*
>        * first check for collision with system attribute names
>        *
> !      * Skip this for a view and type relation, since it doesn't have system
> !      * attributes.
>        */
> !     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           for (i = 0; i < natts; i++)
>           {
> ***************
> *** 473,482 ****
>
>       /*
>        * Next we add the system attributes.  Skip OID if rel has no OIDs.
> !      * Skip all for a view.  We don't bother with making datatype
> !      * dependencies here, since presumably all these types are pinned.
>        */
> !     if (relkind != RELKIND_VIEW)
>       {
>           dpp = SysAtt;
>           for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
> --- 474,483 ----
>
>       /*
>        * Next we add the system attributes.  Skip OID if rel has no OIDs.
> !      * Skip all for a view or type relation.  We don't bother with making
> !      * datatype dependencies here, since presumably all these types are pinned.
>        */
> !     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           dpp = SysAtt;
>           for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
> ***************
> *** 689,701 ****
>        * physical disk file.  (If we fail further down, it's the smgr's
>        * responsibility to remove the disk file again.)
>        *
> !      * NB: create a physical file only if it's not a view.
>        */
>       new_rel_desc = heap_create(relname,
>                                  relnamespace,
>                                  tupdesc,
>                                  shared_relation,
> !                                (relkind != RELKIND_VIEW),
>                                  allow_system_table_mods);
>
>       /* Fetch the relation OID assigned by heap_create */
> --- 690,703 ----
>        * physical disk file.  (If we fail further down, it's the smgr's
>        * responsibility to remove the disk file again.)
>        *
> !      * NB: create a physical file only if it's not a view or type relation.
>        */
>       new_rel_desc = heap_create(relname,
>                                  relnamespace,
>                                  tupdesc,
>                                  shared_relation,
> !                                (relkind != RELKIND_VIEW &&
> !                                 relkind != RELKIND_COMPOSITE_TYPE),
>                                  allow_system_table_mods);
>
>       /* Fetch the relation OID assigned by heap_create */
> ***************
> *** 1131,1137 ****
>       /*
>        * unlink the relation's physical file and finish up.
>        */
> !     if (rel->rd_rel->relkind != RELKIND_VIEW)
>           smgrunlink(DEFAULT_SMGR, rel);
>
>       /*
> --- 1133,1140 ----
>       /*
>        * 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/catalog/namespace.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/namespace.c,v
> retrieving revision 1.30
> diff -c -r1.30 namespace.c
> *** src/backend/catalog/namespace.c    9 Aug 2002 16:45:14 -0000    1.30
> --- src/backend/catalog/namespace.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 1585,1590 ****
> --- 1585,1591 ----
>               case RELKIND_RELATION:
>               case RELKIND_SEQUENCE:
>               case RELKIND_VIEW:
> +             case RELKIND_COMPOSITE_TYPE:
>                   AssertTupleDescHasOid(pgclass->rd_att);
>                   object.classId = RelOid_pg_class;
>                   object.objectId = HeapTupleGetOid(tuple);
> Index: src/backend/catalog/pg_type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_type.c,v
> retrieving revision 1.77
> diff -c -r1.77 pg_type.c
> *** src/backend/catalog/pg_type.c    5 Aug 2002 03:29:16 -0000    1.77
> --- src/backend/catalog/pg_type.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 311,325 ****
>
>           /*
>            * If the type is a rowtype for a relation, mark it as internally
> !          * dependent on the relation.  This allows it to be auto-dropped
> !          * when the relation is, and not otherwise.
>            */
>           if (OidIsValid(relationOid))
>           {
>               referenced.classId = RelOid_pg_class;
>               referenced.objectId = relationOid;
>               referenced.objectSubId = 0;
> !             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
>           }
>
>           /*
> --- 311,338 ----
>
>           /*
>            * If the type is a rowtype for a relation, mark it as internally
> !          * dependent on the relation, *unless* it is a stand-alone composite
> !          * type relation. For the latter case, we have to reverse the
> !          * dependency.
> !          *
> !          * In the former case, this allows the type to be auto-dropped
> !          * when the relation is, and not otherwise. And in the latter,
> !          * of course we get the opposite effect.
>            */
>           if (OidIsValid(relationOid))
>           {
> +             Relation    rel = relation_open(relationOid, AccessShareLock);
> +             char        relkind = rel->rd_rel->relkind;
> +             relation_close(rel, AccessShareLock);
> +
>               referenced.classId = RelOid_pg_class;
>               referenced.objectId = relationOid;
>               referenced.objectSubId = 0;
> !
> !             if (relkind != RELKIND_COMPOSITE_TYPE)
> !                 recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
> !             else
> !                 recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
>           }
>
>           /*
> Index: src/backend/commands/copy.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/copy.c,v
> retrieving revision 1.162
> diff -c -r1.162 copy.c
> *** src/backend/commands/copy.c    2 Aug 2002 18:15:06 -0000    1.162
> --- src/backend/commands/copy.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 398,403 ****
> --- 398,406 ----
>               if (rel->rd_rel->relkind == RELKIND_VIEW)
>                   elog(ERROR, "You cannot copy view %s",
>                        RelationGetRelationName(rel));
> +             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +                 elog(ERROR, "You cannot copy type relation %s",
> +                      RelationGetRelationName(rel));
>               else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
>                   elog(ERROR, "You cannot change sequence relation %s",
>                        RelationGetRelationName(rel));
> ***************
> *** 442,447 ****
> --- 445,453 ----
>           {
>               if (rel->rd_rel->relkind == RELKIND_VIEW)
>                   elog(ERROR, "You cannot copy view %s",
> +                      RelationGetRelationName(rel));
> +             else if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +                 elog(ERROR, "You cannot copy type relation %s",
>                        RelationGetRelationName(rel));
>               else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
>                   elog(ERROR, "You cannot copy sequence %s",
> Index: src/backend/commands/tablecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/tablecmds.c,v
> retrieving revision 1.28
> diff -c -r1.28 tablecmds.c
> *** src/backend/commands/tablecmds.c    7 Aug 2002 21:45:01 -0000    1.28
> --- src/backend/commands/tablecmds.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 345,350 ****
> --- 345,354 ----
>           elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
>                RelationGetRelationName(rel));
>
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "TRUNCATE cannot be used on type relations. '%s' is a type",
> +              RelationGetRelationName(rel));
> +
>       if (!allowSystemTableMods && IsSystemRelation(rel))
>           elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
>                RelationGetRelationName(rel));
> ***************
> *** 3210,3221 ****
>           case RELKIND_RELATION:
>           case RELKIND_INDEX:
>           case RELKIND_VIEW:
>           case RELKIND_SEQUENCE:
>           case RELKIND_TOASTVALUE:
>               /* ok to change owner */
>               break;
>           default:
> !             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
>                    NameStr(tuple_class->relname));
>       }
>   }
> --- 3214,3226 ----
>           case RELKIND_RELATION:
>           case RELKIND_INDEX:
>           case RELKIND_VIEW:
> +         case RELKIND_COMPOSITE_TYPE:
>           case RELKIND_SEQUENCE:
>           case RELKIND_TOASTVALUE:
>               /* ok to change owner */
>               break;
>           default:
> !             elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, type, or sequence",
>                    NameStr(tuple_class->relname));
>       }
>   }
> 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    15 Aug 2002 03:06:23 -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"
> ***************
> *** 50,56 ****
>
>   static Oid findTypeIOFunction(List *procname, bool isOutput);
>
> -
>   /*
>    * DefineType
>    *        Registers a new type.
> --- 51,56 ----
> ***************
> *** 665,668 ****
> --- 665,707 ----
>       }
>
>       return procOid;
> + }
> +
> + /*-------------------------------------------------------------------
> +  * DefineCompositeType
> +  *
> +  * 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...
> +  *
> +  * DefineCompositeType returns relid for use when creating
> +  * an implicit composite type during function creation
> +  *-------------------------------------------------------------------
> +  */
> + Oid
> + DefineCompositeType(const RangeVar *typevar, List *coldeflist)
> + {
> +     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...
> +      */
> +     return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
>   }
> Index: src/backend/executor/execMain.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execMain.c,v
> retrieving revision 1.173
> diff -c -r1.173 execMain.c
> *** src/backend/executor/execMain.c    7 Aug 2002 21:45:02 -0000    1.173
> --- src/backend/executor/execMain.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 786,791 ****
> --- 786,795 ----
>               elog(ERROR, "You can't change view relation %s",
>                    RelationGetRelationName(resultRelationDesc));
>               break;
> +         case RELKIND_COMPOSITE_TYPE:
> +             elog(ERROR, "You can't change type relation %s",
> +                  RelationGetRelationName(resultRelationDesc));
> +             break;
>       }
>
>       MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
> 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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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.358
> diff -c -r2.358 gram.y
> *** src/backend/parser/gram.y    10 Aug 2002 19:01:53 -0000    2.358
> --- src/backend/parser/gram.y    15 Aug 2002 03:06:23 -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,
> ***************
> *** 2233,2238 ****
> --- 2233,2271 ----
>                       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);
> ***************
> *** 2241,2246 ****
> --- 2274,2282 ----
>                       n->definition = $7;
>                       $$ = (Node *)n;
>                   }
> +         ;
> +
> + rowdefinition: '(' TableFuncElementList ')'            { $$ = $2; }
>           ;
>
>   definition: '(' def_list ')'                        { $$ = $2; }
> Index: src/backend/storage/buffer/bufmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/buffer/bufmgr.c,v
> retrieving revision 1.129
> diff -c -r1.129 bufmgr.c
> *** src/backend/storage/buffer/bufmgr.c    11 Aug 2002 21:17:34 -0000    1.129
> --- src/backend/storage/buffer/bufmgr.c    15 Aug 2002 04:47:07 -0000
> ***************
> *** 1051,1056 ****
> --- 1051,1058 ----
>        */
>       if (relation->rd_rel->relkind == RELKIND_VIEW)
>           relation->rd_nblocks = 0;
> +     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         relation->rd_nblocks = 0;
>       else if (!relation->rd_isnew && !relation->rd_istemp)
>           relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
>       return relation->rd_nblocks;
> ***************
> *** 1068,1073 ****
> --- 1070,1077 ----
>   RelationUpdateNumberOfBlocks(Relation relation)
>   {
>       if (relation->rd_rel->relkind == RELKIND_VIEW)
> +         relation->rd_nblocks = 0;
> +     else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
>           relation->rd_nblocks = 0;
>       else
>           relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation);
> Index: src/backend/storage/smgr/smgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v
> retrieving revision 1.58
> diff -c -r1.58 smgr.c
> *** src/backend/storage/smgr/smgr.c    6 Aug 2002 02:36:34 -0000    1.58
> --- src/backend/storage/smgr/smgr.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 263,268 ****
> --- 263,270 ----
>
>       if (reln->rd_rel->relkind == RELKIND_VIEW)
>           return -1;
> +     if (reln->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         return -1;
>       if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0)
>           if (!failOK)
>               elog(ERROR, "cannot open %s: %m", RelationGetRelationName(reln));
> Index: src/backend/tcop/postgres.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/postgres.c,v
> retrieving revision 1.281
> diff -c -r1.281 postgres.c
> *** src/backend/tcop/postgres.c    10 Aug 2002 20:29:18 -0000    1.281
> --- src/backend/tcop/postgres.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 2233,2238 ****
> --- 2233,2242 ----
>               }
>               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    15 Aug 2002 03:06:23 -0000
> ***************
> *** 70,75 ****
> --- 70,76 ----
>       {RELKIND_SEQUENCE, "a", "sequence", "SEQUENCE"},
>       {RELKIND_VIEW, "a", "view", "VIEW"},
>       {RELKIND_INDEX, "an", "index", "INDEX"},
> +     {RELKIND_COMPOSITE_TYPE, "a", "type", "TYPE"},
>       {'\0', "a", "???", "???"}
>   };
>
> ***************
> *** 570,575 ****
> --- 571,589 ----
>                           DefineAggregate(stmt->defnames, stmt->definition);
>                           break;
>                   }
> +             }
> +             break;
> +
> +         case T_CompositeTypeStmt:        /* CREATE TYPE (composite) */
> +             {
> +                 Oid    relid;
> +                 CompositeTypeStmt   *stmt = (CompositeTypeStmt *) parsetree;
> +
> +                 /*
> +                  * DefineCompositeType returns relid for use when creating
> +                  * an implicit composite type during function creation
> +                  */
> +                 relid = DefineCompositeType(stmt->typevar, stmt->coldeflist);
>               }
>               break;
>
> Index: src/backend/utils/adt/tid.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/tid.c,v
> retrieving revision 1.32
> diff -c -r1.32 tid.c
> *** src/backend/utils/adt/tid.c    16 Jul 2002 17:55:25 -0000    1.32
> --- src/backend/utils/adt/tid.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 226,231 ****
> --- 226,234 ----
>       if (rel->rd_rel->relkind == RELKIND_VIEW)
>           return currtid_for_view(rel, tid);
>
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "currtid can't handle type relations");
> +
>       ItemPointerCopy(tid, result);
>       heap_get_latest_tid(rel, SnapshotNow, result);
>
> ***************
> *** 248,253 ****
> --- 251,259 ----
>       rel = heap_openrv(relrv, AccessShareLock);
>       if (rel->rd_rel->relkind == RELKIND_VIEW)
>           return currtid_for_view(rel, tid);
> +
> +     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
> +         elog(ERROR, "currtid can't handle type relations");
>
>       result = (ItemPointer) palloc(sizeof(ItemPointerData));
>       ItemPointerCopy(tid, result);
> Index: src/bin/pg_dump/common.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/common.c,v
> retrieving revision 1.67
> diff -c -r1.67 common.c
> *** src/bin/pg_dump/common.c    30 Jul 2002 21:56:04 -0000    1.67
> --- src/bin/pg_dump/common.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 215,223 ****
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences and views never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> --- 215,224 ----
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences, views, and types never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW ||
> !             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> ***************
> *** 269,277 ****
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences and views never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> --- 270,279 ----
>
>       for (i = 0; i < numTables; i++)
>       {
> !         /* Sequences, views, and types never have parents */
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
> !             tblinfo[i].relkind == RELKIND_VIEW ||
> !             tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
>               continue;
>
>           /* Don't bother computing anything for non-target tables, either */
> Index: src/bin/pg_dump/pg_dump.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.c,v
> retrieving revision 1.281
> diff -c -r1.281 pg_dump.c
> *** src/bin/pg_dump/pg_dump.c    10 Aug 2002 16:57:31 -0000    1.281
> --- src/bin/pg_dump/pg_dump.c    15 Aug 2002 03:06:23 -0000
> ***************
> *** 95,100 ****
> --- 95,101 ----
>                               FuncInfo *g_finfo, int numFuncs,
>                               TypeInfo *g_tinfo, int numTypes);
>   static void dumpOneDomain(Archive *fout, TypeInfo *tinfo);
> + static void dumpOneCompositeType(Archive *fout, TypeInfo *tinfo);
>   static void dumpOneTable(Archive *fout, TableInfo *tbinfo,
>                            TableInfo *g_tblinfo);
>   static void dumpOneSequence(Archive *fout, TableInfo *tbinfo,
> ***************
> *** 1171,1176 ****
> --- 1172,1181 ----
>           if (tblinfo[i].relkind == RELKIND_VIEW)
>               continue;
>
> +         /* Skip TYPE relations */
> +         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
> +             continue;
> +
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE)        /* already dumped */
>               continue;
>
> ***************
> *** 1575,1580 ****
> --- 1580,1586 ----
>       int            i_usename;
>       int            i_typelem;
>       int            i_typrelid;
> +     int            i_typrelkind;
>       int            i_typtype;
>       int            i_typisdefined;
>
> ***************
> *** 1595,1601 ****
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, typtype, typisdefined "
>                             "FROM pg_type");
>       }
>       else
> --- 1601,1609 ----
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, "
> !                           "(select relkind from pg_class where oid = typrelid) as typrelkind, "
> !                           "typtype, typisdefined "
>                             "FROM pg_type");
>       }
>       else
> ***************
> *** 1603,1609 ****
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "0::oid as typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, typtype, typisdefined "
>                             "FROM pg_type");
>       }
>
> --- 1611,1619 ----
>           appendPQExpBuffer(query, "SELECT pg_type.oid, typname, "
>                             "0::oid as typnamespace, "
>                             "(select usename from pg_user where typowner = usesysid) as usename, "
> !                           "typelem, typrelid, "
> !                           "''::char as typrelkind, "
> !                           "typtype, typisdefined "
>                             "FROM pg_type");
>       }
>
> ***************
> *** 1625,1630 ****
> --- 1635,1641 ----
>       i_usename = PQfnumber(res, "usename");
>       i_typelem = PQfnumber(res, "typelem");
>       i_typrelid = PQfnumber(res, "typrelid");
> +     i_typrelkind = PQfnumber(res, "typrelkind");
>       i_typtype = PQfnumber(res, "typtype");
>       i_typisdefined = PQfnumber(res, "typisdefined");
>
> ***************
> *** 1637,1642 ****
> --- 1648,1654 ----
>           tinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
>           tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
>           tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
> +         tinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
>           tinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
>
>           /*
> ***************
> *** 2102,2108 ****
>           appendPQExpBuffer(query,
>                             "SELECT pg_class.oid, relname, relacl, relkind, "
>                             "relnamespace, "
> -
>                             "(select usename from pg_user where relowner = usesysid) as usename, "
>                             "relchecks, reltriggers, "
>                             "relhasindex, relhasrules, relhasoids "
> --- 2114,2119 ----
> ***************
> *** 2113,2118 ****
> --- 2124,2130 ----
>       }
>       else if (g_fout->remoteVersion >= 70200)
>       {
> +         /* before 7.3 there were no type relations with relkind 'c' */
>           appendPQExpBuffer(query,
>                             "SELECT pg_class.oid, relname, relacl, relkind, "
>                             "0::oid as relnamespace, "
> ***************
> *** 2356,2361 ****
> --- 2368,2377 ----
>           if (tblinfo[i].relkind == RELKIND_SEQUENCE)
>               continue;
>
> +         /* Don't bother to collect info for type relations */
> +         if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
> +             continue;
> +
>           /* Don't bother with uninteresting tables, either */
>           if (!tblinfo[i].interesting)
>               continue;
> ***************
> *** 3173,3178 ****
> --- 3189,3293 ----
>   }
>
>   /*
> +  * dumpOneCompositeType
> +  *    writes out to fout the queries to recreate a user-defined stand-alone
> +  *    composite type as requested by dumpTypes
> +  */
> + static void
> + dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
> + {
> +     PQExpBuffer q = createPQExpBuffer();
> +     PQExpBuffer delq = createPQExpBuffer();
> +     PQExpBuffer query = createPQExpBuffer();
> +     PGresult   *res;
> +     int            ntups;
> +     char       *attname;
> +     char       *atttypdefn;
> +     char       *attbasetype;
> +     const char *((*deps)[]);
> +     int            depIdx = 0;
> +     int            i;
> +
> +     deps = malloc(sizeof(char *) * 10);
> +
> +     /* Set proper schema search path so type references list correctly */
> +     selectSourceSchema(tinfo->typnamespace->nspname);
> +
> +     /* Fetch type specific details */
> +     /* We assume here that remoteVersion must be at least 70300 */
> +
> +     appendPQExpBuffer(query, "SELECT a.attname, "
> +                       "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn, "
> +                       "a.atttypid as attbasetype "
> +                       "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
> +                       "WHERE t.oid = '%s'::pg_catalog.oid "
> +                       "AND a.attrelid = t.typrelid",
> +                       tinfo->oid);
> +
> +     res = PQexec(g_conn, query->data);
> +     if (!res ||
> +         PQresultStatus(res) != PGRES_TUPLES_OK)
> +     {
> +         write_msg(NULL, "query to obtain type information failed: %s", PQerrorMessage(g_conn));
> +         exit_nicely();
> +     }
> +
> +     /* Expecting at least a single result */
> +     ntups = PQntuples(res);
> +     if (ntups < 1)
> +     {
> +         write_msg(NULL, "Got no rows from: %s", query->data);
> +         exit_nicely();
> +     }
> +
> +     /* DROP must be fully qualified in case same name appears in pg_catalog */
> +     appendPQExpBuffer(delq, "DROP TYPE %s.",
> +                       fmtId(tinfo->typnamespace->nspname, force_quotes));
> +     appendPQExpBuffer(delq, "%s RESTRICT;\n",
> +                       fmtId(tinfo->typname, force_quotes));
> +
> +     appendPQExpBuffer(q,
> +                       "CREATE TYPE %s AS (",
> +                       fmtId(tinfo->typname, force_quotes));
> +
> +     for (i = 0; i < ntups; i++)
> +     {
> +         attname = PQgetvalue(res, i, PQfnumber(res, "attname"));
> +         atttypdefn = PQgetvalue(res, i, PQfnumber(res, "atttypdefn"));
> +         attbasetype = PQgetvalue(res, i, PQfnumber(res, "attbasetype"));
> +
> +         if (i > 0)
> +             appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
> +         else
> +             appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
> +
> +         /* Depends on the base type */
> +         (*deps)[depIdx++] = strdup(attbasetype);
> +     }
> +     appendPQExpBuffer(q, ");\n");
> +
> +     (*deps)[depIdx++] = NULL;        /* End of List */
> +
> +     ArchiveEntry(fout, tinfo->oid, tinfo->typname,
> +                  tinfo->typnamespace->nspname,
> +                  tinfo->usename, "TYPE", deps,
> +                  q->data, delq->data, NULL, NULL, NULL);
> +
> +     /*** Dump Type Comments ***/
> +     resetPQExpBuffer(q);
> +
> +     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->typname, force_quotes));
> +     dumpComment(fout, q->data,
> +                 tinfo->typnamespace->nspname, tinfo->usename,
> +                 tinfo->oid, "pg_type", 0, NULL);
> +
> +     PQclear(res);
> +     destroyPQExpBuffer(q);
> +     destroyPQExpBuffer(delq);
> +     destroyPQExpBuffer(query);
> + }
> +
> + /*
>    * dumpTypes
>    *      writes out to fout the queries to recreate all the user-defined types
>    */
> ***************
> *** 3188,3195 ****
>           if (!tinfo[i].typnamespace->dump)
>               continue;
>
> !         /* skip relation types */
> !         if (atooid(tinfo[i].typrelid) != 0)
>               continue;
>
>           /* skip undefined placeholder types */
> --- 3303,3310 ----
>           if (!tinfo[i].typnamespace->dump)
>               continue;
>
> !         /* skip relation types for non-stand-alone type relations*/
> !         if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
>               continue;
>
>           /* skip undefined placeholder types */
> ***************
> *** 3207,3212 ****
> --- 3322,3329 ----
>                               finfo, numFuncs, tinfo, numTypes);
>           else if (tinfo[i].typtype == 'd')
>               dumpOneDomain(fout, &tinfo[i]);
> +         else if (tinfo[i].typtype == 'c')
> +             dumpOneCompositeType(fout, &tinfo[i]);
>       }
>   }
>
> ***************
> *** 4832,4837 ****
> --- 4949,4955 ----
>
>           if (tbinfo->relkind != RELKIND_SEQUENCE)
>               continue;
> +
>           if (tbinfo->dump)
>           {
>               dumpOneSequence(fout, tbinfo, schemaOnly, dataOnly);
> ***************
> *** 4847,4852 ****
> --- 4965,4972 ----
>               TableInfo       *tbinfo = &tblinfo[i];
>
>               if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
> +                 continue;
> +             if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
>                   continue;
>
>               if (tbinfo->dump)
> Index: src/bin/pg_dump/pg_dump.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/pg_dump/pg_dump.h,v
> retrieving revision 1.94
> diff -c -r1.94 pg_dump.h
> *** src/bin/pg_dump/pg_dump.h    2 Aug 2002 18:15:08 -0000    1.94
> --- src/bin/pg_dump/pg_dump.h    15 Aug 2002 03:06:23 -0000
> ***************
> *** 47,52 ****
> --- 47,53 ----
>       char       *usename;        /* name of owner, or empty string */
>       char       *typelem;        /* OID */
>       char       *typrelid;        /* OID */
> +     char        typrelkind;        /* 'r', 'v', 'c', etc */
>       char        typtype;        /* 'b', 'c', etc */
>       bool        isArray;        /* true if user-defined array type */
>       bool        isDefined;        /* true if typisdefined */
> Index: src/bin/psql/describe.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/bin/psql/describe.c,v
> retrieving revision 1.60
> diff -c -r1.60 describe.c
> *** src/bin/psql/describe.c    10 Aug 2002 16:01:16 -0000    1.60
> --- src/bin/psql/describe.c    15 Aug 2002 04:14:09 -0000
> ***************
> *** 210,218 ****
>
>       /*
>        * do not include array types (start with underscore), do not include
> !      * user relations (typrelid!=0)
>        */
> !     appendPQExpBuffer(&buf, "WHERE t.typrelid = 0 AND t.typname !~ '^_'\n");
>
>       /* Match name pattern against either internal or external name */
>       processNamePattern(&buf, pattern, true, false,
> --- 210,221 ----
>
>       /*
>        * do not include array types (start with underscore), do not include
> !      * user relations (typrelid!=0) unless they are type relations
>        */
> !     appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
> !     appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
> !                               "where c.oid = t.typrelid)) ");
> !     appendPQExpBuffer(&buf, "AND t.typname !~ '^_'\n");
>
>       /* Match name pattern against either internal or external name */
>       processNamePattern(&buf, pattern, true, false,
> 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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
>   extern void RemoveTypeById(Oid typeOid);
>   extern void DefineDomain(CreateDomainStmt *stmt);
>   extern void RemoveDomain(List *names, DropBehavior behavior);
> + extern Oid 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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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/pl/plpgsql/src/pl_comp.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
> retrieving revision 1.45
> diff -c -r1.45 pl_comp.c
> *** src/pl/plpgsql/src/pl_comp.c    12 Aug 2002 14:25:07 -0000    1.45
> --- src/pl/plpgsql/src/pl_comp.c    15 Aug 2002 04:02:53 -0000
> ***************
> *** 1028,1039 ****
>       }
>
>       /*
> !      * It must be a relation, sequence or view
>        */
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW)
>       {
>           ReleaseSysCache(classtup);
>           pfree(cp[0]);
> --- 1028,1040 ----
>       }
>
>       /*
> !      * It must be a relation, sequence, view, or type
>        */
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW &&
> !         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
>       {
>           ReleaseSysCache(classtup);
>           pfree(cp[0]);
> ***************
> *** 1145,1154 ****
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       relname = NameStr(classStruct->relname);
>
> !     /* accept relation, sequence, or view pg_class entries */
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW)
>           elog(ERROR, "%s isn't a table", relname);
>
>       /*
> --- 1146,1156 ----
>       classStruct = (Form_pg_class) GETSTRUCT(classtup);
>       relname = NameStr(classStruct->relname);
>
> !     /* accept relation, sequence, view, or type pg_class entries */
>       if (classStruct->relkind != RELKIND_RELATION &&
>           classStruct->relkind != RELKIND_SEQUENCE &&
> !         classStruct->relkind != RELKIND_VIEW &&
> !         classStruct->relkind != RELKIND_COMPOSITE_TYPE)
>           elog(ERROR, "%s isn't a table", relname);
>
>       /*
> 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    15 Aug 2002 03:06:23 -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    15 Aug 2002 03:06:23 -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;

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073