diff --git a/contrib/dbsize/dbsize.c b/contrib/dbsize/dbsize.c
index 0e5e63d1eee49f2687b58d693627cbb13599e0a2..8bc216bf7993c5588801b5697d599777e13e7ff4 100644
--- a/contrib/dbsize/dbsize.c
+++ b/contrib/dbsize/dbsize.c
@@ -112,7 +112,7 @@ relation_size(PG_FUNCTION_ARGS)
 
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
 															 "relation_size"));
-	relation = relation_openrv(relrv, AccessShareLock);
+	relation = heap_openrv(relrv, AccessShareLock);
 
 	relnode = relation->rd_rel->relfilenode;
 
@@ -140,7 +140,7 @@ relation_size(PG_FUNCTION_ARGS)
 		segcount++;
 	}
 
-	relation_close(relation, AccessShareLock);
+	heap_close(relation, AccessShareLock);
 
 	PG_RETURN_INT64(totalsize);
 }
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index f100f4d87b5b7a185a6de4dde8e3bb08e400bdba..b2d454f129b19cae91de59bc5a06caf4c16b1b58 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.33 2002/08/23 00:33:24 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.34 2002/08/29 00:17:01 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -32,11 +32,7 @@ CREATE TYPE <replaceable class="parameter">typename</replaceable> ( INPUT = <rep
 )
 
 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> [, ... ] )
+    ( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] )
   </synopsis>
   
   <refsect2 id="R2-SQL-CREATETYPE-1">
@@ -216,8 +212,12 @@ CREATE TYPE
    type names also must not conflict with table names in the same schema.)
   </para>
 
+  <refsect2>
+   <title>Base Types</title>
+
   <para>
-   The first form of <command>CREATE TYPE</command>  requires  the  
+   The first form of <command>CREATE TYPE</command> creates a new base type
+   (scalar type).  It  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
@@ -338,20 +338,27 @@ CREATE TYPE
    a row fit, but they will be kept in the main table preferentially over
    <literal>extended</literal> and <literal>external</literal> items.)
   </para>
+  </refsect2>
+
+  <refsect2>
+   <title>Composite Types</title>
 
   <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.
+   The second form of <command>CREATE TYPE</command>
+   creates a composite type.
+   The composite type is specified by a list of column names and datatypes.
+   This is essentially the same as the row type
+   of a table, but using <command>CREATE TYPE</command> avoids the need to
+   create an actual table when all that is wanted is to define a type.
+   A stand-alone composite type is useful as the return type of a function.
   </para>
+  </refsect2>
 
   <refsect2>
    <title>Array Types</title>
 
    <para>
-    Whenever a user-defined data type is created, 
+    Whenever a user-defined base data type is created, 
     <productname>PostgreSQL</productname> automatically creates an
     associated array type, whose name consists of the base type's
     name prepended with an underscore.  The parser understands this
@@ -436,8 +443,8 @@ CREATE TABLE big_objs (id int4, obj bigobj);
    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;
+CREATE TYPE compfoo AS (f1 int, f2 text);
+CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, fooname FROM foo' LANGUAGE SQL;
 </programlisting>
   </para>
  </refsect1>
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 5fdaf90f0acd2ea46c1a970c2cdd2cd3832d72ee..f6dd0397570a318f3aca20cd509b6450ae0eee87 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.57 2002/08/28 14:35:37 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.58 2002/08/29 00:17:01 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -40,10 +40,10 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
 ( <replaceable class="PARAMETER">select</replaceable> )
     [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ]
 |
-<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] )
     [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> | <replaceable class="PARAMETER">column_definition_list</replaceable> ) ]
 |
-<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argtype</replaceable> [, ...] ] )
+<replaceable class="PARAMETER">table_function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] )
     AS ( <replaceable class="PARAMETER">column_definition_list</replaceable> )
 |
 <replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable>
@@ -142,10 +142,14 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
       <term><replaceable class="PARAMETER">alias</replaceable></term>
       <listitem>
        <para>
-	A substitute name for the preceding
-	<replaceable class="PARAMETER">table_name</replaceable>.
+	A substitute name for the FROM item containing the alias.
 	An alias is used for brevity or to eliminate ambiguity for self-joins
-	(where the same table is scanned multiple times).  If an alias is
+	(where the same table is scanned multiple times).  When an alias
+	is provided, it completely hides the actual name of the table or
+	table function; for example given <literal>FROM foo AS f</>, the
+	remainder of the SELECT must refer to this FROM item as <literal>f</>
+	not <literal>foo</>.
+	If an alias is
 	written, a column alias list can also be written to provide
 	substitute names for one or more columns of the table.
        </para>
@@ -172,12 +176,15 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
 	A table function can appear in the FROM clause.  This acts as though
 	its output were created as a temporary table for the duration of
 	this single SELECT command. An alias may also be used. If an alias is
-	written, a column alias list can also be written to provide	substitute names
-	for one or more columns of the table function. If the table function has been
-	defined as returning the RECORD data type, an alias, or the keyword AS, must
-    also be present, followed by a column definition list in the form
-	( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ).
-	The column definition list must match the actual number and types returned by the function.
+	written, a column alias list can also be written to provide substitute
+	names for one or more columns of the table function. If the table
+	function has been defined as returning the <type>record</> data type,
+	an alias, or the keyword <literal>AS</>, must be present, followed by
+	a column definition list in the form ( <replaceable
+	class="PARAMETER">column_name</replaceable> <replaceable
+	class="PARAMETER">data_type</replaceable> [, ... ] ).
+	The column definition list must match the actual number and types
+	of columns returned by the function.
        </para>
       </listitem>
      </varlistentry>
@@ -395,7 +402,7 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
     this was the default result, and adding sub-tables was done
     by appending <command>*</command> to the table name.
     This old behavior is available via the command 
-    <command>SET SQL_Inheritance TO OFF;</command>
+    <command>SET SQL_Inheritance TO OFF</command>.
    </para>
 
    <para>
@@ -406,16 +413,22 @@ where <replaceable class="PARAMETER">from_item</replaceable> can be:
    </para>
 
    <para>
-    A FROM item can be a table function (i.e. a function that returns
-    multiple rows and columns).  When a table function is created, it may
-	be defined to return a named scalar or composite data type (an existing
-	scalar data type, or a table or view name), or it may be defined to return
-	a RECORD data type. When a table function is defined to return RECORD, it
-	must be followed in the FROM clause by an alias, or the keyword AS alone,
-	and then by a parenthesized list of column names and types. This provides
-	a query-time composite type definition. The FROM clause composite type
-	must match the actual composite type returned from the function or an
-	ERROR will be generated.
+    A FROM item can be a table function (typically, a function that returns
+    multiple rows and/or columns, though actually any function can be used).
+    The function is invoked with the given argument value(s), and then its
+    output is scanned as though it were a table.
+   </para>
+
+   <para>
+    In some cases it is useful to define table functions that can return
+    different column sets depending on how they are invoked.  To support this,
+    the table function can be declared as returning the pseudo-type
+    <type>record</>.  When such a function is used in FROM, it must be
+    followed by an alias, or the keyword <literal>AS</> alone,
+    and then by a parenthesized list of column names and types. This provides
+    a query-time composite type definition. The composite type definition
+    must match the actual composite type returned from the function, or an
+    error will be reported at run-time.
    </para>
 
    <para>
@@ -827,6 +840,38 @@ SELECT name FROM distributors ORDER BY code;
     unless ORDER BY is used to constrain the order.
    </para>
   </refsect2>
+
+  <refsect2 id="SQL-FOR-UPDATE">
+   <refsect2info>
+    <date>2002-08-28</date>
+   </refsect2info>
+   <title id="sql-for-update-title">
+    FOR UPDATE Clause
+   </title>
+   <para>
+    <synopsis>
+    FOR UPDATE [ OF <replaceable class="PARAMETER">tablename</replaceable> [, ...] ]
+    </synopsis>
+   </para>
+
+   <para>
+    FOR UPDATE causes the rows retrieved by the query to be locked as though
+    for update.  This prevents them from being modified or deleted by other
+    transactions until the current transaction ends.
+   </para>
+
+   <para>
+    If specific tables are named in FOR UPDATE, then only rows coming from
+    those tables are locked.
+   </para>
+
+   <para>
+    FOR UPDATE cannot be used in contexts where returned rows can't be clearly
+    identified with individual table rows; for example it can't be used with
+    aggregation.
+   </para>
+  </refsect2>
+
  </refsect1>
 
  <refsect1 id="R1-SQL-SELECT-2">
@@ -1019,8 +1064,7 @@ SELECT * FROM distributors_2(111) AS (f1 int, f2 text);
 <productname>PostgreSQL</productname> allows one to omit 
 the <command>FROM</command> clause from a query. This feature
 was retained from the original PostQuel query language.  It has
-a straightforward use to compute the results of simple constant
-expressions:
+a straightforward use to compute the results of simple expressions:
 
   <programlisting>
 SELECT 2+2;
@@ -1062,6 +1106,11 @@ and later will warn if the implicit-FROM feature is used in a query that also
 contains an explicit FROM clause.
 
    </para>
+
+   <para>
+    The table-function feature is a <productname>PostgreSQL</productname>
+    extension.
+   </para>
   </refsect2>
 
   <refsect2 id="R2-SQL-SELECT-5">
diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml
index 13e139ffbaa4fe8106e6682c4df507d2ebcae832..8eed28791a1e80fe1816165b743f5990aadeab4a 100644
--- a/doc/src/sgml/ref/select_into.sgml
+++ b/doc/src/sgml/ref/select_into.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/select_into.sgml,v 1.19 2002/08/28 14:35:37 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/select_into.sgml,v 1.20 2002/08/29 00:17:01 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -29,20 +29,9 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac
     [ HAVING <replaceable class="PARAMETER">condition</replaceable> [, ...] ]
     [ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="PARAMETER">select</replaceable> ]
     [ ORDER BY <replaceable class="PARAMETER">expression</replaceable> [ ASC | DESC | USING <replaceable class="PARAMETER">operator</replaceable> ] [, ...] ]
-    [ LIMIT [ <replaceable class="PARAMETER">start</replaceable> , ] { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
+    [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
     [ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
     [ FOR UPDATE [ OF <replaceable class="PARAMETER">tablename</replaceable> [, ...] ] ]
-
-where <replaceable class="PARAMETER">from_item</replaceable> can be:
-
-[ ONLY ] <replaceable class="PARAMETER">table_name</replaceable> [ * ]
-    [ [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ] ]
-|
-( <replaceable class="PARAMETER">select</replaceable> )
-    [ AS ] <replaceable class="PARAMETER">alias</replaceable> [ ( <replaceable class="PARAMETER">column_alias_list</replaceable> ) ]
-|
-<replaceable class="PARAMETER">from_item</replaceable> [ NATURAL ] <replaceable class="PARAMETER">join_type</replaceable> <replaceable class="PARAMETER">from_item</replaceable>
-    [ ON <replaceable class="PARAMETER">join_condition</replaceable> | USING ( <replaceable class="PARAMETER">join_column_list</replaceable> ) ]
   </synopsis>
   
   <refsect2 id="R2-SQL-SELECTINTO-1">
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index e457504ebefcf4a5352d3dac79ac6525cf7fa45a..60c78d0588fd7c2e2b48c8152c6b490921c74655 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.152 2002/08/27 04:55:07 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.153 2002/08/29 00:17:01 tgl Exp $
 -->
 
 <appendix id="release">
@@ -26,6 +26,7 @@ worries about funny characters.
 <literallayout><![CDATA[
 PREPARE statement allows caching query plans for interactive statements
 Type OPAQUE is now deprecated in favor of pseudo-types cstring, trigger, etc
+Standalone composite types can now be created with CREATE TYPE
 Files larger than 2 GB are now supported (if supported by the operating system)
 SERIAL no longer implies UNIQUE; specify explicitly if index is wanted
 pg_dump -n and -N options have been removed.  The new behavior is like -n but knows about key words.
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index a38305ce0bc64880684ad496d430ec4a9711d15e..b3f653a28a12fc59a1770e23875188a95068c514 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.56 2002/08/23 16:41:37 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.57 2002/08/29 00:17:02 tgl Exp $
 -->
 
  <chapter id="xfunc">
@@ -10,23 +10,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.56 2002/08/23 16:41:37 tgl E
   <sect1 id="xfunc-intro">
    <title>Introduction</title>
 
-  <comment>
-   Historically, functions were perhaps considered a tool for creating
-   types.  Today, few people build their own types but many write
-   their own functions.  This introduction ought to be changed to
-   reflect this.
-  </comment>
-
-  <para>
-   As  it  turns  out,  part of defining a new type is the
-   definition of functions  that  describe  its  behavior.
-   Consequently,  while  it  is  possible  to define a new
-   function without defining a new type,  the  reverse  is
-   not  true.   We therefore describe how to add new functions 
-   to <productname>PostgreSQL</productname> before  describing  
-   how  to  add  new types.
-  </para>
-
   <para>
    <productname>PostgreSQL</productname> provides four kinds of
    functions:
@@ -285,8 +268,6 @@ SELECT name, double_salary(EMP) AS dream
 
     <para>
      It is also possible to build a function that returns a composite type.
-     (However, as we'll see below, there are some
-     unfortunate restrictions on how the function may be used.)
      This is an example of a function 
      that returns a single <type>EMP</type> row:
 
@@ -330,12 +311,12 @@ ERROR:  function declared to return emp returns varchar instead of text at colum
     </para>     
 
     <para>
-     In the present release of <productname>PostgreSQL</productname>
-     there are some unpleasant restrictions on how functions returning
-     composite types can be used.  Briefly, when calling a function that
-     returns a row, we cannot retrieve the entire row.  We must either
+     A function that returns a row (composite type) can be used as a table
+     function, as described below.  It can also be called in the context
+     of an SQL expression, but only when you
      extract a single attribute out of the row or pass the entire row into
-     another function.  (Trying to display the entire row value will yield
+     another function that accepts the same composite type.  (Trying to
+     display the entire row value will yield 
      a meaningless number.)  For example,
 
 <programlisting>
@@ -357,8 +338,8 @@ ERROR:  parser: parse error at or near "."
     </para>
 
     <para>
-     Another approach is to use
-     functional notation for extracting attributes.  The  simple  way 
+     Another option is to use
+     functional notation for extracting an attribute.  The  simple  way 
      to explain this is that we can use the
      notations <literal>attribute(table)</>  and  <literal>table.attribute</>
      interchangeably:
@@ -412,26 +393,73 @@ SELECT getname(new_emp());
    </sect2>
 
    <sect2>
-    <title><acronym>SQL</acronym> Table Functions (Functions Returning Sets)</title>
+    <title><acronym>SQL</acronym> Table Functions</title>
 
     <para>
      A table function is one that may be used in the <command>FROM</command>
-     clause of a query. All SQL Language functions may be used in this manner.
+     clause of a query. All SQL language functions may be used in this manner,
+     but it is particularly useful for functions returning composite types.
      If the function is defined to return a base type, the table function
-     produces a one column result set. If the function is defined to
-     return <literal>SETOF <replaceable>sometype</></literal>, the table
-     function returns multiple rows. To illustrate a SQL table function,
-     consider the following, which returns <literal>SETOF</literal> a
-     composite type:
+     produces a one-column table.  If the function is defined to return
+     a composite type, the table function produces a column for each column
+     of the composite type.
+    </para>
+
+    <para>
+     Here is an example:
 
 <programlisting>
-CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
+CREATE TABLE foo (fooid int, foosubid int, fooname text);
 INSERT INTO foo VALUES(1,1,'Joe');
 INSERT INTO foo VALUES(1,2,'Ed');
 INSERT INTO foo VALUES(2,1,'Mary');
+
+CREATE FUNCTION getfoo(int) RETURNS foo AS '
+    SELECT * FROM foo WHERE fooid = $1;
+' LANGUAGE SQL;
+
+SELECT *, upper(fooname) FROM getfoo(1) AS t1;
+</programlisting>
+
+<screen>
+ fooid | foosubid | fooname | upper
+-------+----------+---------+-------
+     1 |        1 | Joe     | JOE
+(2 rows)
+</screen>
+
+     As the example shows, we can work with the columns of the function's
+     result just the same as if they were columns of a regular table.
+    </para>
+
+    <para>
+     Note that we only got one row out of the function.  This is because
+     we did not say <literal>SETOF</>.
+    </para>
+
+   </sect2>
+
+   <sect2>
+    <title><acronym>SQL</acronym> Functions Returning Sets</title>
+
+    <para>
+     When an SQL function is declared as returning <literal>SETOF</literal>
+     <replaceable>sometype</>, the function's final
+     <command>SELECT</> query is executed to completion, and each row it
+     outputs is returned as an element of the set.
+    </para>
+
+    <para>
+     This feature is normally used by calling the function as a table
+     function.  In this case each row returned by the function becomes
+     a row of the table seen by the query.  For example, assume that
+     table <literal>foo</> has the same contents as above, and we say:
+
+<programlisting>
 CREATE FUNCTION getfoo(int) RETURNS setof foo AS '
     SELECT * FROM foo WHERE fooid = $1;
 ' LANGUAGE SQL;
+
 SELECT * FROM getfoo(1) AS t1;
 </programlisting>
 
@@ -445,14 +473,7 @@ SELECT * FROM getfoo(1) AS t1;
     </para>
 
     <para>
-     When an SQL function is declared as returning <literal>SETOF
-     <replaceable>sometype</></literal>, the function's final
-     <command>SELECT</> query is executed to completion, and each row it
-     outputs is returned as an element of the set.
-    </para>
-
-    <para>
-     Functions returning sets may also currently be called in the target list
+     Currently, functions returning sets may also be called in the target list
      of a <command>SELECT</> query.  For each row that the <command>SELECT</>
      generates by itself, the function returning set is invoked, and an output
      row is generated for each element of the function's result set. Note,
@@ -1346,7 +1367,8 @@ concat_text(PG_FUNCTION_ARGS)
      <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
      guarantees to return a copy of the specified parameter which is
      safe for writing into. (The normal macros will sometimes return a
-     pointer to the value which must not be written to. Using the
+     pointer to a value that is physically stored in a table, and so
+     must not be written to. Using the
      <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>
      macros guarantees a writable result.)
     </para>
@@ -1471,8 +1493,8 @@ LANGUAGE C;
     <title>Table Function API</title>
 
     <para>
-     The Table Function API assists in the creation of a user defined
-     C Language table functions (<xref linkend="xfunc-tablefunctions">).
+     The Table Function API assists in the creation of user-defined
+     C language table functions (<xref linkend="xfunc-tablefunctions">).
      Table functions are functions that produce a set of rows, made up of
      either base (scalar) data types, or composite (multi-column) data types.
      The API is split into two main components: support for returning
@@ -1482,105 +1504,124 @@ LANGUAGE C;
 
     <para>
      The Table Function API relies on macros and functions to suppress most
-     of the complexity of building composite data types and return multiple
-     results.  In addition to the version-1 conventions discussed elsewhere,
-     a table function always requires the following:
+     of the complexity of building composite data types and returning multiple
+     results.  A table function must follow the version-1 calling convention
+     described above.  In addition, the source file must include:
 <programlisting>
 #include "funcapi.h"
 </programlisting>
     </para>
 
+   <sect3>
+    <title>Returning Tuples (Composite Types)</title>
+
     <para>
      The Table Function API support for returning composite data types
      (or tuples) starts with the AttInMetadata struct. This struct holds
      arrays of individual attribute information needed to create a tuple from
-     raw C strings. It also requires a copy of the TupleDesc. The information
+     raw C strings. It also saves a pointer to the TupleDesc. The information
      carried here is derived from the TupleDesc, but it is stored here to
-     avoid redundant cpu cycles on each call to a Table Function.
+     avoid redundant CPU cycles on each call to a Table Function.  In the
+     case of a function returning a set, the AttInMetadata struct should be
+     computed once during the first call and saved for re-use in later calls.
 <programlisting>
-typedef struct
+typedef struct AttInMetadata
 {
-	/* full TupleDesc */
-	TupleDesc	   tupdesc;
-
-	/* pointer to array of attribute "type"in finfo */
-	FmgrInfo	   *attinfuncs;
+    /* full TupleDesc */
+    TupleDesc       tupdesc;
 
-	/* pointer to array of attribute type typelem */
-	Oid			   *attelems;
+    /* array of attribute type input function finfo */
+    FmgrInfo       *attinfuncs;
 
-	/* pointer to array of attribute type typtypmod */
-	int4		   *atttypmods;
+    /* array of attribute type typelem */
+    Oid            *attelems;
 
+    /* array of attribute typmod */
+    int32    	   *atttypmods;
 }	AttInMetadata;
 </programlisting>
      To assist you in populating this struct, several functions and a macro
      are available. Use
 <programlisting>
-TupleDesc RelationNameGetTupleDesc(char *relname)
+TupleDesc RelationNameGetTupleDesc(const char *relname)
 </programlisting>
-     to get a TupleDesc based on the function's return type relation, or
+     to get a TupleDesc based on a specified relation, or
 <programlisting>
 TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
 </programlisting>
-     to get a TupleDesc based on the function's type oid. This can be used to
-     get a TupleDesc for a base (scalar), or composite (relation) type. Then
+     to get a TupleDesc based on a type OID. This can be used to
+     get a TupleDesc for a base (scalar) or composite (relation) type. Then
 <programlisting>
 AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
 </programlisting>
      will return a pointer to an AttInMetadata struct, initialized based on
-     the function's TupleDesc. AttInMetadata is be used in conjunction with
+     the given TupleDesc. AttInMetadata can be used in conjunction with
      C strings to produce a properly formed tuple. The metadata is stored here
-     for use across calls to avoid redundant work.
+     to avoid redundant work across multiple calls.
     </para>
 
     <para>
-     In order to return a tuple you must create a tuple slot based on the
+     To return a tuple you must create a tuple slot based on the
      TupleDesc. You can use
 <programlisting>
 TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
 </programlisting>
      to initialize this tuple slot, or obtain one through other (user provided)
      means. The tuple slot is needed to create a Datum for return by the
-     function.
+     function.  The same slot can (and should) be re-used on each call.
     </para>
 
     <para>
-     If desired,
+     After constructing an AttInMetadata structure,
 <programlisting>
 HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 </programlisting>
      can be used to build a HeapTuple given user data in C string form.
      "values" is an array of C strings, one for each attribute of the return
-     tuple. The C strings should be in the form expected by the "in" function
-     of the attribute data type. For more information on this requirement,
-     see the individual data type "in" functions in the source code
-     (e.g. textin() for data type TEXT). In order to return a NULL value for
+     tuple. Each C string should be in the form expected by the input function
+     of the attribute data type. In order to return a NULL value for
      one of the attributes, the corresponding pointer in the "values" array
-     should be set to NULL.
+     should be set to NULL.  This function will need to be called again
+     for each tuple you return.
     </para>
 
     <para>
-     In order to get an attribute "in" function and typelem value given the
-     typeid, use
-<programlisting>
-void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
-</programlisting>
+     Building a tuple via TupleDescGetAttInMetadata and BuildTupleFromCStrings
+     is only convenient if your function naturally computes the values to
+     be returned as text strings.  If your code naturally computes the
+     values as a set of Datums, you should instead use the underlying
+     heap_formtuple routine to convert the Datums directly into a tuple.
+     You will still need the TupleDesc and a TupleTableSlot, but not
+     AttInMetadata.
     </para>
 
     <para>
-     Finally, in order to return a tuple using the SRF portion of the API
-     (described below), the tuple must be converted into a Datum. Use
+     Once you have built a tuple to return from your function, the tuple must
+     be converted into a Datum. Use
 <programlisting>
 TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
 </programlisting>
-     to get a Datum given a tuple and a slot.
+     to get a Datum given a tuple and a slot.  This Datum can be returned
+     directly if you intend to return just a single row, or it can be used
+     as the current return value in a set-returning function.
+    </para>
+
+    <para>
+     An example appears below.
     </para>
 
+   </sect3>
+
+   <sect3>
+    <title>Returning Sets</title>
+
     <para>
-     The Table Function API support for set returning functions starts with
-     the FuncCallContext struct. This struct holds function context for
-     SRFs using fcinfo->flinfo->fn_extra to hold a pointer to it across calls.
+     A set-returning function (SRF) is normally called once for each item it
+     returns.  The SRF must therefore save enough state to remember what it
+     was doing and return the next item on each call.  The Table Function API
+     provides the FuncCallContext struct to help control this process.
+     <literal>fcinfo-&gt;flinfo-&gt;fn_extra</> is used to
+     hold a pointer to FuncCallContext across calls.
 <programlisting>
 typedef struct
 {
@@ -1639,12 +1680,13 @@ typedef struct
 
 }	FuncCallContext;
 </programlisting>
-     To assist you in populating this struct, several functions and macros
-     are available. Use
+     An SRF uses several functions and macros that automatically manipulate
+     the FuncCallContext struct (and expect to find it via
+     <literal>fn_extra</>).  Use
 <programlisting>
 SRF_IS_FIRSTCALL()
 </programlisting>
-     to determine if your function has been called for the first or a
+     to determine if your function is being called for the first or a
      subsequent time. On the first call (only) use
 <programlisting>
 SRF_FIRSTCALL_INIT()
@@ -1663,8 +1705,9 @@ SRF_PERCALL_SETUP()
 <programlisting>
 SRF_RETURN_NEXT(funcctx, result)
 </programlisting>
-     to send it and prepare for the next call. Finally, when your function
-     is finished returning data, use
+     to return it to the caller.  (The <literal>result</>
+     must be a Datum, either a single value or a tuple prepared as described
+     earlier.)  Finally, when your function is finished returning data, use
 <programlisting>
 SRF_RETURN_DONE(funcctx)
 </programlisting>
@@ -1677,136 +1720,139 @@ SRF_RETURN_DONE(funcctx)
 Datum
 my_Set_Returning_Function(PG_FUNCTION_ARGS)
 {
-	FuncCallContext	   *funcctx;
-	Datum				result;
-
-	[user defined declarations]
-
-	if(SRF_IS_FIRSTCALL())
-	{
-		[user defined code]
-		funcctx = SRF_FIRSTCALL_INIT();
-		[if returning composite]
-			[obtain slot]
-			funcctx->slot = slot;
-		[endif returning composite]
-		[user defined code]
-	}
-	[user defined code]
-	funcctx = SRF_PERCALL_SETUP();
-	[user defined code]
-
-	if (funcctx->call_cntr < funcctx->max_calls)
-	{
-		[user defined code]
-		[obtain result Datum]
-		SRF_RETURN_NEXT(funcctx, result);
-	}
-	else
-	{
-		SRF_RETURN_DONE(funcctx);
-	}
+    FuncCallContext     *funcctx;
+    Datum                result;
+    [user defined declarations]
+
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* one-time setup code appears here: */
+        [user defined code]
+        funcctx = SRF_FIRSTCALL_INIT();
+        [if returning composite]
+            [build TupleDesc, and perhaps AttInMetadata]
+            [obtain slot]
+            funcctx-&gt;slot = slot;
+        [endif returning composite]
+        [user defined code]
+    }
+
+    /* each-time setup code appears here: */
+    [user defined code]
+    funcctx = SRF_PERCALL_SETUP();
+    [user defined code]
+
+    /* this is just one way we might test whether we are done: */
+    if (funcctx-&gt;call_cntr &lt; funcctx-&gt;max_calls)
+    {
+        /* here we want to return another item: */
+        [user defined code]
+        [obtain result Datum]
+        SRF_RETURN_NEXT(funcctx, result);
+    }
+    else
+    {
+        /* here we are done returning items, and just need to clean up: */
+        [user defined code]
+        SRF_RETURN_DONE(funcctx);
+    }
 }
 </programlisting>
     </para>
 
     <para>
-     An example of a simple composite returning SRF looks like:
+     A complete example of a simple SRF returning a composite type looks like:
 <programlisting>
 PG_FUNCTION_INFO_V1(testpassbyval);
 Datum
 testpassbyval(PG_FUNCTION_ARGS)
 {
-	FuncCallContext	   *funcctx;
-	int					call_cntr;
-	int					max_calls;
-	TupleDesc			tupdesc;
-	TupleTableSlot	   *slot;
-	AttInMetadata	   *attinmeta;
+    FuncCallContext     *funcctx;
+    int                  call_cntr;
+    int                  max_calls;
+    TupleDesc            tupdesc;
+    TupleTableSlot       *slot;
+    AttInMetadata       *attinmeta;
+
+     /* stuff done only on the first call of the function */
+     if (SRF_IS_FIRSTCALL())
+     {
+        /* create a function context for cross-call persistence */
+         funcctx = SRF_FIRSTCALL_INIT();
+
+        /* total number of tuples to be returned */
+        funcctx-&gt;max_calls = PG_GETARG_UINT32(0);
 
-	/* stuff done only on the first call of the function */
- 	if(SRF_IS_FIRSTCALL())
- 	{
-		/* create a function context for cross-call persistence */
- 		funcctx = SRF_FIRSTCALL_INIT();
-
-		/* total number of tuples to be returned */
-		funcctx->max_calls = PG_GETARG_UINT32(0);
-
-		/*
-		 * Build a tuple description for a __testpassbyval tuple
-		 */
-		tupdesc = RelationNameGetTupleDesc("__testpassbyval");
+        /*
+         * Build a tuple description for a __testpassbyval tuple
+         */
+        tupdesc = RelationNameGetTupleDesc("__testpassbyval");
 
-		/* allocate a slot for a tuple with this tupdesc */
-		slot = TupleDescGetSlot(tupdesc);
+        /* allocate a slot for a tuple with this tupdesc */
+        slot = TupleDescGetSlot(tupdesc);
 
-		/* assign slot to function context */
-		funcctx->slot = slot;
+        /* assign slot to function context */
+        funcctx-&gt;slot = slot;
 
-		/*
-		 * Generate attribute metadata needed later to produce tuples from raw
-		 * C strings
-		 */
-		attinmeta = TupleDescGetAttInMetadata(tupdesc);
-		funcctx->attinmeta = attinmeta;
+        /*
+         * Generate attribute metadata needed later to produce tuples from raw
+         * C strings
+         */
+        attinmeta = TupleDescGetAttInMetadata(tupdesc);
+        funcctx-&gt;attinmeta = attinmeta;
     }
 
-	/* stuff done on every call of the function */
- 	funcctx = SRF_PERCALL_SETUP();
+    /* stuff done on every call of the function */
+    funcctx = SRF_PERCALL_SETUP();
 
-	call_cntr = funcctx->call_cntr;
-	max_calls = funcctx->max_calls;
-	slot = funcctx->slot;
-	attinmeta = funcctx->attinmeta;
+    call_cntr = funcctx-&gt;call_cntr;
+    max_calls = funcctx-&gt;max_calls;
+    slot = funcctx-&gt;slot;
+    attinmeta = funcctx-&gt;attinmeta;
  
- 	if (call_cntr < max_calls)	/* do when there is more left to send */
- 	{
-		char	   **values;
-		HeapTuple	tuple;
-		Datum		result;
-
-		/*
-		 * Prepare a values array for storage in our slot.
-		 * This should be an array of C strings which will
-		 * be processed later by the appropriate "in" functions.
-		 */
-		values = (char **) palloc(3 * sizeof(char *));
-		values[0] = (char *) palloc(16 * sizeof(char));
-		values[1] = (char *) palloc(16 * sizeof(char));
-		values[2] = (char *) palloc(16 * sizeof(char));
-
-		snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
-		snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
-		snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
-
-		/* build a tuple */
-		tuple = BuildTupleFromCStrings(attinmeta, values);
-
-		/* make the tuple into a datum */
-		result = TupleGetDatum(slot, tuple);
-
-		/* Clean up */
-		pfree(values[0]);
-		pfree(values[1]);
-		pfree(values[2]);
-		pfree(values);
-
- 		SRF_RETURN_NEXT(funcctx, result);
- 	}
- 	else	/* do when there is no more left */
- 	{
- 		SRF_RETURN_DONE(funcctx);
- 	}
+    if (call_cntr &lt; max_calls)    /* do when there is more left to send */
+    {
+        char       **values;
+        HeapTuple    tuple;
+        Datum        result;
+
+        /*
+         * Prepare a values array for storage in our slot.
+         * This should be an array of C strings which will
+         * be processed later by the appropriate "in" functions.
+         */
+        values = (char **) palloc(3 * sizeof(char *));
+        values[0] = (char *) palloc(16 * sizeof(char));
+        values[1] = (char *) palloc(16 * sizeof(char));
+        values[2] = (char *) palloc(16 * sizeof(char));
+
+        snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
+        snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
+        snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
+
+        /* build a tuple */
+        tuple = BuildTupleFromCStrings(attinmeta, values);
+
+        /* make the tuple into a datum */
+        result = TupleGetDatum(slot, tuple);
+
+        /* Clean up */
+        pfree(values[0]);
+        pfree(values[1]);
+        pfree(values[2]);
+        pfree(values);
+
+         SRF_RETURN_NEXT(funcctx, result);
+    }
+    else    /* do when there is no more left */
+    {
+         SRF_RETURN_DONE(funcctx);
+    }
 }
 </programlisting>
      with supporting SQL code of
 <programlisting>
-CREATE VIEW __testpassbyval AS
-  SELECT
-    0::INT4 AS f1,
-    0::INT4 AS f2,
-    0::INT4 AS f3;
+CREATE TYPE __testpassbyval AS (f1 int4, f2 int4, f3 int4);
 
 CREATE OR REPLACE FUNCTION testpassbyval(int4, int4) RETURNS setof __testpassbyval
   AS 'MODULE_PATHNAME','testpassbyval' LANGUAGE 'c' IMMUTABLE STRICT;
@@ -1816,6 +1862,9 @@ CREATE OR REPLACE FUNCTION testpassbyval(int4, int4) RETURNS setof __testpassbyv
     <para>
      See contrib/tablefunc for more examples of Table Functions.
     </para>
+
+   </sect3>
+
    </sect2>
 
    <sect2>
@@ -2031,23 +2080,23 @@ CREATE FUNCTION test(int, int) RETURNS int
     Table functions work wherever tables do in <literal>SELECT</> statements.
     For example
 <programlisting>
-CREATE TABLE foo (fooid int, foosubid int, fooname text, primary key(fooid,foosubid));
-CREATE FUNCTION getfoo(int) RETURNS foo AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+CREATE TABLE foo (fooid int, foosubid int, fooname text);
+
+CREATE FUNCTION getfoo(int) RETURNS setof foo AS '
+    SELECT * FROM foo WHERE fooid = $1;
+' LANGUAGE SQL;
+
 SELECT * FROM getfoo(1) AS t1;
-SELECT * FROM foo where foosubid in (select foosubid from getfoo(foo.fooid) z where z.fooid = foo.fooid);
+
+SELECT * FROM foo
+WHERE foosubid in (select foosubid from getfoo(foo.fooid) z
+                   where z.fooid = foo.fooid);
+
 CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
 SELECT * FROM vw_getfoo;
 </programlisting>
     are all valid statements.
    </para>
-
-   <para>
-    Currently, table functions are supported as SQL language functions
-    (<xref linkend="xfunc-sql">) and C language functions
-    (<xref linkend="xfunc-c">). See these individual sections for more
-    details.
-   </para>
-
   </sect1>
 
   <sect1 id="xfunc-plhandler">
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 0e1765fc09ea9777bde238f231938eb125a2b968..8637c72b492aedd74ff67075ce9d16715a8d3d1e 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.85 2002/08/05 02:30:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.86 2002/08/29 00:17:02 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -570,7 +570,7 @@ BuildDescForRelation(List *schema)
  * Given a (possibly qualified) relation name, build a TupleDesc.
  */
 TupleDesc
-RelationNameGetTupleDesc(char *relname)
+RelationNameGetTupleDesc(const char *relname)
 {
 	RangeVar   *relvar;
 	Relation	rel;
@@ -580,7 +580,7 @@ RelationNameGetTupleDesc(char *relname)
 	/* Open relation and get the tuple description */
 	relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
 	relvar = makeRangeVarFromNameList(relname_list);
-	rel = heap_openrv(relvar, AccessShareLock);
+	rel = relation_openrv(relvar, AccessShareLock);
 	tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
 	relation_close(rel, AccessShareLock);
 
@@ -611,50 +611,46 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 	{
 		/* Composite data type, i.e. a table's row type */
 		Oid			relid = typeidTypeRelid(typeoid);
+		Relation	rel;
+		int			natts;
 
-		if (OidIsValid(relid))
-		{
-			Relation	rel;
-			int			natts;
+		if (!OidIsValid(relid))
+			elog(ERROR, "Invalid typrelid for complex type %u", typeoid);
 
-			rel = relation_open(relid, AccessShareLock);
-			tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
-			natts = tupdesc->natts;
-			relation_close(rel, AccessShareLock);
+		rel = relation_open(relid, AccessShareLock);
+		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+		natts = tupdesc->natts;
+		relation_close(rel, AccessShareLock);
+		/* XXX should we hold the lock to ensure table doesn't change? */
 
-			/* check to see if we've given column aliases */
-			if(colaliases != NIL)
-			{
-				char	   *label;
-				int			varattno;
+		if (colaliases != NIL)
+		{
+			int			varattno;
 
-				/* does the List length match the number of attributes */
-				if (length(colaliases) != natts)
-					elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
+			/* does the list length match the number of attributes? */
+			if (length(colaliases) != natts)
+				elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
 
-				/* OK, use the aliases instead */
-				for (varattno = 0; varattno < natts; varattno++)
-				{
-					label = strVal(nth(varattno, colaliases));
+			/* OK, use the aliases instead */
+			for (varattno = 0; varattno < natts; varattno++)
+			{
+				char	   *label = strVal(nth(varattno, colaliases));
 
-					if (label != NULL)
-						namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
-				}
+				if (label != NULL)
+					namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
 			}
 		}
-		else
-			elog(ERROR, "Invalid return relation specified for function");
 	}
 	else if (functyptype == 'b' || functyptype == 'd')
 	{
 		/* Must be a base data type, i.e. scalar */
 		char	   *attname;
 
-		/* the alias List is required for base types */
+		/* the alias list is required for base types */
 		if (colaliases == NIL)
 			elog(ERROR, "TypeGetTupleDesc: no column alias was provided");
 
-		/* the alias List length must be 1 */
+		/* the alias list length must be 1 */
 		if (length(colaliases) != 1)
 			elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
 
@@ -671,8 +667,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 						   false);
 	}
 	else if (functyptype == 'p' && typeoid == RECORDOID)
-		elog(ERROR, "Unable to determine tuple description for function"
-						" returning \"record\"");
+		elog(ERROR, "Unable to determine tuple description for function returning \"record\"");
 	else
 		elog(ERROR, "Unknown kind of return type specified for function");
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 325dff88dbcfc6e4a76f30316ba644f17cfe9cf7..89e0fec0ed5eed215eda1f1d00efe2674b8a5bc7 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.145 2002/08/13 20:11:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.146 2002/08/29 00:17:02 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -607,6 +607,9 @@ heap_open(Oid relationId, LOCKMODE lockmode)
 	else if (r->rd_rel->relkind == RELKIND_SPECIAL)
 		elog(ERROR, "%s is a special relation",
 			 RelationGetRelationName(r));
+	else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+		elog(ERROR, "%s is a composite type",
+			 RelationGetRelationName(r));
 
 	pgstat_initstats(&r->pgstat_info, r);
 
@@ -633,6 +636,9 @@ heap_openrv(const RangeVar *relation, LOCKMODE lockmode)
 	else if (r->rd_rel->relkind == RELKIND_SPECIAL)
 		elog(ERROR, "%s is a special relation",
 			 RelationGetRelationName(r));
+	else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+		elog(ERROR, "%s is a composite type",
+			 RelationGetRelationName(r));
 
 	pgstat_initstats(&r->pgstat_info, r);
 
@@ -659,6 +665,9 @@ heap_openr(const char *sysRelationName, LOCKMODE lockmode)
 	else if (r->rd_rel->relkind == RELKIND_SPECIAL)
 		elog(ERROR, "%s is a special relation",
 			 RelationGetRelationName(r));
+	else if (r->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+		elog(ERROR, "%s is a composite type",
+			 RelationGetRelationName(r));
 
 	pgstat_initstats(&r->pgstat_info, r);
 
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0c21400ca1db0face1a80d63f3c7ed6b030c2835..c768ca2d55475a2348456f7513527219a471aad4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.221 2002/08/15 16:36:00 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.222 2002/08/29 00:17:02 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -69,6 +69,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 static void AddNewRelationType(const char *typeName,
 							   Oid typeNamespace,
 							   Oid new_rel_oid,
+							   char new_rel_kind,
 							   Oid new_type_oid);
 static void RelationRemoveInheritance(Relation relation);
 static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
@@ -357,7 +358,7 @@ CheckAttributeNames(TupleDesc tupdesc, bool relhasoids, char relkind)
 	/*
 	 * first check for collision with system attribute names
 	 *
-	 * Skip this for a view and type relation, since it doesn't have system
+	 * Skip this for a view or type relation, since those don't have system
 	 * attributes.
 	 */
 	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
@@ -618,6 +619,7 @@ static void
 AddNewRelationType(const char *typeName,
 				   Oid typeNamespace,
 				   Oid new_rel_oid,
+				   char new_rel_kind,
 				   Oid new_type_oid)
 {
 	/*
@@ -633,6 +635,7 @@ AddNewRelationType(const char *typeName,
 			   typeNamespace,	/* type namespace */
 			   new_type_oid,	/* preassigned oid for type */
 			   new_rel_oid,		/* relation oid */
+			   new_rel_kind,	/* relation kind */
 			   sizeof(Oid),		/* internal size */
 			   'c',				/* type-type (complex) */
 			   ',',				/* default array delimiter */
@@ -728,7 +731,11 @@ heap_create_with_catalog(const char *relname,
 	 * NOTE: we could get a unique-index failure here, in case the same name
 	 * has already been used for a type.
 	 */
-	AddNewRelationType(relname, relnamespace, new_rel_oid, new_type_oid);
+	AddNewRelationType(relname,
+					   relnamespace,
+					   new_rel_oid,
+					   relkind,
+					   new_type_oid);
 
 	/*
 	 * now add tuples to pg_attribute for the attributes in our new
@@ -904,7 +911,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 	 * did this ... but when cascading from a drop of some other object,
 	 * we may not have any lock.)
 	 */
-	rel = heap_open(relid, AccessExclusiveLock);
+	rel = relation_open(relid, AccessExclusiveLock);
 
 	attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
 
@@ -943,7 +950,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 
 	heap_close(attr_rel, RowExclusiveLock);
 
-	heap_close(rel, NoLock);
+	relation_close(rel, NoLock);
 }
 
 /*
@@ -1036,7 +1043,7 @@ RemoveAttrDefaultById(Oid attrdefId)
 	myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
 
 	/* Get an exclusive lock on the relation owning the attribute */
-	myrel = heap_open(myrelid, AccessExclusiveLock);
+	myrel = relation_open(myrelid, AccessExclusiveLock);
 
 	/* Now we can delete the pg_attrdef row */
 	simple_heap_delete(attrdef_rel, &tuple->t_self);
@@ -1069,7 +1076,7 @@ RemoveAttrDefaultById(Oid attrdefId)
 	heap_close(attr_rel, RowExclusiveLock);
 
 	/* Keep lock on attribute's rel until end of xact */
-	heap_close(myrel, NoLock);
+	relation_close(myrel, NoLock);
 }
 
 /* ----------------------------------------------------------------
@@ -1099,7 +1106,7 @@ heap_drop_with_catalog(Oid rid)
 	/*
 	 * Open and lock the relation.
 	 */
-	rel = heap_open(rid, AccessExclusiveLock);
+	rel = relation_open(rid, AccessExclusiveLock);
 
 	/*
 	 * Release all buffers that belong to this relation, after writing any
@@ -1134,7 +1141,7 @@ heap_drop_with_catalog(Oid rid)
 	 * unlink the relation's physical file and finish up.
 	 */
 	if (rel->rd_rel->relkind != RELKIND_VIEW &&
-			rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+		rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
 		smgrunlink(DEFAULT_SMGR, rel);
 
 	/*
@@ -1142,7 +1149,7 @@ heap_drop_with_catalog(Oid rid)
 	 * relation until transaction commit.  This ensures no one else will
 	 * try to do something with the doomed relation.
 	 */
-	heap_close(rel, NoLock);
+	relation_close(rel, NoLock);
 
 	/*
 	 * flush the relation from the relcache
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 8f6caa5d4d9e688136f7658e8e74ee4420165095..33fc901e86717a5430eb704acadad34c492c98bc 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.31 2002/08/15 16:36:01 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.32 2002/08/29 00:17:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1585,7 +1585,6 @@ RemoveTempRelations(Oid tempNamespaceId)
 			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);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1bdb3796e6670cdd04fde0ebb269dcab356f7089..e8608925bf51844b3c4bafcb34d61ec071bd83b7 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.90 2002/08/23 16:41:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.91 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -400,7 +400,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
 		 * attributes to ensure that they match the datatypes of the
 		 * non-resjunk columns.
 		 */
-		reln = heap_open(typerelid, AccessShareLock);
+		reln = relation_open(typerelid, AccessShareLock);
 		relnatts = reln->rd_rel->relnatts;
 		rellogcols = 0;				/* we'll count nondeleted cols as we go */
 		colindex = 0;
@@ -447,7 +447,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
 			elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
 				 format_type_be(rettype), rellogcols);
 
-		heap_close(reln, AccessShareLock);
+		relation_close(reln, AccessShareLock);
 	}
 	else if (fn_typtype == 'p' && rettype == RECORDOID)
 	{
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 6c6a135a0b9a0e567d0219f6fbbd5576d39f5bfc..ec0704b78e60e89c1d4920419800a0e2a5805657 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.79 2002/08/24 15:00:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.80 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -129,7 +129,8 @@ Oid
 TypeCreate(const char *typeName,
 		   Oid typeNamespace,
 		   Oid assignedTypeOid,
-		   Oid relationOid,			/* only for 'c'atalog typeTypes */
+		   Oid relationOid,			/* only for 'c'atalog typeType */
+		   char relationKind,		/* ditto */
 		   int16 internalSize,
 		   char typeType,
 		   char typDelim,
@@ -332,15 +333,11 @@ TypeCreate(const char *typeName,
 		 */
 		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)
+			if (relationKind != RELKIND_COMPOSITE_TYPE)
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 			else
 				recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index ce489280182a10ea4af5cc342b3c4e9a01cbb48d..ea04fd687dc08f6a22e828ceae2842d0a8b6d182 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1996-2001, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.57 2002/08/22 00:01:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.58 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -362,7 +362,7 @@ CommentAttribute(List *qualname, char *comment)
 
 	/* Open the containing relation to ensure it won't go away meanwhile */
 	rel = makeRangeVarFromNameList(relname);
-	relation = heap_openrv(rel, AccessShareLock);
+	relation = relation_openrv(rel, AccessShareLock);
 
 	/* Check object security */
 
@@ -383,7 +383,7 @@ CommentAttribute(List *qualname, char *comment)
 
 	/* Done, but hold lock until commit */
 
-	heap_close(relation, NoLock);
+	relation_close(relation, NoLock);
 }
 
 /*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c0b40c6e1431d34e41f41da090ccc131f9338746..b9eecf4b8be1f87fcbd657075aa36847393ae912 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.167 2002/08/24 15:00:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.168 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -399,9 +399,6 @@ DoCopy(const CopyStmt *stmt)
 			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));
@@ -447,9 +444,6 @@ DoCopy(const CopyStmt *stmt)
 			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",
 					 RelationGetRelationName(rel));
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 13cf272525c85f79f68e039300fadb4af3def302..efae790dd8ce257141a77a2bfb978146d9d91f7d 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.84 2002/07/20 15:12:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.85 2002/08/29 00:17:03 tgl Exp $
  *
  */
 
@@ -79,7 +79,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
 	if (query->commandType == CMD_UTILITY)
 	{
 		/* rewriter will not cope with utility statements */
-		PROJECT_LINE_OF_TEXT(tstate, "Utility statements have no plan structure");
+		do_text_output_oneline(tstate, "Utility statements have no plan structure");
 	}
 	else
 	{
@@ -89,7 +89,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
 		if (rewritten == NIL)
 		{
 			/* In the case of an INSTEAD NOTHING, tell at least that */
-			PROJECT_LINE_OF_TEXT(tstate, "Query rewrites to nothing");
+			do_text_output_oneline(tstate, "Query rewrites to nothing");
 		}
 		else
 		{
@@ -99,7 +99,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
 				ExplainOneQuery(lfirst(l), stmt, tstate);
 				/* put a blank line between plans */
 				if (lnext(l) != NIL)
-					PROJECT_LINE_OF_TEXT(tstate, "");
+					do_text_output_oneline(tstate, "");
 			}
 		}
 	}
@@ -122,9 +122,9 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 	if (query->commandType == CMD_UTILITY)
 	{
 		if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
-			PROJECT_LINE_OF_TEXT(tstate, "NOTIFY");
+			do_text_output_oneline(tstate, "NOTIFY");
 		else
-			PROJECT_LINE_OF_TEXT(tstate, "UTILITY");
+			do_text_output_oneline(tstate, "UTILITY");
 		return;
 	}
 
@@ -189,7 +189,7 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 			do_text_output_multiline(tstate, f);
 			pfree(f);
 			if (es->printCost)
-				PROJECT_LINE_OF_TEXT(tstate, "");	/* separator line */
+				do_text_output_oneline(tstate, "");	/* separator line */
 		}
 	}
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 81f1c4cfb7231ccc07f432d875309d7a94c1d0c9..04b0266dbc572c0041497754b6213b33229e2d9f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.35 2002/08/28 20:18:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.36 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -228,7 +228,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	 * see the new rel anyway until we commit), but it keeps the lock
 	 * manager from complaining about deadlock risks.
 	 */
-	rel = heap_open(relationId, AccessExclusiveLock);
+	rel = relation_open(relationId, AccessExclusiveLock);
 
 	/*
 	 * Now add any newly specified column default values and CHECK
@@ -293,7 +293,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	 * Clean up.  We keep lock on new relation (although it shouldn't be
 	 * visible to anyone else anyway, until commit).
 	 */
-	heap_close(rel, NoLock);
+	relation_close(rel, NoLock);
 
 	return relationId;
 }
@@ -1064,7 +1064,7 @@ renameatt(Oid relid,
 	 * Grab an exclusive lock on the target table, which we will NOT
 	 * release until end of transaction.
 	 */
-	targetrelation = heap_open(relid, AccessExclusiveLock);
+	targetrelation = relation_open(relid, AccessExclusiveLock);
 
 	/*
 	 * permissions checking.  this would normally be done in utility.c,
@@ -1210,7 +1210,7 @@ renameatt(Oid relid,
 							   true, false);
 	}
 
-	heap_close(targetrelation, NoLock); /* close rel but keep lock! */
+	relation_close(targetrelation, NoLock); /* close rel but keep lock! */
 }
 
 /*
@@ -3247,13 +3247,12 @@ CheckTupleType(Form_pg_class tuple_class)
 		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",
+			elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
 				 NameStr(tuple_class->relname));
 	}
 }
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 02e5a686e7758cba5bcd44a16dd09583c1bef044..bb1250b3572d3381b658c73431cda8ed6d000a83 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.11 2002/08/23 16:41:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.12 2002/08/29 00:17:03 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -233,6 +233,7 @@ DefineType(List *names, List *parameters)
 				   typeNamespace,	/* namespace */
 				   InvalidOid,		/* preassigned type oid (not done here) */
 				   InvalidOid,		/* relation oid (n/a here) */
+				   0,				/* relation kind (ditto) */
 				   internalLength,	/* internal size */
 				   'b',				/* type-type (base type) */
 				   delimiter,		/* array element delimiter */
@@ -262,6 +263,7 @@ DefineType(List *names, List *parameters)
 			   typeNamespace,	/* namespace */
 			   InvalidOid,		/* preassigned type oid (not done here) */
 			   InvalidOid,		/* relation oid (n/a here) */
+			   0,				/* relation kind (ditto) */
 			   -1,				/* internal size */
 			   'b',				/* type-type (base type) */
 			   DEFAULT_TYPDELIM,	/* array element delimiter */
@@ -562,6 +564,7 @@ DefineDomain(CreateDomainStmt *stmt)
 				   domainNamespace,		/* namespace */
 				   InvalidOid,			/* preassigned type oid (none here) */
 				   InvalidOid,			/* relation oid (n/a here) */
+				   0,					/* relation kind (ditto) */
 				   internalLength,		/* internal size */
 				   'd',					/* type-type (domain type) */
 				   delimiter,			/* array element delimiter */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 7a6e1c69956aa8b654735dbee2c58646dae1e1fc..6318d79d4eb916331d02bedec1dcd23b0b83a0fc 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.175 2002/08/28 20:46:22 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.176 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -786,10 +786,6 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
 			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));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 06a784ce26d1f92d39395473ab064542ad46fb8f..afadcd3137b028ef74fb2ea4090b3c8ab6d083d6 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.56 2002/07/20 05:49:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.57 2002/08/29 00:17:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,8 +109,9 @@
 
 #include "funcapi.h"
 #include "access/heapam.h"
-#include "catalog/pg_type.h"
 #include "executor/executor.h"
+#include "utils/lsyscache.h"
+
 
 /* ----------------------------------------------------------------
  *				  tuple table create/delete functions
@@ -676,8 +677,7 @@ ExecTypeFromTL(List *targetList, hasoid_t withoid)
 }
 
 /*
- * TupleDescGetSlot - Initialize a slot based on the supplied
- * tupledesc
+ * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
  */
 TupleTableSlot *
 TupleDescGetSlot(TupleDesc tupdesc)
@@ -695,40 +695,36 @@ TupleDescGetSlot(TupleDesc tupdesc)
 }
 
 /*
- * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the
+ * TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
  * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
  * to produce a properly formed tuple.
  */
 AttInMetadata *
 TupleDescGetAttInMetadata(TupleDesc tupdesc)
 {
-	int				natts;
+	int				natts = tupdesc->natts;
 	int				i;
 	Oid				atttypeid;
 	Oid				attinfuncid;
-	Oid				attelem;
 	FmgrInfo	   *attinfuncinfo;
 	Oid			   *attelems;
-	int4		   *atttypmods;
+	int32		   *atttypmods;
 	AttInMetadata  *attinmeta;
 
 	attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
-	natts = tupdesc->natts;
 
 	/*
 	 * Gather info needed later to call the "in" function for each attribute
 	 */
 	attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
 	attelems = (Oid *) palloc(natts * sizeof(Oid));
-	atttypmods = (int4 *) palloc(natts * sizeof(int4));
+	atttypmods = (int32 *) palloc(natts * sizeof(int32));
 
 	for (i = 0; i < natts; i++)
 	{
 		atttypeid = tupdesc->attrs[i]->atttypid;
-		get_type_metadata(atttypeid, &attinfuncid, &attelem);
-
+		getTypeInputInfo(atttypeid, &attinfuncid, &attelems[i]);
 		fmgr_info(attinfuncid, &attinfuncinfo[i]);
-		attelems[i] = attelem;
 		atttypmods[i] = tupdesc->attrs[i]->atttypmod;
 	}
 	attinmeta->tupdesc = tupdesc;
@@ -746,39 +742,35 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
 HeapTuple
 BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 {
-	TupleDesc			tupdesc;
-	int					natts;
-	HeapTuple			tuple;
+	TupleDesc			tupdesc = attinmeta->tupdesc;
+	int					natts = tupdesc->natts;
+	Datum			   *dvalues;
 	char			   *nulls;
 	int					i;
-	Datum			   *dvalues;
-	FmgrInfo			attinfuncinfo;
 	Oid					attelem;
-	int4				atttypmod;
-
-	tupdesc = attinmeta->tupdesc;
-	natts = tupdesc->natts;
+	int32				atttypmod;
+	HeapTuple			tuple;
 
 	dvalues = (Datum *) palloc(natts * sizeof(Datum));
 	nulls = (char *) palloc(natts * sizeof(char));
 
-	/* Call the "in" function for each attribute */
+	/* Call the "in" function for each non-null attribute */
 	for (i = 0; i < natts; i++)
 	{
 		if (values[i] != NULL)
 		{
-			attinfuncinfo = attinmeta->attinfuncs[i];
 			attelem = attinmeta->attelems[i];
 			atttypmod = attinmeta->atttypmods[i];
 
-			dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
-										ObjectIdGetDatum(attelem),
-										Int32GetDatum(atttypmod));
+			dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i],
+									   CStringGetDatum(values[i]),
+									   ObjectIdGetDatum(attelem),
+									   Int32GetDatum(atttypmod));
 			nulls[i] = ' ';
 		}
 		else
 		{
-			dvalues[i] = PointerGetDatum(NULL);
+			dvalues[i] = (Datum) 0;
 			nulls[i] = 'n';
 		}
 	}
@@ -788,6 +780,13 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 	 */
 	tuple = heap_formtuple(tupdesc, dvalues, nulls);
 
+	/*
+	 * Release locally palloc'd space.  XXX would probably be good to
+	 * pfree values of pass-by-reference datums, as well.
+	 */
+	pfree(dvalues);
+	pfree(nulls);
+
 	return tuple;
 }
 
@@ -804,7 +803,7 @@ begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc)
 
 	tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
 
-	tstate->tupdesc = tupdesc;
+	tstate->metadata = TupleDescGetAttInMetadata(tupdesc);
 	tstate->destfunc = DestToFunction(dest);
 
 	(*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
@@ -823,20 +822,22 @@ void
 do_tup_output(TupOutputState *tstate, char **values)
 {
 	/* build a tuple from the input strings using the tupdesc */
-	AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tstate->tupdesc);
-	HeapTuple	tuple = BuildTupleFromCStrings(attinmeta, values);
+	HeapTuple	tuple = BuildTupleFromCStrings(tstate->metadata, values);
 
 	/* send the tuple to the receiver */
 	(*tstate->destfunc->receiveTuple) (tuple,
-									   tstate->tupdesc,
+									   tstate->metadata->tupdesc,
 									   tstate->destfunc);
 	/* clean up */
 	heap_freetuple(tuple);
 }
 
-/* write a chunk of text, breaking at newline characters
+/*
+ * write a chunk of text, breaking at newline characters
+ *
  * NB: scribbles on its input!
- * Should only be used for a single TEXT attribute tupdesc.
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
  */
 void
 do_text_output_multiline(TupOutputState *tstate, char *text)
@@ -859,5 +860,6 @@ void
 end_tup_output(TupOutputState *tstate)
 {
 	(*tstate->destfunc->cleanup) (tstate->destfunc);
+	/* XXX worth cleaning up the attinmetadata? */
 	pfree(tstate);
 }
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index eba919b8b66f8c650dbf11d63375dddb8693e621..fe473404b915c02cefaa1d04b2a309946f588f48 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.55 2002/08/23 16:41:37 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.56 2002/08/29 00:17:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -193,9 +193,10 @@ init_sql_fcache(FmgrInfo *finfo)
 	 */
 	fcache->typlen = typeStruct->typlen;
 
-	if (typeStruct->typtype != 'c')
+	if (typeStruct->typtype != 'c' &&
+		procedureStruct->prorettype != RECORDOID)
 	{
-		/* The return type is not a relation, so just use byval */
+		/* The return type is not a composite type, so just use byval */
 		fcache->typbyval = typeStruct->typbyval;
 		fcache->returnsTuple = false;
 	}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index b8aecc2b82511c2ea13e55aec67af080e05fb7be..381b6047bf0729b022cd583d385544b50abac2df 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.5 2002/08/05 02:30:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.6 2002/08/29 00:17:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,10 +130,10 @@ FunctionNext(FunctionScan *node)
 /* ----------------------------------------------------------------
  *		ExecFunctionScan(node)
  *
- *		Scans the Function sequentially and returns the next qualifying
+ *		Scans the function sequentially and returns the next qualifying
  *		tuple.
  *		It calls the ExecScan() routine and passes it the access method
- *		which retrieve tuples sequentially.
+ *		which retrieves tuples sequentially.
  *
  */
 
@@ -156,7 +156,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 	FunctionScanState  *scanstate;
 	RangeTblEntry	   *rte;
 	Oid					funcrettype;
-	Oid					funcrelid;
 	char				functyptype;
 	TupleDesc			tupdesc = NULL;
 
@@ -201,31 +200,26 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 
 	/*
 	 * Now determine if the function returns a simple or composite type,
-	 * and check/add column aliases.
+	 * and build an appropriate tupdesc.
 	 */
 	functyptype = get_typtype(funcrettype);
 
-	/*
-	 * Build a suitable tupledesc representing the output rows
-	 */
 	if (functyptype == 'c')
 	{
-		funcrelid = typeidTypeRelid(funcrettype);
-		if (OidIsValid(funcrelid))
-		{
-			/*
-			 * Composite data type, i.e. a table's row type
-			 * Same as ordinary relation RTE
-			 */
-			Relation	rel;
+		/*
+		 * Composite data type, i.e. a table's row type
+		 */
+		Oid			funcrelid;
+		Relation	rel;
 
-			rel = relation_open(funcrelid, AccessShareLock);
-			tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
-			relation_close(rel, AccessShareLock);
-			scanstate->returnsTuple = true;
-		}
-		else
-			elog(ERROR, "Invalid return relation specified for function");
+		funcrelid = typeidTypeRelid(funcrettype);
+		if (!OidIsValid(funcrelid))
+			elog(ERROR, "Invalid typrelid for complex type %u",
+				 funcrettype);
+		rel = relation_open(funcrelid, AccessShareLock);
+		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+		relation_close(rel, AccessShareLock);
+		scanstate->returnsTuple = true;
 	}
 	else if (functyptype == 'b' || functyptype == 'd')
 	{
@@ -461,8 +455,7 @@ function_getonetuple(FunctionScanState *scanstate,
 			 */
 			if (fn_typtype == 'p' && fn_typeid == RECORDOID)
 				if (tupledesc_mismatch(tupdesc, slot->ttc_tupleDescriptor))
-					elog(ERROR, "Query-specified return tuple and actual"
-									" function return tuple do not match");
+					elog(ERROR, "Query-specified return tuple and actual function return tuple do not match");
 		}
 		else
 		{
@@ -480,7 +473,7 @@ function_getonetuple(FunctionScanState *scanstate,
 								  slot,				/* slot to store in */
 								  InvalidBuffer,	/* buffer associated with
 													 * this tuple */
-								  true);			/* pfree this pointer */
+								  true);			/* pfree this tuple */
 		}
 	}
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3a407753392029a5091eb679fe1ea3adcfb38af1..ca88b520f96212d5caa53eb8a35daa27000d37ba 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.169 2002/08/26 17:53:58 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.170 2002/08/29 00:17:04 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -145,7 +145,7 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
 static void
 _outNotifyStmt(StringInfo str, NotifyStmt *node)
 {
-	appendStringInfo(str, "NOTIFY :relation ");
+	appendStringInfo(str, " NOTIFY :relation ");
 	_outNode(str, node->relation);
 }
 
@@ -153,14 +153,14 @@ static void
 _outSelectStmt(StringInfo str, SelectStmt *node)
 {
 	/* XXX this is pretty durn incomplete */
-	appendStringInfo(str, "SELECT :where ");
+	appendStringInfo(str, " SELECT :where ");
 	_outNode(str, node->whereClause);
 }
 
 static void
 _outFuncCall(StringInfo str, FuncCall *node)
 {
-	appendStringInfo(str, "FUNCTION ");
+	appendStringInfo(str, " FUNCCALL ");
 	_outNode(str, node->funcname);
 	appendStringInfo(str, " :args ");
 	_outNode(str, node->args);
@@ -1006,7 +1006,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 		case RTE_FUNCTION:
 			appendStringInfo(str, ":funcexpr ");
 			_outNode(str, node->funcexpr);
-			appendStringInfo(str, ":coldeflist ");
+			appendStringInfo(str, " :coldeflist ");
 			_outNode(str, node->coldeflist);
 			break;
 		case RTE_JOIN:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 561145bb97de16f07c7ab2ea348b320e21c8ba75..463a8d5a4e538ee5f00d8eee47ed0a2eff8574a3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.363 2002/08/28 20:46:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.364 2002/08/29 00:17:04 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -204,8 +204,8 @@ static void doNegateFloat(Value *v);
 
 %type <list>	stmtblock, stmtmulti,
 				OptTableElementList, TableElementList, OptInherit, definition,
-				opt_distinct, opt_definition, func_args, rowdefinition
-				func_args_list, func_as, createfunc_opt_list
+				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,
 				sort_clause, opt_sort_clause, sortby_list, index_params,
@@ -216,7 +216,7 @@ static void doNegateFloat(Value *v);
 				insert_target_list, def_list, opt_indirection,
 				group_clause, TriggerFuncArgs, select_limit,
 				opt_select_limit, opclass_item_list, trans_options,
-				TableFuncElementList, OptTableFuncElementList,
+				TableFuncElementList,
 				convert_args, prep_type_clause, prep_type_list,
 				execute_param_clause, execute_param_list
 
@@ -1424,13 +1424,13 @@ OptTableElementList:
 		;
 
 TableElementList:
-			TableElementList ',' TableElement
+			TableElement
 				{
-					$$ = lappend($1, $3);
+					$$ = makeList1($1);
 				}
-			| TableElement
+			| TableElementList ',' TableElement
 				{
-					$$ = makeList1($1);
+					$$ = lappend($1, $3);
 				}
 		;
 
@@ -2234,11 +2234,12 @@ DefineStmt:
 					n->definition = $4;
 					$$ = (Node *)n;
 				}
-			| CREATE TYPE_P any_name AS rowdefinition
+			| CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
 				{
 					CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
 					RangeVar *r = makeNode(RangeVar);
 
+					/* can't use qualified_name, sigh */
 					switch (length($3))
 					{
 						case 1:
@@ -2258,13 +2259,12 @@ DefineStmt:
 							break;
 						default:
 							elog(ERROR,
-							"Improper qualified name "
-							"(too many dotted names): %s",
+								 "Improper qualified name (too many dotted names): %s",
 								 NameListToString($3));
 							break;
 					}
 					n->typevar = r;
-					n->coldeflist = $5;
+					n->coldeflist = $6;
 					$$ = (Node *)n;
 				}
 			| CREATE CHARACTER SET opt_as any_name GET definition opt_collate
@@ -2277,9 +2277,6 @@ DefineStmt:
 				}
 		;
 
-rowdefinition: '(' TableFuncElementList ')'			{ $$ = $2; }
-		;
-
 definition: '(' def_list ')'						{ $$ = $2; }
 		;
 
@@ -4539,14 +4536,22 @@ table_ref:	relation_expr
 					n->coldeflist = NIL;
 					$$ = (Node *) n;
 				}
-			| func_table AS '(' OptTableFuncElementList ')'
+			| func_table alias_clause
+				{
+					RangeFunction *n = makeNode(RangeFunction);
+					n->funccallnode = $1;
+					n->alias = $2;
+					n->coldeflist = NIL;
+					$$ = (Node *) n;
+				}
+			| func_table AS '(' TableFuncElementList ')'
 				{
 					RangeFunction *n = makeNode(RangeFunction);
 					n->funccallnode = $1;
 					n->coldeflist = $4;
 					$$ = (Node *) n;
 				}
-			| func_table AS ColId '(' OptTableFuncElementList ')'
+			| func_table AS ColId '(' TableFuncElementList ')'
 				{
 					RangeFunction *n = makeNode(RangeFunction);
 					Alias *a = makeNode(Alias);
@@ -4556,7 +4561,7 @@ table_ref:	relation_expr
 					n->coldeflist = $5;
 					$$ = (Node *) n;
 				}
-			| func_table ColId '(' OptTableFuncElementList ')'
+			| func_table ColId '(' TableFuncElementList ')'
 				{
 					RangeFunction *n = makeNode(RangeFunction);
 					Alias *a = makeNode(Alias);
@@ -4566,14 +4571,6 @@ table_ref:	relation_expr
 					n->coldeflist = $4;
 					$$ = (Node *) n;
 				}
-			| func_table alias_clause
-				{
-					RangeFunction *n = makeNode(RangeFunction);
-					n->funccallnode = $1;
-					n->alias = $2;
-					n->coldeflist = NIL;
-					$$ = (Node *) n;
-				}
 			| select_with_parens
 				{
 					/*
@@ -4815,24 +4812,18 @@ func_table: func_name '(' ')'
 
 where_clause:
 			WHERE a_expr							{ $$ = $2; }
-													/* no qualifiers */
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
 
-OptTableFuncElementList:
-			TableFuncElementList				{ $$ = $1; }
-			| /*EMPTY*/							{ $$ = NIL; }
-		;
-
 TableFuncElementList:
-			TableFuncElementList ',' TableFuncElement
+			TableFuncElement
 				{
-					$$ = lappend($1, $3);
+					$$ = makeList1($1);
 				}
-			| TableFuncElement
+			| TableFuncElementList ',' TableFuncElement
 				{
-					$$ = makeList1($1);
+					$$ = lappend($1, $3);
 				}
 		;
 
@@ -4842,7 +4833,6 @@ TableFuncElement:	ColId Typename
 					n->colname = $1;
 					n->typename = $2;
 					n->constraints = NIL;
-
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 6713c665098dac020ad90e1bdf4fa8c13c920123..174c05790d9b3af81b8f7ea8e5f0ba2178659233 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.77 2002/08/08 17:00:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.78 2002/08/29 00:17:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -900,17 +900,14 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	 * Now determine if the function returns a simple or composite type,
 	 * and check/add column aliases.
 	 */
-	functyptype = get_typtype(funcrettype);
-
 	if (coldeflist != NIL)
 	{
 		/*
 		 * we *only* allow a coldeflist for functions returning a
 		 * RECORD pseudo-type
 		 */
-		if (functyptype != 'p' || (functyptype == 'p' && funcrettype != RECORDOID))
-			elog(ERROR, "A column definition list is only allowed for"
-						" functions returning RECORD");
+		if (funcrettype != RECORDOID)
+			elog(ERROR, "A column definition list is only allowed for functions returning RECORD");
 	}
 	else
 	{
@@ -918,57 +915,55 @@ addRangeTableEntryForFunction(ParseState *pstate,
 		 * ... and a coldeflist is *required* for functions returning a
 		 * RECORD pseudo-type
 		 */
-		if (functyptype == 'p' && funcrettype == RECORDOID)
-			elog(ERROR, "A column definition list is required for functions"
-						" returning RECORD");
+		if (funcrettype == RECORDOID)
+			elog(ERROR, "A column definition list is required for functions returning RECORD");
 	}
 
+	functyptype = get_typtype(funcrettype);
+
 	if (functyptype == 'c')
 	{
 		/*
 		 * Named composite data type, i.e. a table's row type
 		 */
 		Oid			funcrelid = typeidTypeRelid(funcrettype);
+		Relation	rel;
+		int			maxattrs;
 
-		if (OidIsValid(funcrelid))
-		{
-			/*
-			 * Get the rel's relcache entry.  This access ensures that we have an
-			 * up-to-date relcache entry for the rel.
-			 */
-			Relation	rel;
-			int			maxattrs;
+		if (!OidIsValid(funcrelid))
+			elog(ERROR, "Invalid typrelid for complex type %u",
+				 funcrettype);
 
-			rel = heap_open(funcrelid, AccessShareLock);
-
-			/*
-			 * Since the rel is open anyway, let's check that the number of column
-			 * aliases is reasonable.
-			 */
-			maxattrs = RelationGetNumberOfAttributes(rel);
-			if (maxattrs < numaliases)
-				elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
-					 RelationGetRelationName(rel), maxattrs, numaliases);
+		/*
+		 * Get the rel's relcache entry.  This access ensures that we have an
+		 * up-to-date relcache entry for the rel.
+		 */
+		rel = relation_open(funcrelid, AccessShareLock);
 
-			/* fill in alias columns using actual column names */
-			for (varattno = numaliases; varattno < maxattrs; varattno++)
-			{
-				char	   *attrname;
+		/*
+		 * Since the rel is open anyway, let's check that the number of column
+		 * aliases is reasonable.
+		 */
+		maxattrs = RelationGetNumberOfAttributes(rel);
+		if (maxattrs < numaliases)
+			elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+				 RelationGetRelationName(rel), maxattrs, numaliases);
 
-				attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-				eref->colnames = lappend(eref->colnames, makeString(attrname));
-			}
+		/* fill in alias columns using actual column names */
+		for (varattno = numaliases; varattno < maxattrs; varattno++)
+		{
+			char	   *attrname;
 
-			/*
-			 * Drop the rel refcount, but keep the access lock till end of
-			 * transaction so that the table can't be deleted or have its schema
-			 * modified underneath us.
-			 */
-			heap_close(rel, NoLock);
+			attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+			eref->colnames = lappend(eref->colnames, makeString(attrname));
 		}
-		else
-			elog(ERROR, "Invalid return relation specified for function %s",
-				 funcname);
+
+		/*
+		 * Drop the rel refcount, but keep the access lock till end of
+		 * transaction so that the table can't be deleted or have its schema
+		 * modified underneath us.
+		 */
+		relation_close(rel, NoLock);
 	}
 	else if (functyptype == 'b' || functyptype == 'd')
 	{
@@ -986,10 +981,12 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	{
 		List	   *col;
 
+		/* Use the column definition list to form the alias list */
+		eref->colnames = NIL;
 		foreach(col, coldeflist)
 		{
-			char	   *attrname;
 			ColumnDef  *n = lfirst(col);
+			char	   *attrname;
 
 			attrname = pstrdup(n->colname);
 			eref->colnames = lappend(eref->colnames, makeString(attrname));
@@ -1277,63 +1274,58 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 				char functyptype = get_typtype(funcrettype);
 				List *coldeflist = rte->coldeflist;
 
-				/*
-				 * Build a suitable tupledesc representing the output rows
-				 */
 				if (functyptype == 'c')
 				{
+					/*
+					 * Composite data type, i.e. a table's row type
+					 * Same as ordinary relation RTE
+					 */
 					Oid	funcrelid = typeidTypeRelid(funcrettype);
-					if (OidIsValid(funcrelid))
+					Relation	rel;
+					int			maxattrs;
+					int			numaliases;
+
+					if (!OidIsValid(funcrelid))
+						elog(ERROR, "Invalid typrelid for complex type %u",
+							 funcrettype);
+
+					rel = relation_open(funcrelid, AccessShareLock);
+					maxattrs = RelationGetNumberOfAttributes(rel);
+					numaliases = length(rte->eref->colnames);
+
+					for (varattno = 0; varattno < maxattrs; varattno++)
 					{
-						/*
-						 * Composite data type, i.e. a table's row type
-						 * Same as ordinary relation RTE
-						 */
-						Relation	rel;
-						int			maxattrs;
-						int			numaliases;
-
-						rel = heap_open(funcrelid, AccessShareLock);
-						maxattrs = RelationGetNumberOfAttributes(rel);
-						numaliases = length(rte->eref->colnames);
-
-						for (varattno = 0; varattno < maxattrs; varattno++)
+						Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+
+						if (attr->attisdropped)
+							continue;
+
+						if (colnames)
 						{
-							Form_pg_attribute attr = rel->rd_att->attrs[varattno];
-
-							if (attr->attisdropped)
-								continue;
-
-							if (colnames)
-							{
-								char	   *label;
-
-								if (varattno < numaliases)
-									label = strVal(nth(varattno, rte->eref->colnames));
-								else
-									label = NameStr(attr->attname);
-								*colnames = lappend(*colnames, makeString(pstrdup(label)));
-							}
-
-							if (colvars)
-							{
-								Var		   *varnode;
-
-								varnode = makeVar(rtindex,
-												attr->attnum,
-												attr->atttypid,
-												attr->atttypmod,
-												sublevels_up);
-
-								*colvars = lappend(*colvars, varnode);
-							}
+							char	   *label;
+
+							if (varattno < numaliases)
+								label = strVal(nth(varattno, rte->eref->colnames));
+							else
+								label = NameStr(attr->attname);
+							*colnames = lappend(*colnames, makeString(pstrdup(label)));
 						}
 
-						heap_close(rel, AccessShareLock);
+						if (colvars)
+						{
+							Var		   *varnode;
+
+							varnode = makeVar(rtindex,
+											  attr->attnum,
+											  attr->atttypid,
+											  attr->atttypmod,
+											  sublevels_up);
+
+							*colvars = lappend(*colvars, varnode);
+						}
 					}
-					else
-						elog(ERROR, "Invalid return relation specified"
-									" for function");
+
+					relation_close(rel, AccessShareLock);
 				}
 				else if (functyptype == 'b' || functyptype == 'd')
 				{
@@ -1376,12 +1368,9 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 						if (colvars)
 						{
 							Var		   *varnode;
-							HeapTuple	typeTuple;
 							Oid			atttypid;
 
-							typeTuple = typenameType(colDef->typename);
-							atttypid = HeapTupleGetOid(typeTuple);
-							ReleaseSysCache(typeTuple);
+							atttypid = typenameTypeId(colDef->typename);
 
 							varnode = makeVar(rtindex,
 											attnum,
@@ -1394,8 +1383,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 					}
 				}
 				else
-					elog(ERROR, "Unknown kind of return type specified"
-								" for function");
+					elog(ERROR, "Unknown kind of return type specified for function");
 			}
 			break;
 		case RTE_JOIN:
@@ -1595,9 +1583,6 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 				char functyptype = get_typtype(funcrettype);
 				List *coldeflist = rte->coldeflist;
 
-				/*
-				 * Build a suitable tupledesc representing the output rows
-				 */
 				if (functyptype == 'c')
 				{
 					/*
@@ -1605,36 +1590,33 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 					 * Same as ordinary relation RTE
 					 */
 					Oid funcrelid = typeidTypeRelid(funcrettype);
+					HeapTuple			tp;
+					Form_pg_attribute	att_tup;
 
-					if (OidIsValid(funcrelid))
-					{
-						HeapTuple			tp;
-						Form_pg_attribute	att_tup;
-
-						tp = SearchSysCache(ATTNUM,
-											ObjectIdGetDatum(funcrelid),
-											Int16GetDatum(attnum),
-											0, 0);
-						/* this shouldn't happen... */
-						if (!HeapTupleIsValid(tp))
-							elog(ERROR, "Relation %s does not have attribute %d",
-								 get_rel_name(funcrelid), attnum);
-						att_tup = (Form_pg_attribute) GETSTRUCT(tp);
-						/*
-						 * If dropped column, pretend it ain't there.  See notes
-						 * in scanRTEForColumn.
-						 */
-						if (att_tup->attisdropped)
-							elog(ERROR, "Relation \"%s\" has no column \"%s\"",
-								 get_rel_name(funcrelid),
-								 NameStr(att_tup->attname));
-						*vartype = att_tup->atttypid;
-						*vartypmod = att_tup->atttypmod;
-						ReleaseSysCache(tp);
-					}
-					else
-						elog(ERROR, "Invalid return relation specified"
-									" for function");
+					if (!OidIsValid(funcrelid))
+						elog(ERROR, "Invalid typrelid for complex type %u",
+							 funcrettype);
+
+					tp = SearchSysCache(ATTNUM,
+										ObjectIdGetDatum(funcrelid),
+										Int16GetDatum(attnum),
+										0, 0);
+					/* this shouldn't happen... */
+					if (!HeapTupleIsValid(tp))
+						elog(ERROR, "Relation \"%s\" does not have attribute %d",
+							 get_rel_name(funcrelid), attnum);
+					att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+					/*
+					 * If dropped column, pretend it ain't there.  See notes
+					 * in scanRTEForColumn.
+					 */
+					if (att_tup->attisdropped)
+						elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+							 get_rel_name(funcrelid),
+							 NameStr(att_tup->attname));
+					*vartype = att_tup->atttypid;
+					*vartypmod = att_tup->atttypmod;
+					ReleaseSysCache(tp);
 				}
 				else if (functyptype == 'b' || functyptype == 'd')
 				{
@@ -1647,19 +1629,12 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 				else if (functyptype == 'p' && funcrettype == RECORDOID)
 				{
 					ColumnDef  *colDef = nth(attnum - 1, coldeflist);
-					HeapTuple	typeTuple;
-					Oid			atttypid;
-
-					typeTuple = typenameType(colDef->typename);
-					atttypid = HeapTupleGetOid(typeTuple);
-					ReleaseSysCache(typeTuple);
 
-					*vartype = atttypid;
+					*vartype = typenameTypeId(colDef->typename);
 					*vartypmod = -1;
 				}
 				else
-					elog(ERROR, "Unknown kind of return type specified"
-								" for function");
+					elog(ERROR, "Unknown kind of return type specified for function");
 			}
 			break;
 		case RTE_JOIN:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b16adef54dbce3e68463c23891c3f929c4b8053f..65745be3c00173cfbb66ae37be37310530a9c274 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.173 2002/08/27 04:55:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.174 2002/08/29 00:17:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -583,14 +583,9 @@ ProcessUtility(Node *parsetree,
 
 		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);
+				DefineCompositeType(stmt->typevar, stmt->coldeflist);
 			}
 			break;
 
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 38e540e3c85f161d2731682955c87b827d7b21ee..83d0d1051df974056d7122a049f1c79762f0ebb8 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -5,22 +5,22 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.2 2002/08/27 04:00:28 momjian Exp $
+ *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
  */
-
 #include "postgres.h"
-#include "fmgr.h"
+
 #include "funcapi.h"
 #include "catalog/pg_type.h"
 #include "storage/lmgr.h"
 #include "storage/lock.h"
 #include "storage/lwlock.h"
 #include "storage/proc.h"
+#include "utils/builtins.h"
 
-Datum pg_lock_status(PG_FUNCTION_ARGS);
 
 static int next_lock(int locks[]);
 
+
 Datum
 pg_lock_status(PG_FUNCTION_ARGS)
 {
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index bd642a26e9719471dc999efc3a8534e91b3a0786..d90c20adf981bce9e8bbd0f72c450ce7b6c12ffd 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.34 2002/08/28 20:46:24 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.35 2002/08/29 00:17:05 tgl Exp $
  *
  * NOTES
  *	  input routine largely stolen from boxin().
@@ -226,9 +226,6 @@ currtid_byreloid(PG_FUNCTION_ARGS)
 	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);
 
@@ -252,9 +249,6 @@ currtid_byrelname(PG_FUNCTION_ARGS)
 	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);
 
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 079ba2152a7f0de1891fce09541c2afd519ee239..66dc58d6c4b4935706ce3aa003da7e36d0d556cd 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.80 2002/08/26 17:53:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.81 2002/08/29 00:17:05 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -1190,6 +1190,33 @@ get_typtype(Oid typid)
 		return '\0';
 }
 
+/*
+ * getTypeInputInfo
+ *
+ *		Get info needed for converting values of a type to internal form
+ */
+void
+getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem)
+{
+	HeapTuple	typeTuple;
+	Form_pg_type pt;
+
+	typeTuple = SearchSysCache(TYPEOID,
+							   ObjectIdGetDatum(type),
+							   0, 0, 0);
+	if (!HeapTupleIsValid(typeTuple))
+		elog(ERROR, "getTypeInputInfo: Cache lookup of type %u failed", type);
+	pt = (Form_pg_type) GETSTRUCT(typeTuple);
+
+	if (!pt->typisdefined)
+		elog(ERROR, "Type \"%s\" is only a shell", NameStr(pt->typname));
+
+	*typInput = pt->typinput;
+	*typElem = pt->typelem;
+
+	ReleaseSysCache(typeTuple);
+}
+
 /*
  * getTypeOutputInfo
  *
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index a8d4e4fb5f599621d61e9e04646f9e16e1fc0bf5..28311c26b7b2ec3db8901244afb10d4d8beb7bc0 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -6,13 +6,18 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
+ * IDENTIFICATION
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/funcapi.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
+ *
  *-------------------------------------------------------------------------
  */
+#include "postgres.h"
 
 #include "funcapi.h"
 #include "catalog/pg_type.h"
 #include "utils/syscache.h"
 
+
 /*
  * init_MultiFuncCall
  * Create an empty FuncCallContext data structure
@@ -99,8 +104,6 @@ per_MultiFuncCall(PG_FUNCTION_ARGS)
 void
 end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
 {
-	MemoryContext oldcontext;
-
 	/* unbind from fcinfo */
 	fcinfo->flinfo->fn_extra = NULL;
 
@@ -108,32 +111,8 @@ end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
 	 * Caller is responsible to free up memory for individual
 	 * struct elements other than att_in_funcinfo and elements.
 	 */
-	oldcontext = MemoryContextSwitchTo(funcctx->fmctx);
-
 	if (funcctx->attinmeta != NULL)
 		pfree(funcctx->attinmeta);
 
 	pfree(funcctx);
-
-	MemoryContextSwitchTo(oldcontext);
-}
-
-void
-get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
-{
-	HeapTuple		typeTuple;
-	Form_pg_type	typtup;
-
-	typeTuple = SearchSysCache(TYPEOID,
-							   ObjectIdGetDatum(typeid),
-							   0, 0, 0);
-	if (!HeapTupleIsValid(typeTuple))
-		elog(ERROR, "get_type_metadata: Cache lookup of type %u failed", typeid);
-
-	typtup = (Form_pg_type) GETSTRUCT(typeTuple);
-
-	*attinfuncid = typtup->typinput;
-	*attelem = typtup->typelem;
-
-	ReleaseSysCache(typeTuple);
 }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b73118289fce6779125de125a7792a58c8357bc1..660cd124ba90c6afec74c5bb34a5d760109af516 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5,7 +5,7 @@
  * command, configuration file, and command line options.
  * See src/backend/utils/misc/README for more information.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.84 2002/08/26 17:53:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.85 2002/08/29 00:17:05 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -2284,7 +2284,7 @@ ShowGUCConfigOption(const char *name)
 	tstate = begin_tup_output_tupdesc(dest, tupdesc);
 
 	/* Send it */
-	PROJECT_LINE_OF_TEXT(tstate, value);
+	do_text_output_oneline(tstate, value);
 
 	end_tup_output(tstate);
 }
@@ -2462,7 +2462,7 @@ show_all_settings(PG_FUNCTION_ARGS)
 
  	if (call_cntr < max_calls)	/* do when there is more left to send */
  	{
-		char	   **values;
+		char	   *values[2];
 		char	   *varname;
 		char	   *varval;
 		bool		noshow;
@@ -2474,7 +2474,9 @@ show_all_settings(PG_FUNCTION_ARGS)
 		 */
 		do
 		{
-			varval = GetConfigOptionByNum(call_cntr, (const char **) &varname, &noshow);
+			varval = GetConfigOptionByNum(call_cntr,
+										  (const char **) &varname,
+										  &noshow);
 			if (noshow)
 			{
 				/* varval is a palloc'd copy, so free it */
@@ -2495,9 +2497,8 @@ show_all_settings(PG_FUNCTION_ARGS)
 		 * This should be an array of C strings which will
 		 * be processed later by the appropriate "in" functions.
 		 */
-		values = (char **) palloc(2 * sizeof(char *));
-		values[0] = pstrdup(varname);
-		values[1] = varval;	/* varval is already a palloc'd copy */
+		values[0] = varname;
+		values[1] = varval;
 
 		/* build a tuple */
 		tuple = BuildTupleFromCStrings(attinmeta, values);
@@ -2506,10 +2507,8 @@ show_all_settings(PG_FUNCTION_ARGS)
 		result = TupleGetDatum(slot, tuple);
 
 		/* Clean up */
-		pfree(values[0]);
 		if (varval != NULL)
-			pfree(values[1]);
-		pfree(values);
+			pfree(varval);
 
  		SRF_RETURN_NEXT(funcctx, result);
  	}
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 4f33ff4e4fd46f85f3d7ad46b355b1af007cc7de..ddcb617974527d4ef707c34d0d875f8359dfddab 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.68 2002/08/15 16:36:06 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.69 2002/08/29 00:17:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -215,10 +215,9 @@ flagInhTables(TableInfo *tblinfo, int numTables,
 
 	for (i = 0; i < numTables; i++)
 	{
-		/* Sequences, views, and types never have parents */
+		/* Sequences and views never have parents */
 		if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
-			tblinfo[i].relkind == RELKIND_VIEW ||
-			tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+			tblinfo[i].relkind == RELKIND_VIEW)
 			continue;
 
 		/* Don't bother computing anything for non-target tables, either */
@@ -270,10 +269,9 @@ flagInhAttrs(TableInfo *tblinfo, int numTables,
 
 	for (i = 0; i < numTables; i++)
 	{
-		/* Sequences, views, and types never have parents */
+		/* Sequences and views never have parents */
 		if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
-			tblinfo[i].relkind == RELKIND_VIEW ||
-			tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
+			tblinfo[i].relkind == RELKIND_VIEW)
 			continue;
 
 		/* Don't bother computing anything for non-target tables, either */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a56f176841a3a3c8b4347fe754636e0eb23f8233..d552f63a3a81b4a37e6e9e84580bc7f6605bc4fd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.294 2002/08/28 20:57:22 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.295 2002/08/29 00:17:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1148,10 +1148,6 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
 		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;
 
@@ -1581,7 +1577,8 @@ getTypes(int *numTypes)
 						  "typnamespace, "
 						  "(select usename from pg_user where typowner = usesysid) as usename, "
 						  "typelem, typrelid, "
-						  "(select relkind from pg_class where oid = typrelid) as typrelkind, "
+						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
+						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
 						  "typtype, typisdefined "
 						  "FROM pg_type");
 	}
@@ -1591,7 +1588,8 @@ getTypes(int *numTypes)
 						  "0::oid as typnamespace, "
 						  "(select usename from pg_user where typowner = usesysid) as usename, "
 						  "typelem, typrelid, "
-						  "''::char as typrelkind, "
+						  "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
+						  "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END as typrelkind, "
 						  "typtype, typisdefined "
 						  "FROM pg_type");
 	}
@@ -2120,7 +2118,6 @@ getTables(int *numTables)
 	}
 	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, "
@@ -2392,10 +2389,6 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
 		if (tbinfo->relkind == RELKIND_SEQUENCE)
 			continue;
 
-		/* Don't bother to collect info for type relations */
-		if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE)
-			continue;
-
 		/* Don't bother with uninteresting tables, either */
 		if (!tbinfo->interesting)
 			continue;
@@ -3210,7 +3203,7 @@ dumpOneDomain(Archive *fout, TypeInfo *tinfo)
 	/* DROP must be fully qualified in case same name appears in pg_catalog */
 	appendPQExpBuffer(delq, "DROP DOMAIN %s.",
 					  fmtId(tinfo->typnamespace->nspname));
-	appendPQExpBuffer(delq, "%s RESTRICT;\n",
+	appendPQExpBuffer(delq, "%s;\n",
 					  fmtId(tinfo->typname));
 
 	appendPQExpBuffer(q,
@@ -3263,15 +3256,10 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
 	PQExpBuffer query = createPQExpBuffer();
 	PGresult   *res;
 	int			ntups;
-	char	   *attname;
-	char	   *atttypdefn;
-	char	   *attbasetype;
-	const char *((*deps)[]);
-	int			depIdx = 0;
+	int		i_attname;
+	int		i_atttypdefn;
 	int			i;
 
-	deps = malloc(sizeof(char *) * 10);
-
 	/* Set proper schema search path so type references list correctly */
 	selectSourceSchema(tinfo->typnamespace->nspname);
 
@@ -3279,11 +3267,12 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
 	/* 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 "
+					  "pg_catalog.format_type(a.atttypid, a.atttypmod) as atttypdefn "
 					  "FROM pg_catalog.pg_type t, pg_catalog.pg_attribute a "
 					  "WHERE t.oid = '%s'::pg_catalog.oid "
-					  "AND a.attrelid = t.typrelid",
+					  "AND a.attrelid = t.typrelid "
+					  "AND NOT a.attisdropped "
+					  "ORDER BY a.attnum ",
 					  tinfo->oid);
 
 	res = PQexec(g_conn, query->data);
@@ -3302,37 +3291,35 @@ dumpOneCompositeType(Archive *fout, TypeInfo *tinfo)
 		exit_nicely();
 	}
 
-	/* DROP must be fully qualified in case same name appears in pg_catalog */
-	appendPQExpBuffer(delq, "DROP TYPE %s.",
-					  fmtId(tinfo->typnamespace->nspname));
-	appendPQExpBuffer(delq, "%s RESTRICT;\n",
-					  fmtId(tinfo->typname));
+	i_attname = PQfnumber(res, "attname");
+	i_atttypdefn = PQfnumber(res, "atttypdefn");
 
-	appendPQExpBuffer(q,
-					  "CREATE TYPE %s AS (",
+	appendPQExpBuffer(q, "CREATE TYPE %s AS (",
 					  fmtId(tinfo->typname));
 
 	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"));
+		char	   *attname;
+		char	   *atttypdefn;
 
-		if (i > 0)
-			appendPQExpBuffer(q, ",\n\t %s %s", attname, atttypdefn);
-		else
-			appendPQExpBuffer(q, "%s %s", attname, atttypdefn);
+		attname = PQgetvalue(res, i, i_attname);
+		atttypdefn = PQgetvalue(res, i, i_atttypdefn);
 
-		/* Depends on the base type */
-		(*deps)[depIdx++] = strdup(attbasetype);
+		if (i > 0)
+			appendPQExpBuffer(q, ",\n\t");
+		appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
 	}
 	appendPQExpBuffer(q, ");\n");
 
-	(*deps)[depIdx++] = NULL;		/* End of List */
+	/* DROP must be fully qualified in case same name appears in pg_catalog */
+	appendPQExpBuffer(delq, "DROP TYPE %s.",
+					  fmtId(tinfo->typnamespace->nspname));
+	appendPQExpBuffer(delq, "%s;\n",
+					  fmtId(tinfo->typname));
 
 	ArchiveEntry(fout, tinfo->oid, tinfo->typname,
 				 tinfo->typnamespace->nspname,
-				 tinfo->usename, "TYPE", deps,
+				 tinfo->usename, "TYPE", NULL,
 				 q->data, delq->data, NULL, NULL, NULL);
 
 	/*** Dump Type Comments ***/
@@ -3365,7 +3352,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs,
 		if (!tinfo[i].typnamespace->dump)
 			continue;
 
-		/* skip relation types for non-stand-alone type relations*/
+		/* skip complex types, except for standalone composite types */
 		if (atooid(tinfo[i].typrelid) != 0 && tinfo[i].typrelkind != 'c')
 			continue;
 
@@ -5046,8 +5033,6 @@ dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
 
 			if (tbinfo->relkind == RELKIND_SEQUENCE) /* already dumped */
 				continue;
-			if (tbinfo->relkind == RELKIND_COMPOSITE_TYPE) /* dumped as a type */
-				continue;
 
 			if (tbinfo->dump)
 			{
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 64cef431701437a20b49b87563e3081c28a32ff5..4bf3b6cc4916d7b2c8978f4909a49c9a51a9febd 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,7 +3,7 @@
  *
  * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.66 2002/08/27 20:16:48 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.67 2002/08/29 00:17:05 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
@@ -196,8 +196,10 @@ describeTypes(const char *pattern, bool verbose)
 	if (verbose)
 		appendPQExpBuffer(&buf,
 				 "  t.typname AS \"%s\",\n"
-				 "  CASE WHEN t.typlen < 0\n"
-				 "    THEN CAST('var' AS pg_catalog.text)\n"
+				 "  CASE WHEN t.typrelid != 0\n"
+				 "      THEN CAST('tuple' AS pg_catalog.text)\n"
+				 "    WHEN t.typlen < 0\n"
+				 "      THEN CAST('var' AS pg_catalog.text)\n"
 				 "    ELSE CAST(t.typlen AS pg_catalog.text)\n"
 				 "  END AS \"%s\",\n",
 				 _("Internal name"), _("Size"));
@@ -209,12 +211,12 @@ describeTypes(const char *pattern, bool verbose)
 			 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
 
 	/*
-	 * do not include array types (start with underscore), do not include
-	 * user relations (typrelid!=0) unless they are type relations
+	 * do not include array types (start with underscore); do not include
+	 * complex types (typrelid!=0) unless they are standalone composite types
 	 */
 	appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
-	appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_class c "
-							  "where c.oid = t.typrelid)) ");
+	appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
+							  "WHERE c.oid = t.typrelid)) ");
 	appendPQExpBuffer(&buf, "AND t.typname !~ '^_'\n");
 
 	/* Match name pattern against either internal or external name */
@@ -801,6 +803,10 @@ describeOneTableDetails(const char *schemaname,
 			printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
 							  schemaname, relationname);
 			break;
+		case 'c':
+			printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
+							  schemaname, relationname);
+			break;
 		default:
 			printfPQExpBuffer(&title, _("?%c? \"%s.%s\""),
 							  tableinfo.relkind, schemaname, relationname);
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 8efb6c07a3d439f470cf8903824d8dd80d01ab68..3708a71bab9ede8646fc85c2ff3c59f87523cff4 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_type.h,v 1.130 2002/08/26 17:54:01 tgl Exp $
+ * $Id: pg_type.h,v 1.131 2002/08/29 00:17:06 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -542,6 +542,7 @@ extern Oid TypeCreate(const char *typeName,
 		   Oid typeNamespace,
 		   Oid assignedTypeOid,
 		   Oid relationOid,
+		   char relationKind,
 		   int16 internalSize,
 		   char typeType,
 		   char typDelim,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 2fdb5bc210ca503dae8483334093003efdb4793b..88104565976736d15467b93a144b912113675725 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.73 2002/08/02 18:15:09 tgl Exp $
+ * $Id: executor.h,v 1.74 2002/08/29 00:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -123,7 +123,8 @@ extern void SetChangedParamList(Plan *node, List *newchg);
 
 typedef struct TupOutputState
 {
-	TupleDesc	tupdesc;
+	/* use "struct" here to allow forward reference */
+	struct AttInMetadata *metadata;
 	DestReceiver *destfunc;
 } TupOutputState;
 
@@ -132,10 +133,15 @@ extern void do_tup_output(TupOutputState *tstate, char **values);
 extern void do_text_output_multiline(TupOutputState *tstate, char *text);
 extern void end_tup_output(TupOutputState *tstate);
 
-#define PROJECT_LINE_OF_TEXT(tstate, text_to_project) \
+/*
+ * Write a single line of text given as a C string.
+ *
+ * Should only be used with a single-TEXT-attribute tupdesc.
+ */
+#define do_text_output_oneline(tstate, text_to_emit) \
 	do { \
 		char *values_[1]; \
-		values_[0] = (text_to_project); \
+		values_[0] = (text_to_emit); \
 		do_tup_output(tstate, values_); \
 	} while (0)
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 294147538206bc2aa2f8e7c08f825f6d6766209d..27dbdf20e62061a9207d0bee6ca64ad464807733 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,26 +9,18 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
+ * $Id: funcapi.h,v 1.5 2002/08/29 00:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef FUNCAPI_H
 #define FUNCAPI_H
 
-#include "postgres.h"
-
 #include "fmgr.h"
-#include "access/htup.h"
 #include "access/tupdesc.h"
 #include "executor/executor.h"
 #include "executor/tuptable.h"
 
-/*
- * All functions that can be called directly by fmgr must have this signature.
- * (Other functions can be called by using a handler that does have this
- * signature.)
- */
-
 
 /*-------------------------------------------------------------------------
  *	Support to ease writing Functions returning composite types
@@ -40,20 +32,19 @@
  * is derived from the TupleDesc, but it is stored here to
  * avoid redundant cpu cycles on each call to an SRF.
  */
-typedef struct
+typedef struct AttInMetadata
 {
 	/* full TupleDesc */
 	TupleDesc	   tupdesc;
 
-	/* pointer to array of attribute "type"in finfo */
+	/* array of attribute type input function finfo */
 	FmgrInfo	   *attinfuncs;
 
-	/* pointer to array of attribute type typelem */
+	/* array of attribute type typelem */
 	Oid			   *attelems;
 
-	/* pointer to array of attribute type typtypmod */
-	int4		   *atttypmods;
-
+	/* array of attribute typmod */
+	int32		   *atttypmods;
 }	AttInMetadata;
 
 /*-------------------------------------------------------------------------
@@ -63,7 +54,7 @@ typedef struct
  * This struct holds function context for Set Returning Functions.
  * Use fn_extra to hold a pointer to it across calls
  */
-typedef struct
+typedef struct FuncCallContext
 {
 	/*
 	 * Number of times we've been called before.
@@ -120,35 +111,34 @@ typedef struct
 
 }	FuncCallContext;
 
-/*-------------------------------------------------------------------------
+/*----------
  *	Support to ease writing Functions returning composite types
  *
  * External declarations:
- * TupleDesc RelationNameGetTupleDesc(char *relname) - Use to get a TupleDesc
- *		based on the function's return type relation.
+ * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
+ *		TupleDesc based on a specified relation.
  * TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use to get a
- *		TupleDesc based on the function's type oid. This can be used to get
- *		a TupleDesc for a base (scalar), or composite (relation) type.
+ *		TupleDesc based on a type OID. This can be used to get
+ *		a TupleDesc for a base (scalar) or composite (relation) type.
  * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
  *		given a TupleDesc.
- * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Get a pointer
- *		to AttInMetadata based on the function's TupleDesc. AttInMetadata can
+ * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Build an
+ *		AttInMetadata struct based on the given TupleDesc. AttInMetadata can
  *		be used in conjunction with C strings to produce a properly formed
  *		tuple. Store the metadata here for use across calls to avoid redundant
  *		work.
  * HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -
  *		build a HeapTuple given user data in C string form. values is an array
  *		of C strings, one for each attribute of the return tuple.
- * void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem) - Get
- *      an attribute "in" function and typelem value given the typeid.
  *
  * Macro declarations:
  * TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
  *		given a tuple and a slot.
+ *----------
  */
 
 /* from tupdesc.c */
-extern TupleDesc RelationNameGetTupleDesc(char *relname);
+extern TupleDesc RelationNameGetTupleDesc(const char *relname);
 extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
 
 /* from execTuples.c */
@@ -156,13 +146,11 @@ extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
 extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
 extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
 
-/* from funcapi.c */
-extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem);
-
 #define TupleGetDatum(_slot, _tuple) \
 	PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, true))
 
-/*-------------------------------------------------------------------------
+
+/*----------
  *		Support for Set Returning Functions (SRFs)
  *
  * The basic API for SRFs looks something like:
@@ -200,6 +188,7 @@ extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem);
  * 	}
  * }
  *
+ *----------
  */
 
 /* from funcapi.c */
@@ -208,12 +197,15 @@ extern FuncCallContext *per_MultiFuncCall(PG_FUNCTION_ARGS);
 extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 
 #define SRF_IS_FIRSTCALL() (fcinfo->flinfo->fn_extra == NULL)
+
 #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
+
 #define SRF_PERCALL_SETUP() per_MultiFuncCall(fcinfo)
+
 #define SRF_RETURN_NEXT(_funcctx, _result) \
 	do { \
 		ReturnSetInfo	   *rsi; \
-		_funcctx->call_cntr++; \
+		(_funcctx)->call_cntr++; \
 		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
 		rsi->isDone = ExprMultipleResult; \
 		PG_RETURN_DATUM(_result); \
@@ -225,7 +217,6 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
 		end_MultiFuncCall(fcinfo, _funcctx); \
 		rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
 		rsi->isDone = ExprEndResult; \
-		_funcctx->slot = NULL; \
 		PG_RETURN_NULL(); \
 	} while (0)
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index ee25cc62c95a44e282e7019ec272fdf0689c37b0..6e146e2ca6d67b6ca179c951b9c121b8ddb51ccb 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.71 2002/08/04 19:48:10 momjian Exp $
+ * $Id: execnodes.h,v 1.72 2002/08/29 00:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -509,17 +509,13 @@ typedef struct SubqueryScanState
  *		Function nodes are used to scan the results of a
  *		function appearing in FROM (typically a function returning set).
  *
- *		functionmode		function operating mode:
- *							- repeated call
- *							- materialize
- *							- return query
+ *		functionmode		function operating mode
  *		tupdesc				function's return tuple description
  *		tuplestorestate		private state of tuplestore.c
  *		funcexpr			function expression being evaluated
  *		returnsTuple		does function return tuples?
  *		fn_typeid			OID of function return type
- *		fn_typtype			return Datum type, i.e. 'b'ase,
- *							'c'atalog, or 'p'seudo
+ *		fn_typtype			return type's typtype
  * ----------------
  */
 typedef enum FunctionMode
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index bdee336b002eeb8dffb8144e36b908de3514aa9e..6f33ee4e42c9a541d8aa563d4d2754f20826c9c2 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.195 2002/08/22 03:24:01 momjian Exp $
+ * $Id: builtins.h,v 1.196 2002/08/29 00:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -690,6 +690,9 @@ extern Datum show_config_by_name(PG_FUNCTION_ARGS);
 extern Datum set_config_by_name(PG_FUNCTION_ARGS);
 extern Datum show_all_settings(PG_FUNCTION_ARGS);
 
+/* lockfuncs.c */
+extern Datum pg_lock_status(PG_FUNCTION_ARGS);
+
 /* catalog/pg_conversion.c */
 extern Datum pg_convert3(PG_FUNCTION_ARGS);
 
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 78d099086592a3d239c4cbf52eea11994bfec67f..2681d67139f97767c5a6d65ccaee25d25f45cec0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.59 2002/08/26 17:54:02 tgl Exp $
+ * $Id: lsyscache.h,v 1.60 2002/08/29 00:17:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,6 +54,7 @@ extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
 extern char get_typstorage(Oid typid);
 extern Node *get_typdefault(Oid typid);
 extern char get_typtype(Oid typid);
+extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem);
 extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem,
 							  bool *typIsVarlena);
 extern Oid getBaseType(Oid typid);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 582859d15a5ee38010b0fc01501ce317f7f3f04f..bf7d2df24dc96b970a7693805d334dd734048666 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -134,6 +134,44 @@ SELECT * FROM vw_getfoo;
      1 |        2 | Ed
 (2 rows)
 
+-- sql, proretset = f, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+ fooid | foosubid | fooname 
+-------+----------+---------
+     1 |        1 | Joe
+(1 row)
+
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS 
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+ fooid | foosubid | fooname 
+-------+----------+---------
+     1 |        1 | Joe
+(1 row)
+
+-- sql, proretset = t, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS setof record AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+ fooid | foosubid | fooname 
+-------+----------+---------
+     1 |        1 | Joe
+     1 |        2 | Ed
+(2 rows)
+
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+ fooid | foosubid | fooname 
+-------+----------+---------
+     1 |        1 | Joe
+     1 |        2 | Ed
+(2 rows)
+
 -- plpgsql, proretset = f, prorettype = b
 DROP VIEW vw_getfoo;
 DROP FUNCTION getfoo(int);
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 0ace80e5d4d5771e49bec5231d05dd201579b340..03a3a558461493884402216183427751e5ba7d8c 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -63,6 +63,24 @@ SELECT * FROM getfoo(1) AS t1;
 CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);
 SELECT * FROM vw_getfoo;
 
+-- sql, proretset = f, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS 
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+
+-- sql, proretset = t, prorettype = record
+DROP VIEW vw_getfoo;
+DROP FUNCTION getfoo(int);
+CREATE FUNCTION getfoo(int) RETURNS setof record AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
+SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
+(fooid int, foosubid int, fooname text);
+SELECT * FROM vw_getfoo;
+
 -- plpgsql, proretset = f, prorettype = b
 DROP VIEW vw_getfoo;
 DROP FUNCTION getfoo(int);