We've discussed at least a couple of times before that it would be nice
to be able to create stand-alone composite types. Tom mentioned that
ideally this would be done as part of a refactoring of system tables so
that attributes belonged to pg_type, instead of belonging to pg_class.
But it wasn't clear that this approach was worth the effort,
particularly due to backwards compatability breakage.

Recently Tom mentioned another alternative (see:
http://archives.postgresql.org/pgsql-hackers/2002-07/msg00788.php for
more). The basic idea was to "create a new 'dummy' relkind for a
pg_class entry that isn't a real relation, but merely a front for a
composite type in pg_type."

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.

3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
    composite type, e.g.:

    CREATE [ OR REPLACE ] FUNCTION name ( [ argtype [, ...] ] )
    RETURNS [setof] { data_type | (column_name data_type [, ... ]) }...

    This would automatically create a stand-alone composite type with a
    system generated name for the function. Thanks to the new dependency
    tracking, the implicit composite type would go away if the function
    is dropped.


Comments, objections, or thoughts?

Thanks,

Joe



Mon, 29 Jul 2002 22:50:41 -0700
Joe Conway <mail@joeconway.com>
Proposal: stand-alone composite types
> 3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
>     composite type, e.g.:
>
>     CREATE [ OR REPLACE ] FUNCTION name ( [ argtype [, ...] ] )
>     RETURNS [setof] { data_type | (column_name data_type [, ... ]) }...
>
>     This would automatically create a stand-alone composite type with a
>     system generated name for the function. Thanks to the new dependency
>     tracking, the implicit composite type would go away if the function
>     is dropped.
>
>
> Comments, objections, or thoughts?

I'm just licking my lips in anticipation of converting my entire website to
SRFs ;)

Chris


Tue, 30 Jul 2002 14:00:46 +0800
"Christopher Kings-Lynne" <chriskl@familyhealth.com.au>
Re: Proposal: stand-alone composite types
Joe Conway wrote:
> 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.

I'm working on stand-alone composite types and running into a 
reduce/reduce problem with the grammer. Any suggestions would be 
appreciated. Here's what I have:

DefineStmt:
   CREATE AGGREGATE func_name definition
   {
     . . .
   }
   | CREATE TYPE_P qualified_name AS
     '(' TableFuncElementList ')'
   {
     CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
     n->typevar = $3;
     n->coldeflist = $6;
     $$ = (Node *)n;
   }

Thanks,

Joe


Wed, 07 Aug 2002 09:55:47 -0700
Joe Conway <mail@joeconway.com>
Re: Proposal: stand-alone composite types
Joe Conway <mail@joeconway.com> writes:
> I'm working on stand-alone composite types and running into a 
> reduce/reduce problem with the grammer. Any suggestions would be 
> appreciated. Here's what I have:

> DefineStmt:
>    | CREATE TYPE_P qualified_name AS
>      '(' TableFuncElementList ')'

Use any_name, not qualified_name.  As-is, you're forcing the parser
to try to distinguish the two forms of CREATE TYPE before it can
see anything that would tell the difference.

In hindsight I think it was a mistake to set up RangeVar/qualified_name
as a distinct reduction path from non-relation qualified names ---
we'd have been better off using a single production and a uniform
intermediate representation.  But I haven't had time to investigate
simplifying the grammar that way.

			regards, tom lane

Wed, 07 Aug 2002 23:25:52 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: 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
Wed, 07 Aug 2002 21:26:03 -0700
Joe Conway <mail@joeconway.com>
stand-alone composite types patch (was [HACKERS] Proposal:
 stand-alone
	composite types)
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

Thu, 08 Aug 2002 00:55:22 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
	stand-alone composite types)
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



Wed, 07 Aug 2002 22:06:47 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Thu, 08 Aug 2002 01:21:43 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
	stand-alone composite types)
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


Thu, 08 Aug 2002 14:11:38 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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
Thu, 08 Aug 2002 15:48:41 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Thu, 08 Aug 2002 18:57:04 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
	stand-alone composite types)
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


Thu, 08 Aug 2002 16:40:00 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Thu, 08 Aug 2002 23:51:51 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
	stand-alone composite types)
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


Fri, 09 Aug 2002 09:01:56 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Fri, 09 Aug 2002 12:26:30 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal: 
Joe Conway writes:

> 3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
>     composite type, e.g.:

Forgive this blunt question, but:  Why?

Of course I can see the answer, it's convenient, but wouldn't the system
be more consistent overall if all functions and types are declared
explicitly?

-- 
Peter Eisentraut   peter_e@gmx.net


Sat, 10 Aug 2002 00:53:03 +0200 (CEST)
Peter Eisentraut <peter_e@gmx.net>
Re: Proposal: stand-alone composite types
Peter Eisentraut wrote:
> Joe Conway writes:
>>3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
>>    composite type, e.g.:
> 
> Forgive this blunt question, but:  Why?

Now's a *great* time for a blunt question because I haven't started 
actively working on this yet. Much better than after I'm done.  ;-)


> Of course I can see the answer, it's convenient, but wouldn't the system
> be more consistent overall if all functions and types are declared
> explicitly?
> 

And of couse you are correct. It is almost purely convenience. My 
reasoning was this: if I am creating a function which returns a 
composite type, then the fact that a named composite type exists is 
superfluous to me. It would be more natural for me to do:

    CREATE FUNCTION foo() RETURNS SETOF (f1 int, f2 text);

than to do:

    CREATE TYPE some_arbitrary_name AS (f1 int, f2 text);
    CREATE FUNCTION foo() RETURNS SETOF some_arbitrary_name;

But I admit it is only a "nice-to-have", not a "need-to-have".

How do others feel? Do we want to be able to implicitly create a 
composite type during function creation? Or is it unneeded bloat?

I prefer the former, but don't have a strong argument against the latter.

Joe



Fri, 09 Aug 2002 16:03:55 -0700
Joe Conway <mail@joeconway.com>
Re: Proposal: stand-alone composite types
I think it buys the same as SERIAL does for sequences.

Is it likely to have more than one function using a complex type like
that?  If not, then allowing it's creation (not enforcing) could be
useful.


On Fri, 2002-08-09 at 19:03, Joe Conway wrote:
> Peter Eisentraut wrote:
> > Joe Conway writes:
> >>3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
> >>    composite type, e.g.:
> > 
> > Forgive this blunt question, but:  Why?
> 
> Now's a *great* time for a blunt question because I haven't started 
> actively working on this yet. Much better than after I'm done.  ;-)
> 
> 
> > Of course I can see the answer, it's convenient, but wouldn't the system
> > be more consistent overall if all functions and types are declared
> > explicitly?
> > 
> 
> And of couse you are correct. It is almost purely convenience. My 
> reasoning was this: if I am creating a function which returns a 
> composite type, then the fact that a named composite type exists is 
> superfluous to me. It would be more natural for me to do:
> 
>     CREATE FUNCTION foo() RETURNS SETOF (f1 int, f2 text);
> 
> than to do:
> 
>     CREATE TYPE some_arbitrary_name AS (f1 int, f2 text);
>     CREATE FUNCTION foo() RETURNS SETOF some_arbitrary_name;
> 
> But I admit it is only a "nice-to-have", not a "need-to-have".
> 
> How do others feel? Do we want to be able to implicitly create a 
> composite type during function creation? Or is it unneeded bloat?
> 
> I prefer the former, but don't have a strong argument against the latter.
> 
> Joe
> 
> 
> 
> ---------------------------(end of broadcast)---------------------------
> TIP 2: you can get off all lists at once with the unregister command
>     (send "unregister YourEmailAddressHere" to majordomo@postgresql.org)
> 



09 Aug 2002 19:08:11 -0400
Rod Taylor <rbt@zort.ca>
Re: Proposal: stand-alone composite types
Rod Taylor wrote:
> I think it buys the same as SERIAL does for sequences.

That's a great analogy.

> Is it likely to have more than one function using a complex type like
> that?  If not, then allowing it's creation (not enforcing) could be
> useful.

That's what I was thinking. In cases where you want to use the type for 
several functions, use CREATE TYPE. If you only need the type for one 
function, let the function creation process manage it for you.

Joe


Fri, 09 Aug 2002 16:13:30 -0700
Joe Conway <mail@joeconway.com>
Re: Proposal: stand-alone composite types

> > Is it likely to have more than one function using a complex type like
> > that?  If not, then allowing it's creation (not enforcing) could be
> > useful.
> 
> That's what I was thinking. In cases where you want to use the type for 
> several functions, use CREATE TYPE. If you only need the type for one 
> function, let the function creation process manage it for you.

So long as the type dissapears with the drop of the function.  But don't
make stuff you don't clean up :)



09 Aug 2002 19:46:28 -0400
Rod Taylor <rbt@zort.ca>
Re: Proposal: stand-alone composite types
> That's what I was thinking. In cases where you want to use the type for
> several functions, use CREATE TYPE. If you only need the type for one
> function, let the function creation process manage it for you.

It would be nice then to have some mechanism for converting the
"automatic type" to a named type which could be used elsewhere.
Otherwise one would need to garbage collect the separate stuff later,
which would probably go into the "not so convenient" category of
features...

                   - Thomas

Fri, 09 Aug 2002 17:26:30 -0700
Thomas Lockhart <lockhart@fourpalms.org>
Re: Proposal: stand-alone composite types
Thomas Lockhart wrote:
>>That's what I was thinking. In cases where you want to use the type for
>>several functions, use CREATE TYPE. If you only need the type for one
>>function, let the function creation process manage it for you.
> 
> It would be nice then to have some mechanism for converting the
> "automatic type" to a named type which could be used elsewhere.
> Otherwise one would need to garbage collect the separate stuff later,
> which would probably go into the "not so convenient" category of
> features...

Well I think that could be handled with the new dependency tracking 
system. Same as the SERIAL/sequence analogy -- when you drop the 
function, the type would automatically and transparently also get dropped.

Joe



Fri, 09 Aug 2002 17:36:46 -0700
Joe Conway <mail@joeconway.com>
Re: Proposal: stand-alone composite types
Joe Conway wrote:
> Thomas Lockhart wrote:
> >>That's what I was thinking. In cases where you want to use the type for
> >>several functions, use CREATE TYPE. If you only need the type for one
> >>function, let the function creation process manage it for you.
> > 
> > It would be nice then to have some mechanism for converting the
> > "automatic type" to a named type which could be used elsewhere.
> > Otherwise one would need to garbage collect the separate stuff later,
> > which would probably go into the "not so convenient" category of
> > features...
> 
> Well I think that could be handled with the new dependency tracking 
> system. Same as the SERIAL/sequence analogy -- when you drop the 
> function, the type would automatically and transparently also get dropped.

All this type extension stuff is complex.  If we can make it easier for
people to get started with it, we should.

-- 
  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

Fri, 9 Aug 2002 21:59:30 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: Proposal: stand-alone composite types
Peter Eisentraut <peter_e@gmx.net> writes:
> Joe Conway writes:
>> 3. Modify CREATE FUNCTION to allow the implicit creation of a dependent
>> composite type, e.g.:

> Forgive this blunt question, but:  Why?

> Of course I can see the answer, it's convenient, but wouldn't the system
> be more consistent overall if all functions and types are declared
> explicitly?

I was wondering about that too, in particular: what name are you going
to give to the implicit type, and what if it conflicts?

The already-accepted mechanism for anonymous function-result types for
RECORD functions doesn't have that problem, because it has no need to
create a catalog entry for the anonymous type.  But I'm not sure what
to do for record types that need to be present in the catalogs.

			regards, tom lane

Sat, 10 Aug 2002 00:52:57 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: Proposal: stand-alone composite types 
Tom Lane wrote:
> I was wondering about that too, in particular: what name are you going
> to give to the implicit type, and what if it conflicts?
> 
> The already-accepted mechanism for anonymous function-result types for
> RECORD functions doesn't have that problem, because it has no need to
> create a catalog entry for the anonymous type.  But I'm not sure what
> to do for record types that need to be present in the catalogs.

I was intending to use the same naming method used for SERIAL sequences.

But since the poll from this afternoon only showed weak support and 
relatively strong objections, I'm OK with putting this aside for now. If 
enough people seem interested once they start using table functions in 
7.3, we can always resurrect this idea.

The most important changes (IMHO) were the "anonymous type" and "CREATE 
TYPE x AS()" pieces anyway, so I'm happy where we are (at least once the 
stand-alone composite type patch is applied ;) ). Onward and upward...

Joe




Fri, 09 Aug 2002 22:12:26 -0700
Joe Conway <mail@joeconway.com>
Re: Proposal: stand-alone composite types
> than to do:
>
>     CREATE TYPE some_arbitrary_name AS (f1 int, f2 text);
>     CREATE FUNCTION foo() RETURNS SETOF some_arbitrary_name;
>
> But I admit it is only a "nice-to-have", not a "need-to-have".
>
> How do others feel? Do we want to be able to implicitly create a
> composite type during function creation? Or is it unneeded bloat?
>
> I prefer the former, but don't have a strong argument against the latter.

The former is super sweet, but does require some extra catalog entries for
every procedure - but that's the DBA's problem.  They can always use the
latter syntax.  The format syntax is cool and easy and it Should Just Work
for newbies...

Chris



Sat, 10 Aug 2002 17:58:17 +0800
"Christopher Kings-Lynne" <chriskl@familyhealth.com.au>
Re: Proposal: stand-alone composite types

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

Sun, 11 Aug 2002 01:09:17 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Sun, 11 Aug 2002 10:44:17 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal: 
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



Sun, 11 Aug 2002 08:05:02 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:

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

Mon, 12 Aug 2002 01:06:31 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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);
  
  	/*
Wed, 14 Aug 2002 22:43:01 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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




Wed, 14 Aug 2002 20:19:45 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:
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

Wed, 14 Aug 2002 23:30:35 -0400
Tom Lane <tgl@sss.pgh.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal: 
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
Wed, 14 Aug 2002 22:19:19 -0700
Joe Conway <mail@joeconway.com>
Re: stand-alone composite types patch (was [HACKERS] Proposal:

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

Thu, 15 Aug 2002 12:35:26 -0400 (EDT)
Bruce Momjian <pgman@candle.pha.pa.us>
Re: stand-alone composite types patch (was [HACKERS] Proposal: