diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ac941598be7546dc395005606f502e2b548b534f..4a521a0fac79193db79252da9a6f71ee83d5311e 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -447,7 +447,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc = NULL;
 	int			call_cntr;
 	int			max_calls;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	char	   *msg;
 	PGresult   *res = NULL;
@@ -566,9 +565,10 @@ dblink_fetch(PG_FUNCTION_ARGS)
 
 		if (functyptype == 'c')
 			tupdesc = TypeGetTupleDesc(functypeid, NIL);
-		else if (functyptype == 'p' && functypeid == RECORDOID)
+		else if (functypeid == RECORDOID)
 		{
-			if (!rsinfo || !IsA(rsinfo, ReturnSetInfo))
+			if (!rsinfo || !IsA(rsinfo, ReturnSetInfo) ||
+				rsinfo->expectedDesc == NULL)
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("function returning record called in context "
@@ -582,8 +582,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
 			elog(ERROR, "return type must be a row type");
 
 		/* store needed metadata for subsequent calls */
-		slot = TupleDescGetSlot(tupdesc);
-		funcctx->slot = slot;
 		attinmeta = TupleDescGetAttInMetadata(tupdesc);
 		funcctx->attinmeta = attinmeta;
 
@@ -599,8 +597,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
 	call_cntr = funcctx->call_cntr;
 	max_calls = funcctx->max_calls;
 
-	slot = funcctx->slot;
-
 	res = (PGresult *) funcctx->user_fctx;
 	attinmeta = funcctx->attinmeta;
 	tupdesc = attinmeta->tupdesc;
@@ -626,7 +622,7 @@ dblink_fetch(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = TupleGetDatum(slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
@@ -649,7 +645,6 @@ dblink_record(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc = NULL;
 	int			call_cntr;
 	int			max_calls;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	char	   *msg;
 	PGresult   *res = NULL;
@@ -741,7 +736,7 @@ dblink_record(PG_FUNCTION_ARGS)
 			/* need a tuple descriptor representing one TEXT column */
 			tupdesc = CreateTemplateTupleDesc(1, false);
 			TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
-							   TEXTOID, -1, 0, false);
+							   TEXTOID, -1, 0);
 
 			/*
 			 * and save a copy of the command status string to return as
@@ -776,9 +771,10 @@ dblink_record(PG_FUNCTION_ARGS)
 		{
 			if (functyptype == 'c')
 				tupdesc = TypeGetTupleDesc(functypeid, NIL);
-			else if (functyptype == 'p' && functypeid == RECORDOID)
+			else if (functypeid == RECORDOID)
 			{
-				if (!rsinfo || !IsA(rsinfo, ReturnSetInfo))
+				if (!rsinfo || !IsA(rsinfo, ReturnSetInfo) ||
+					rsinfo->expectedDesc == NULL)
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("function returning record called in context "
@@ -793,8 +789,6 @@ dblink_record(PG_FUNCTION_ARGS)
 		}
 
 		/* store needed metadata for subsequent calls */
-		slot = TupleDescGetSlot(tupdesc);
-		funcctx->slot = slot;
 		attinmeta = TupleDescGetAttInMetadata(tupdesc);
 		funcctx->attinmeta = attinmeta;
 
@@ -810,8 +804,6 @@ dblink_record(PG_FUNCTION_ARGS)
 	call_cntr = funcctx->call_cntr;
 	max_calls = funcctx->max_calls;
 
-	slot = funcctx->slot;
-
 	res = (PGresult *) funcctx->user_fctx;
 	attinmeta = funcctx->attinmeta;
 	tupdesc = attinmeta->tupdesc;
@@ -846,7 +838,7 @@ dblink_record(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = TupleGetDatum(slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
@@ -925,7 +917,7 @@ dblink_exec(PG_FUNCTION_ARGS)
 		/* need a tuple descriptor representing one TEXT column */
 		tupdesc = CreateTemplateTupleDesc(1, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 
 		/*
 		 * and save a copy of the command status string to return as our
@@ -939,7 +931,7 @@ dblink_exec(PG_FUNCTION_ARGS)
 		/* need a tuple descriptor representing one TEXT column */
 		tupdesc = CreateTemplateTupleDesc(1, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 
 		/*
 		 * and save a copy of the command status string to return as our
@@ -978,7 +970,6 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 	FuncCallContext *funcctx;
 	int32		call_cntr;
 	int32		max_calls;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	MemoryContext oldcontext;
 
@@ -1010,15 +1001,9 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 		 */
 		tupdesc = CreateTemplateTupleDesc(2, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "position",
-						   INT4OID, -1, 0, false);
+						   INT4OID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "colname",
-						   TEXTOID, -1, 0, false);
-
-		/* allocate a slot for a tuple with this tupdesc */
-		slot = TupleDescGetSlot(tupdesc);
-
-		/* assign slot to function context */
-		funcctx->slot = slot;
+						   TEXTOID, -1, 0);
 
 		/*
 		 * Generate attribute metadata needed later to produce tuples from
@@ -1053,8 +1038,6 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 	call_cntr = funcctx->call_cntr;
 	max_calls = funcctx->max_calls;
 
-	slot = funcctx->slot;
-
 	results = (char **) funcctx->user_fctx;
 	attinmeta = funcctx->attinmeta;
 
@@ -1075,7 +1058,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = TupleGetDatum(slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/contrib/intagg/int_aggregate.c b/contrib/intagg/int_aggregate.c
index 2bb06ff73a4a81c51cf6bd6d3ebccbf9789b5808..bd03f5c0c317e37cdfe29bcab219f85cfa4f63b3 100644
--- a/contrib/intagg/int_aggregate.c
+++ b/contrib/intagg/int_aggregate.c
@@ -25,7 +25,6 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
-#include "utils/sets.h"
 #include "utils/syscache.h"
 #include "access/tupmacs.h"
 #include "access/xact.h"
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index abc2249aedfcd3c805c2524d157afb8c0ea9a393..ca082618568d182b6f178a4cf53daf4e81415868 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.13 2003/11/29 19:51:35 pgsql Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.14 2004/04/01 21:28:43 tgl Exp $
  *
  * Copyright (c) 2001,2002	Tatsuo Ishii
  *
@@ -111,7 +111,6 @@ pgstattuple_real(Relation rel)
 	uint64		free_space = 0; /* free/reusable space in bytes */
 	double		free_percent;	/* free/reusable space in % */
 	TupleDesc	tupdesc;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	char	  **values;
 	int			i;
@@ -122,9 +121,6 @@ pgstattuple_real(Relation rel)
 	 */
 	tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
 
-	/* allocate a slot for a tuple with this tupdesc */
-	slot = TupleDescGetSlot(tupdesc);
-
 	/*
 	 * Generate attribute metadata needed later to produce tuples from raw
 	 * C strings
@@ -192,7 +188,7 @@ pgstattuple_real(Relation rel)
 	}
 
 	/*
-	 * Prepare a values array for storage in our slot. This should be an
+	 * Prepare a values array for constructing the tuple. This should be an
 	 * array of C strings which will be processed later by the appropriate
 	 * "in" functions.
 	 */
@@ -214,7 +210,7 @@ pgstattuple_real(Relation rel)
 	tuple = BuildTupleFromCStrings(attinmeta, values);
 
 	/* make the tuple into a datum */
-	result = TupleGetDatum(slot, tuple);
+	result = HeapTupleGetDatum(tuple);
 
 	/* Clean up */
 	for (i = 0; i < NCOLUMNS; i++)
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 622164b91b551320aeaee1570bd395ad43ee88ff..3eccebf476f767fde76a6cb40a7c235849bf75a0 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -351,7 +351,6 @@ crosstab(PG_FUNCTION_ARGS)
 	TupleDesc	ret_tupdesc;
 	int			call_cntr;
 	int			max_calls;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	SPITupleTable *spi_tuptable = NULL;
 	TupleDesc	spi_tupdesc;
@@ -429,10 +428,10 @@ crosstab(PG_FUNCTION_ARGS)
 
 		if (functyptype == 'c')
 		{
-			/* Build a tuple description for a functypeid tuple */
+			/* Build a tuple description for a named composite type */
 			tupdesc = TypeGetTupleDesc(functypeid, NIL);
 		}
-		else if (functyptype == 'p' && functypeid == RECORDOID)
+		else if (functypeid == RECORDOID)
 		{
 			if (fcinfo->nargs != 2)
 				ereport(ERROR,
@@ -461,12 +460,6 @@ crosstab(PG_FUNCTION_ARGS)
 					 errmsg("return and sql tuple descriptions are " \
 							"incompatible")));
 
-		/* allocate a slot for a tuple with this tupdesc */
-		slot = TupleDescGetSlot(tupdesc);
-
-		/* assign slot to function context */
-		funcctx->slot = slot;
-
 		/*
 		 * Generate attribute metadata needed later to produce tuples from
 		 * raw C strings
@@ -499,9 +492,6 @@ crosstab(PG_FUNCTION_ARGS)
 	call_cntr = funcctx->call_cntr;
 	max_calls = funcctx->max_calls;
 
-	/* return slot for our tuple */
-	slot = funcctx->slot;
-
 	/* user context info */
 	fctx = (crosstab_fctx *) funcctx->user_fctx;
 	lastrowid = fctx->lastrowid;
@@ -621,7 +611,7 @@ crosstab(PG_FUNCTION_ARGS)
 				tuple = BuildTupleFromCStrings(attinmeta, values);
 
 				/* make the tuple into a datum */
-				result = TupleGetDatum(slot, tuple);
+				result = HeapTupleGetDatum(tuple);
 
 				/* Clean up */
 				for (i = 0; i < num_categories + 1; i++)
@@ -1675,7 +1665,7 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_categories)
 	strcpy(attname, "rowname");
 
 	TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid,
-					   -1, 0, false);
+					   -1, 0);
 
 	/* now the category values columns */
 	sql_attr = spi_tupdesc->attrs[2];
@@ -1687,7 +1677,7 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_categories)
 
 		sprintf(attname, "category_%d", i + 1);
 		TupleDescInitEntry(tupdesc, attnum, attname, sql_atttypid,
-						   -1, 0, false);
+						   -1, 0);
 	}
 
 	return tupdesc;
diff --git a/contrib/tsearch2/ts_stat.c b/contrib/tsearch2/ts_stat.c
index 732f25b6bd1638f301a3558b7d0d195774ae5279..a6518e34396d9845118ad20ba62255009109ad0f 100644
--- a/contrib/tsearch2/ts_stat.c
+++ b/contrib/tsearch2/ts_stat.c
@@ -303,7 +303,6 @@ ts_setup_firstcall(FuncCallContext *funcctx, tsstat * stat)
 	memcpy(st->stat, stat, stat->len);
 	funcctx->user_fctx = (void *) st;
 	tupdesc = RelationNameGetTupleDesc("statinfo");
-	funcctx->slot = TupleDescGetSlot(tupdesc);
 	funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -334,7 +333,7 @@ ts_process_call(FuncCallContext *funcctx)
 		(values[0])[entry->len] = '\0';
 
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = TupleGetDatum(funcctx->slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		pfree(values[0]);
 		st->cur++;
diff --git a/contrib/tsearch2/wparser.c b/contrib/tsearch2/wparser.c
index b7e45e51885f449d9160e46721cb43bf62068bf4..9c3c4430480d2329596c10c5d6d45de3a9de43c9 100644
--- a/contrib/tsearch2/wparser.c
+++ b/contrib/tsearch2/wparser.c
@@ -187,7 +187,6 @@ setup_firstcall(FuncCallContext *funcctx, Oid prsid)
 		);
 	funcctx->user_fctx = (void *) st;
 	tupdesc = RelationNameGetTupleDesc("tokentype");
-	funcctx->slot = TupleDescGetSlot(tupdesc);
 	funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -211,7 +210,7 @@ process_call(FuncCallContext *funcctx)
 		values[2] = st->list[st->cur].descr;
 
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = TupleGetDatum(funcctx->slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		pfree(values[1]);
 		pfree(values[2]);
@@ -391,7 +390,6 @@ prs_setup_firstcall(FuncCallContext *funcctx, int prsid, text *txt)
 
 	funcctx->user_fctx = (void *) st;
 	tupdesc = RelationNameGetTupleDesc("tokenout");
-	funcctx->slot = TupleDescGetSlot(tupdesc);
 	funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -413,7 +411,7 @@ prs_process_call(FuncCallContext *funcctx)
 		sprintf(tid, "%d", st->list[st->cur].type);
 		values[1] = st->list[st->cur].lexem;
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = TupleGetDatum(funcctx->slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		pfree(values[1]);
 		st->cur++;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 196fdc6efdd761b341283b81f57876bd383249c8..0cdbdcfb7b52e042bed9fe525fc5a7b6dfcda6a2 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.84 2004/02/15 21:01:38 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.85 2004/04/01 21:28:43 tgl Exp $
  -->
 
 <chapter id="catalogs">
@@ -809,24 +809,6 @@
       </entry>
      </row>
 
-     <row>
-      <entry><structfield>attisset</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry></entry>
-      <entry>
-       If true, this attribute is a set.  In that case, what is really
-       stored in the attribute is the OID of a row in the
-       <structname>pg_proc</structname> catalog.  The
-       <structname>pg_proc</structname> row contains the query
-       string that defines this set, i.e., the query to run to get
-       the set.  So the <structfield>atttypid</structfield> (see
-       above) refers to the type returned by this query, but the
-       actual length of this attribute is the length (size) of an
-       <type>oid</type>.  --- At least this is the theory.  All this
-       is probably quite broken these days.
-      </entry>
-     </row>
-
      <row>
       <entry><structfield>attalign</structfield></entry>
       <entry><type>char</type></entry>
diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml
index c87a136bdd471fe89a8a1e4ef6595fdf45ecdd84..4018c2e3e1ba68efbfdf60d24936ceb74d7777ca 100644
--- a/doc/src/sgml/spi.sgml
+++ b/doc/src/sgml/spi.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.33 2004/03/17 01:05:10 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.34 2004/04/01 21:28:43 tgl Exp $
 -->
 
 <chapter id="spi">
@@ -1953,8 +1953,7 @@ char * SPI_getrelname(Relation <parameter>rel</parameter>)
    allocations made by <function>palloc</function>,
    <function>repalloc</function>, or SPI utility functions (except for
    <function>SPI_copytuple</function>,
-   <function>SPI_copytupledesc</function>,
-   <function>SPI_copytupleintoslot</function>,
+   <function>SPI_returntuple</function>,
    <function>SPI_modifytuple</function>, and
    <function>SPI_palloc</function>) are made in this context.  When a
    procedure disconnects from the SPI manager (via
@@ -2169,7 +2168,9 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
 
   <para>
    <function>SPI_copytuple</function> makes a copy of a row in the
-   upper executor context.
+   upper executor context.  This is normally used to return a modified
+   row from a trigger.  In a function declared to return a composite
+   type, use <function>SPI_returntuple</function> instead.
   </para>
  </refsect1>
 
@@ -2200,21 +2201,21 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
 
 <!-- *********************************************** -->
 
-<refentry id="spi-spi-copytupledesc">
+<refentry id="spi-spi-returntuple">
  <refmeta>
-  <refentrytitle>SPI_copytupledesc</refentrytitle>
+  <refentrytitle>SPI_returntuple</refentrytitle>
  </refmeta>
 
  <refnamediv>
-  <refname>SPI_copytupledesc</refname>
-  <refpurpose>make a copy of a row descriptor in the upper executor context</refpurpose>
+  <refname>SPI_returntuple</refname>
+  <refpurpose>prepare to return a tuple as a Datum</refpurpose>
  </refnamediv>
 
- <indexterm><primary>SPI_copytupledesc</primary></indexterm>
+ <indexterm><primary>SPI_returntuple</primary></indexterm>
 
  <refsynopsisdiv>
 <synopsis>
-TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
+HeapTupleHeader SPI_returntuple(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
 </synopsis>
  </refsynopsisdiv>
 
@@ -2222,63 +2223,16 @@ TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
   <title>Description</title>
 
   <para>
-   <function>SPI_copytupledesc</function> makes a copy of a row
-   descriptor in the upper executor context.
+   <function>SPI_returntuple</function> makes a copy of a row in
+   the upper executor context, returning it in the form of a rowtype Datum.
+   The returned pointer need only be converted to Datum via PointerGetDatum
+   before returning.
   </para>
- </refsect1>
-
- <refsect1>
-  <title>Arguments</title>
-
-  <variablelist>
-   <varlistentry>
-    <term><literal>TupleDesc <parameter>tupdesc</parameter></literal></term>
-    <listitem>
-     <para>
-      row descriptor to be copied
-     </para>
-    </listitem>
-   </varlistentry>
-  </variablelist>
- </refsect1>
-
- <refsect1>
-  <title>Return Value</title>
-
-  <para>
-   the copied row descriptor; <symbol>NULL</symbol> only if
-   <parameter>tupdesc</parameter> is <symbol>NULL</symbol>
-  </para>
- </refsect1>
-</refentry>
-
-<!-- *********************************************** -->
-
-<refentry id="spi-spi-copytupleintoslot">
- <refmeta>
-  <refentrytitle>SPI_copytupleintoslot</refentrytitle>
- </refmeta>
-
- <refnamediv>
-  <refname>SPI_copytupleintoslot</refname>
-  <refpurpose>make a copy of a row and descriptor in the upper executor context</refpurpose>
- </refnamediv>
-
- <indexterm><primary>SPI_copytupleintoslot</primary></indexterm>
-
- <refsynopsisdiv>
-<synopsis>
-TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
-</synopsis>
- </refsynopsisdiv>
-
- <refsect1>
-  <title>Description</title>
 
   <para>
-   <function>SPI_copytupleintoslot</function> makes a copy of a row in
-   the upper executor context, returning it in the form of a filled-in
-   <type>TupleTableSlot</type> structure.
+   Note that this should be used for functions that are declared to return
+   composite types.  It is not used for triggers; use
+   <function>SPI_copytuple</> for returning a modified row in a trigger.
   </para>
  </refsect1>
 
@@ -2299,7 +2253,8 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
     <term><literal>TupleDesc <parameter>rowdesc</parameter></literal></term>
     <listitem>
      <para>
-      row descriptor to be copied
+      descriptor for row (pass the same descriptor each time for most
+      effective caching)
      </para>
     </listitem>
    </varlistentry>
@@ -2310,9 +2265,9 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
   <title>Return Value</title>
 
   <para>
-   <type>TupleTableSlot</type> containing the copied row and
-   descriptor; <symbol>NULL</symbol> only if
-   <parameter>row</parameter> or <parameter>rowdesc</parameter> are
+   <type>HeapTupleHeader</type> pointing to copied row;
+   <symbol>NULL</symbol> only if
+   <parameter>row</parameter> or <parameter>rowdesc</parameter> is
    <symbol>NULL</symbol>
   </para>
  </refsect1>
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index c8f71296857b6dc481674362355c603ec6329c3d..4b06aefd362eb9413260df3f628c4fc80e66c0dd 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.80 2004/03/09 16:57:47 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.81 2004/04/01 21:28:43 tgl Exp $
 -->
 
  <sect1 id="xfunc">
@@ -1623,7 +1623,7 @@ SELECT name, c_overpaid(emp, 1500) AS overpaid
 #include "executor/executor.h"  /* for GetAttributeByName() */
 
 bool
-c_overpaid(TupleTableSlot *t, /* the current row of emp */
+c_overpaid(HeapTupleHeader t, /* the current row of emp */
            int32 limit)
 {
     bool isnull;
@@ -1647,7 +1647,7 @@ PG_FUNCTION_INFO_V1(c_overpaid);
 Datum
 c_overpaid(PG_FUNCTION_ARGS)
 {
-    TupleTableSlot  *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
+    HeapTupleHeader  t = PG_GETARG_HEAPTUPLEHEADER(0);
     int32            limit = PG_GETARG_INT32(1);
     bool isnull;
     int32 salary;
@@ -1666,7 +1666,8 @@ c_overpaid(PG_FUNCTION_ARGS)
      <function>GetAttributeByName</function> is the 
      <productname>PostgreSQL</productname> system function that
      returns attributes out of the specified row.  It has
-     three arguments: the argument of type <type>TupleTableSlot*</type> passed into
+     three arguments: the argument of type <type>HeapTupleHeader</type> passed
+     into 
      the  function, the name of the desired attribute, and a
      return parameter that tells whether  the  attribute
      is  null.   <function>GetAttributeByName</function> returns a <type>Datum</type>
@@ -1674,6 +1675,11 @@ c_overpaid(PG_FUNCTION_ARGS)
      appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function> macro.
     </para>
 
+    <para>
+     There is also <function>GetAttributeByNum</function>, which selects
+     the target attribute by column number instead of name.
+    </para>
+
     <para>
      The following command declares the function
      <function>c_overpaid</function> in SQL:
@@ -1681,8 +1687,11 @@ c_overpaid(PG_FUNCTION_ARGS)
 <programlisting>
 CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
     AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_overpaid'
-    LANGUAGE C;
+    LANGUAGE C STRICT;
 </programlisting>
+
+     Notice we have used <literal>STRICT</> so that we did not have to
+     check whether the input arguments were NULL.
     </para>
    </sect2>
 
@@ -1700,38 +1709,25 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
     </para>
 
     <para>
-     The support for returning composite data types (or rows) starts
-     with the <structname>AttInMetadata</> structure. This structure
-     holds arrays of individual attribute information needed to create
-     a row from raw C strings. The information contained in the
-     structure is derived from a <structname>TupleDesc</> structure,
-     but it is stored to avoid redundant computations on each call to
-     a set-returning function (see next section).  In the case of a
-     function returning a set, the <structname>AttInMetadata</>
-     structure should be computed once during the first call and saved
-     for reuse in later calls.  <structname>AttInMetadata</> also
-     saves a pointer to the original <structname>TupleDesc</>.
-<programlisting>
-typedef struct AttInMetadata
-{
-    /* full TupleDesc */
-    TupleDesc       tupdesc;
-
-    /* array of attribute type input function finfo */
-    FmgrInfo       *attinfuncs;
-
-    /* array of attribute type typelem */
-    Oid            *attelems;
-
-    /* array of attribute typmod */
-    int32    	   *atttypmods;
-}	AttInMetadata;
-</programlisting>
+     There are two ways you can build a composite data value (henceforth
+     a <quote>tuple</>): you can build it from an array of Datum values,
+     or from an array of C strings that can be passed to the input
+     conversion functions of the tuple's column datatypes.  In either
+     case, you first need to obtain or construct a <structname>TupleDesc</>
+     descriptor for the tuple structure.  When working with Datums, you
+     pass the <structname>TupleDesc</> to <function>BlessTupleDesc</>,
+     and then call <function>heap_formtuple</> for each row.  When working
+     with C strings, you pass the <structname>TupleDesc</> to
+     <function>TupleDescGetAttInMetadata</>, and then call
+     <function>BuildTupleFromCStrings</> for each row.  In the case of a
+     function returning a set of tuples, the setup steps can all be done
+     once during the first call of the function.
     </para>
 
     <para>
-     To assist you in populating this structure, several functions and a macro
-     are available. Use
+     Several helper functions are available for setting up the initial
+     <structname>TupleDesc</>.  If you want to use a named composite type,
+     you can fetch the information from the system catalogs.  Use
 <programlisting>
 TupleDesc RelationNameGetTupleDesc(const char *relname)
 </programlisting>
@@ -1741,36 +1737,43 @@ TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
 </programlisting>
      to get a <structname>TupleDesc</> based on a type OID. This can
      be used to get a <structname>TupleDesc</> for a base or
-     composite type. Then
+     composite type.  When writing a function that returns
+     <structname>record</>, the expected <structname>TupleDesc</> 
+     must be passed in by the caller.
+    </para>
+
+    <para>
+     Once you have a <structname>TupleDesc</>, call
+<programlisting>
+TupleDesc BlessTupleDesc(TupleDesc tupdesc)
+</programlisting>
+     if you plan to work with Datums, or
 <programlisting>
 AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
 </programlisting>
-     will return a pointer to an <structname>AttInMetadata</>,
-     initialized based on the given
-     <structname>TupleDesc</>. <structname>AttInMetadata</> can be
-     used in conjunction with C strings to produce a properly formed
-     row value (internally called tuple).
+     if you plan to work with C strings.  If you are writing a function
+     returning set, you can save the results of these functions in the
+     <structname>FuncCallContext</> structure --- use the
+     <structfield>tuple_desc</> or <structfield>attinmeta</> field
+     respectively.
     </para>
 
     <para>
-     To return a tuple you must create a tuple slot based on the
-     <structname>TupleDesc</>. You can use
+     When working with Datums, use
 <programlisting>
-TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
+HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls)
 </programlisting>
-     to initialize this tuple slot, or obtain one through other (user provided)
-     means. The tuple slot is needed to create a <type>Datum</> for return by the
-     function.  The same slot can (and should) be reused on each call.
+     to build a <structname>HeapTuple</> given user data in Datum form.
     </para>
 
     <para>
-     After constructing an <structname>AttInMetadata</> structure,
+     When working with C strings, use
 <programlisting>
 HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
 </programlisting>
-     can be used to build a <structname>HeapTuple</> given user data
-     in C string form.  <literal>values</literal> is an array of C strings, one for
-     each attribute of the return row. Each C string should be in
+     to build a <structname>HeapTuple</> given user data
+     in C string form.  <literal>values</literal> is an array of C strings,
+     one for each attribute of the return row. 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 <parameter>values</> array
@@ -1778,25 +1781,13 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
      be called again for each row you return.
     </para>
 
-    <para>
-     Building a tuple via <function>TupleDescGetAttInMetadata</> and
-     <function>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
-     <type>Datum</> values, you should instead use the underlying
-     function <function>heap_formtuple</> to convert the
-     <type>Datum</type> values directly into a tuple.  You will still need
-     the <structname>TupleDesc</> and a <structname>TupleTableSlot</>,
-     but not <structname>AttInMetadata</>.
-    </para>
-
     <para>
      Once you have built a tuple to return from your function, it
      must be converted into a <type>Datum</>. Use
 <programlisting>
-TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
+HeapTupleGetDatum(HeapTuple tuple)
 </programlisting>
-     to get a <type>Datum</> given a tuple and a slot.  This
+     to convert a <structname>HeapTuple</> into a valid Datum.  This
      <type>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.
@@ -1851,8 +1842,8 @@ typedef struct
     /*
      * OPTIONAL pointer to result slot
      * 
-     * slot is for use when returning tuples (i.e., composite data types)
-     * and is not needed when returning base data types.
+     * This is obsolete and only present for backwards compatibility, viz,
+     * user-defined SRFs that use the deprecated TupleDescGetSlot().
      */
     TupleTableSlot *slot;
 
@@ -1868,9 +1859,9 @@ typedef struct
      * OPTIONAL pointer to struct containing attribute type input metadata
      * 
      * attinmeta is for use when returning tuples (i.e., composite data types)
-     * and is not needed when returning base data types. It
-     * is only needed if you intend to use BuildTupleFromCStrings() to create
-     * the return tuple.
+     * and is not used when returning base data types. It is only needed
+     * if you intend to use BuildTupleFromCStrings() to create the return
+     * tuple.
      */
     AttInMetadata *attinmeta;
 
@@ -1883,6 +1874,18 @@ typedef struct
      * of the SRF.
      */
     MemoryContext multi_call_memory_ctx;
+
+    /*
+     * OPTIONAL pointer to struct containing tuple description
+     *
+     * tuple_desc is for use when returning tuples (i.e. composite data types)
+     * and is only needed if you are going to build the tuples with
+     * heap_formtuple() rather than with BuildTupleFromCStrings().  Note that
+     * the TupleDesc pointer stored here should usually have been run through
+     * BlessTupleDesc() first.
+     */
+    TupleDesc tuple_desc;
+
 } FuncCallContext;
 </programlisting>
     </para>
@@ -1956,8 +1959,6 @@ my_set_returning_function(PG_FUNCTION_ARGS)
         <replaceable>user code</replaceable>
         <replaceable>if returning composite</replaceable>
             <replaceable>build TupleDesc, and perhaps AttInMetadata</replaceable>
-            <replaceable>obtain slot</replaceable>
-            funcctx-&gt;slot = slot;
         <replaceable>endif returning composite</replaceable>
         <replaceable>user code</replaceable>
         MemoryContextSwitchTo(oldcontext);
@@ -1998,7 +1999,6 @@ testpassbyval(PG_FUNCTION_ARGS)
     int                  call_cntr;
     int                  max_calls;
     TupleDesc            tupdesc;
-    TupleTableSlot      *slot;
     AttInMetadata       *attinmeta;
 
      /* stuff done only on the first call of the function */
@@ -2018,12 +2018,6 @@ testpassbyval(PG_FUNCTION_ARGS)
         /* Build a tuple description for a __testpassbyval tuple */
         tupdesc = RelationNameGetTupleDesc("__testpassbyval");
 
-        /* allocate a slot for a tuple with this tupdesc */
-        slot = TupleDescGetSlot(tupdesc);
-
-        /* assign slot to function context */
-        funcctx-&gt;slot = slot;
-
         /*
          * generate attribute metadata needed later to produce tuples from raw
          * C strings
@@ -2039,7 +2033,6 @@ testpassbyval(PG_FUNCTION_ARGS)
 
     call_cntr = funcctx-&gt;call_cntr;
     max_calls = funcctx-&gt;max_calls;
-    slot = funcctx-&gt;slot;
     attinmeta = funcctx-&gt;attinmeta;
  
     if (call_cntr &lt; max_calls)    /* do when there is more left to send */
@@ -2049,7 +2042,7 @@ testpassbyval(PG_FUNCTION_ARGS)
         Datum        result;
 
         /*
-         * Prepare a values array for storage in our slot.
+         * Prepare a values array for building the returned tuple.
          * This should be an array of C strings which will
          * be processed later by the type input functions.
          */
@@ -2066,7 +2059,7 @@ testpassbyval(PG_FUNCTION_ARGS)
         tuple = BuildTupleFromCStrings(attinmeta, values);
 
         /* make the tuple into a datum */
-        result = TupleGetDatum(slot, tuple);
+        result = HeapTupleGetDatum(tuple);
 
         /* clean up (this is not really necessary) */
         pfree(values[0]);
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index f1b20ff27323436f4c4ee2984ecafe937ae1c03f..4355c0df4c704b744daca974b92f3c445dbbfaef 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.89 2004/01/16 20:51:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.90 2004/04/01 21:28:43 tgl Exp $
  *
  * NOTES
  *	  The old interface functions have been converted to macros
@@ -31,6 +31,8 @@
 
 /* ----------------
  *		ComputeDataSize
+ *
+ * Determine size of the data area of a tuple to be constructed
  * ----------------
  */
 Size
@@ -417,7 +419,7 @@ nocachegetattr(HeapTuple tuple,
  * ----------------
  */
 Datum
-heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
+heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
 {
 	Datum		result;
 
@@ -451,6 +453,31 @@ heap_getsysattr(HeapTuple tup, int attnum, bool *isnull)
 		case TableOidAttributeNumber:
 			result = ObjectIdGetDatum(tup->t_tableOid);
 			break;
+
+			/*
+			 * If the attribute number is 0, then we are supposed to return
+			 * the entire tuple as a row-type Datum.  (Using zero for this
+			 * purpose is unclean since it risks confusion with "invalid attr"
+			 * result codes, but it's not worth changing now.)
+			 *
+			 * We have to make a copy of the tuple so we can safely insert the
+			 * Datum overhead fields, which are not set in on-disk tuples.
+			 */
+		case InvalidAttrNumber:
+			{
+				HeapTupleHeader	dtup;
+
+				dtup = (HeapTupleHeader) palloc(tup->t_len);
+				memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
+
+				HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
+				HeapTupleHeaderSetTypeId(dtup, tupleDesc->tdtypeid);
+				HeapTupleHeaderSetTypMod(dtup, tupleDesc->tdtypmod);
+
+				result = PointerGetDatum(dtup);
+			}
+			break;
+
 		default:
 			elog(ERROR, "invalid attnum: %d", attnum);
 			result = 0;			/* keep compiler quiet */
@@ -547,19 +574,10 @@ heap_deformtuple(HeapTuple tuple,
 /* ----------------
  *		heap_formtuple
  *
- *		constructs a tuple from the given *value and *null arrays
- *
- * old comments
- *		Handles alignment by aligning 2 byte attributes on short boundries
- *		and 3 or 4 byte attributes on long word boundries on a vax; and
- *		aligning non-byte attributes on short boundries on a sun.  Does
- *		not properly align fixed length arrays of 1 or 2 byte types (yet).
+ *		constructs a tuple from the given *value and *nulls arrays
  *
  *		Null attributes are indicated by a 'n' in the appropriate byte
- *		of the *null.	Non-null attributes are indicated by a ' ' (space).
- *
- *		Fix me.  (Figure that must keep context if debug--allow give oid.)
- *		Assumes in order.
+ *		of *nulls.	Non-null attributes are indicated by a ' ' (space).
  * ----------------
  */
 HeapTuple
@@ -581,6 +599,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
 				 errmsg("number of columns (%d) exceeds limit (%d)",
 						numberOfAttributes, MaxTupleAttributeNumber)));
 
+	/*
+	 * Determine total space needed
+	 */
 	for (i = 0; i < numberOfAttributes; i++)
 	{
 		if (nulls[i] != ' ')
@@ -602,15 +623,26 @@ heap_formtuple(TupleDesc tupleDescriptor,
 
 	len += ComputeDataSize(tupleDescriptor, value, nulls);
 
-	tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+	/*
+	 * Allocate and zero the space needed.  Note that the tuple body and
+	 * HeapTupleData management structure are allocated in one chunk.
+	 */
+	tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
 	tuple->t_datamcxt = CurrentMemoryContext;
-	td = tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
-
-	MemSet((char *) td, 0, len);
+	tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
 
+	/*
+	 * And fill in the information.  Note we fill the Datum fields even
+	 * though this tuple may never become a Datum.
+	 */
 	tuple->t_len = len;
 	ItemPointerSetInvalid(&(tuple->t_self));
 	tuple->t_tableOid = InvalidOid;
+
+	HeapTupleHeaderSetDatumLength(td, len);
+	HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
+	HeapTupleHeaderSetTypMod(td, tupleDescriptor->tdtypmod);
+
 	td->t_natts = numberOfAttributes;
 	td->t_hoff = hoff;
 
@@ -759,15 +791,15 @@ heap_addheader(int natts,		/* max domain index */
 	hoff = MAXALIGN(hoff);
 	len = hoff + structlen;
 
-	tuple = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+	tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + len);
+	tuple->t_datamcxt = CurrentMemoryContext;
+	tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
 
 	tuple->t_len = len;
 	ItemPointerSetInvalid(&(tuple->t_self));
 	tuple->t_tableOid = InvalidOid;
-	tuple->t_datamcxt = CurrentMemoryContext;
-	tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
 
-	MemSet((char *) td, 0, hoff);
+	/* we don't bother to fill the Datum fields */
 
 	td->t_natts = natts;
 	td->t_hoff = hoff;
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 614cb00f5c2532930d0a52678eba00964c751ec5..1a016d86f80302631e542faea5c11449ba7df802 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.101 2003/11/29 19:51:39 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.102 2004/04/01 21:28:43 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -28,12 +28,16 @@
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /* ----------------------------------------------------------------
  *		CreateTemplateTupleDesc
  *
  *		This function allocates and zeros a tuple descriptor structure.
+ *
+ * Tuple type ID information is initially set for an anonymous record type;
+ * caller can overwrite this if needed.
  * ----------------------------------------------------------------
  */
 TupleDesc
@@ -47,24 +51,26 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
 	AssertArg(natts >= 0);
 
 	/*
-	 * allocate enough memory for the tuple descriptor and zero it as
-	 * TupleDescInitEntry assumes that the descriptor is filled with NULL
-	 * pointers.
+	 * Allocate enough memory for the tuple descriptor, and zero the
+	 * attrs[] array since TupleDescInitEntry assumes that the array
+	 * is filled with NULL pointers.
 	 */
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 
-	desc->natts = natts;
-	desc->tdhasoid = hasoid;
-
 	if (natts > 0)
-	{
-		uint32		size = natts * sizeof(Form_pg_attribute);
-
-		desc->attrs = (Form_pg_attribute *) palloc0(size);
-	}
+		desc->attrs = (Form_pg_attribute *)
+			palloc0(natts * sizeof(Form_pg_attribute));
 	else
 		desc->attrs = NULL;
+
+	/*
+	 * Initialize other fields of the tupdesc.
+	 */
+	desc->natts = natts;
 	desc->constr = NULL;
+	desc->tdtypeid = RECORDOID;
+	desc->tdtypmod = -1;
+	desc->tdhasoid = hasoid;
 
 	return desc;
 }
@@ -74,6 +80,9 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
  *
  *		This function allocates a new TupleDesc pointing to a given
  *		Form_pg_attribute array
+ *
+ * Tuple type ID information is initially set for an anonymous record type;
+ * caller can overwrite this if needed.
  * ----------------------------------------------------------------
  */
 TupleDesc
@@ -90,6 +99,8 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
 	desc->attrs = attrs;
 	desc->natts = natts;
 	desc->constr = NULL;
+	desc->tdtypeid = RECORDOID;
+	desc->tdtypmod = -1;
 	desc->tdhasoid = hasoid;
 
 	return desc;
@@ -101,22 +112,21 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
  *		This function creates a new TupleDesc by copying from an existing
  *		TupleDesc
  *
- *		!!! Constraints are not copied !!!
+ *		!!! Constraints and defaults are not copied !!!
  * ----------------------------------------------------------------
  */
 TupleDesc
 CreateTupleDescCopy(TupleDesc tupdesc)
 {
 	TupleDesc	desc;
-	int			i,
-				size;
+	int			i;
 
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 	desc->natts = tupdesc->natts;
 	if (desc->natts > 0)
 	{
-		size = desc->natts * sizeof(Form_pg_attribute);
-		desc->attrs = (Form_pg_attribute *) palloc(size);
+		desc->attrs = (Form_pg_attribute *)
+			palloc(desc->natts * sizeof(Form_pg_attribute));
 		for (i = 0; i < desc->natts; i++)
 		{
 			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -127,7 +137,11 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 	}
 	else
 		desc->attrs = NULL;
+
 	desc->constr = NULL;
+
+	desc->tdtypeid = tupdesc->tdtypeid;
+	desc->tdtypmod = tupdesc->tdtypmod;
 	desc->tdhasoid = tupdesc->tdhasoid;
 
 	return desc;
@@ -137,7 +151,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
  *		CreateTupleDescCopyConstr
  *
  *		This function creates a new TupleDesc by copying from an existing
- *		TupleDesc (with Constraints)
+ *		TupleDesc (including its constraints and defaults)
  * ----------------------------------------------------------------
  */
 TupleDesc
@@ -145,15 +159,14 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 {
 	TupleDesc	desc;
 	TupleConstr *constr = tupdesc->constr;
-	int			i,
-				size;
+	int			i;
 
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 	desc->natts = tupdesc->natts;
 	if (desc->natts > 0)
 	{
-		size = desc->natts * sizeof(Form_pg_attribute);
-		desc->attrs = (Form_pg_attribute *) palloc(size);
+		desc->attrs = (Form_pg_attribute *)
+			palloc(desc->natts * sizeof(Form_pg_attribute));
 		for (i = 0; i < desc->natts; i++)
 		{
 			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
@@ -162,9 +175,10 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 	}
 	else
 		desc->attrs = NULL;
+
 	if (constr)
 	{
-		TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
+		TupleConstr *cpy = (TupleConstr *) palloc0(sizeof(TupleConstr));
 
 		cpy->has_not_null = constr->has_not_null;
 
@@ -197,10 +211,16 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 	else
 		desc->constr = NULL;
 
+	desc->tdtypeid = tupdesc->tdtypeid;
+	desc->tdtypmod = tupdesc->tdtypmod;
 	desc->tdhasoid = tupdesc->tdhasoid;
+
 	return desc;
 }
 
+/*
+ * Free a TupleDesc including all substructure
+ */
 void
 FreeTupleDesc(TupleDesc tupdesc)
 {
@@ -244,6 +264,10 @@ FreeTupleDesc(TupleDesc tupdesc)
 
 /*
  * Compare two TupleDesc structures for logical equality
+ *
+ * Note: we deliberately do not check the attrelid and tdtypmod fields.
+ * This allows typcache.c to use this routine to see if a cached record type
+ * matches a requested type, and is harmless for relcache.c's uses.
  */
 bool
 equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
@@ -254,8 +278,11 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 
 	if (tupdesc1->natts != tupdesc2->natts)
 		return false;
+	if (tupdesc1->tdtypeid != tupdesc2->tdtypeid)
+		return false;
 	if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
 		return false;
+
 	for (i = 0; i < tupdesc1->natts; i++)
 	{
 		Form_pg_attribute attr1 = tupdesc1->attrs[i];
@@ -265,6 +292,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 		 * We do not need to check every single field here: we can
 		 * disregard attrelid, attnum (it was used to place the row in the
 		 * attrs array) and everything derived from the column datatype.
+		 * Also, attcacheoff must NOT be checked since it's possibly not
+		 * set in both copies.
 		 */
 		if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)
 			return false;
@@ -272,6 +301,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 			return false;
 		if (attr1->attstattarget != attr2->attstattarget)
 			return false;
+		if (attr1->attndims != attr2->attndims)
+			return false;
 		if (attr1->atttypmod != attr2->atttypmod)
 			return false;
 		if (attr1->attstorage != attr2->attstorage)
@@ -287,6 +318,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
 		if (attr1->attinhcount != attr2->attinhcount)
 			return false;
 	}
+
 	if (tupdesc1->constr != NULL)
 	{
 		TupleConstr *constr1 = tupdesc1->constr;
@@ -360,8 +392,7 @@ TupleDescInitEntry(TupleDesc desc,
 				   const char *attributeName,
 				   Oid oidtypeid,
 				   int32 typmod,
-				   int attdim,
-				   bool attisset)
+				   int attdim)
 {
 	HeapTuple	tuple;
 	Form_pg_type typeForm;
@@ -403,7 +434,6 @@ TupleDescInitEntry(TupleDesc desc,
 
 	att->attnum = attributeNumber;
 	att->attndims = attdim;
-	att->attisset = attisset;
 
 	att->attnotnull = false;
 	att->atthasdef = false;
@@ -416,70 +446,13 @@ TupleDescInitEntry(TupleDesc desc,
 						   0, 0, 0);
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for type %u", oidtypeid);
-
-	/*
-	 * type info exists so we initialize our attribute information from
-	 * the type tuple we found..
-	 */
 	typeForm = (Form_pg_type) GETSTRUCT(tuple);
 
-	att->atttypid = HeapTupleGetOid(tuple);
-
-	/*
-	 * There are a couple of cases where we must override the information
-	 * stored in pg_type.
-	 *
-	 * First: if this attribute is a set, what is really stored in the
-	 * attribute is the OID of a tuple in the pg_proc catalog. The pg_proc
-	 * tuple contains the query string which defines this set - i.e., the
-	 * query to run to get the set. So the atttypid (just assigned above)
-	 * refers to the type returned by this query, but the actual length of
-	 * this attribute is the length (size) of an OID.
-	 *
-	 * (Why not just make the atttypid point to the OID type, instead of the
-	 * type the query returns?	Because the executor uses the atttypid to
-	 * tell the front end what type will be returned, and in the end the
-	 * type returned will be the result of the query, not an OID.)
-	 *
-	 * (Why not wait until the return type of the set is known (i.e., the
-	 * recursive call to the executor to execute the set has returned)
-	 * before telling the front end what the return type will be?  Because
-	 * the executor is a delicate thing, and making sure that the correct
-	 * order of front-end commands is maintained is messy, especially
-	 * considering that target lists may change as inherited attributes
-	 * are considered, etc.  Ugh.)
-	 *
-	 * Second: if we are dealing with a complex type (a tuple type), then
-	 * pg_type will say that the representation is the same as Oid.  But
-	 * if typmod is sizeof(Pointer) then the internal representation is
-	 * actually a pointer to a TupleTableSlot, and we have to substitute
-	 * that information.
-	 *
-	 * A set of complex type is first and foremost a set, so its
-	 * representation is Oid not pointer.  So, test that case first.
-	 */
-	if (attisset)
-	{
-		att->attlen = sizeof(Oid);
-		att->attbyval = true;
-		att->attalign = 'i';
-		att->attstorage = 'p';
-	}
-	else if (typeForm->typtype == 'c' && typmod == sizeof(Pointer))
-	{
-		att->attlen = sizeof(Pointer);
-		att->attbyval = true;
-		att->attalign = 'd';	/* kluge to work with 8-byte pointers */
-		/* XXX ought to have a separate attalign value for pointers ... */
-		att->attstorage = 'p';
-	}
-	else
-	{
-		att->attlen = typeForm->typlen;
-		att->attbyval = typeForm->typbyval;
-		att->attalign = typeForm->typalign;
-		att->attstorage = typeForm->typstorage;
-	}
+	att->atttypid = oidtypeid;
+	att->attlen = typeForm->typlen;
+	att->attbyval = typeForm->typbyval;
+	att->attalign = typeForm->typalign;
+	att->attstorage = typeForm->typstorage;
 
 	ReleaseSysCache(tuple);
 }
@@ -491,7 +464,8 @@ TupleDescInitEntry(TupleDesc desc,
  * Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
  *
  * Note: the default assumption is no OIDs; caller may modify the returned
- * TupleDesc if it wants OIDs.
+ * TupleDesc if it wants OIDs.  Also, tdtypeid will need to be filled in
+ * later on.
  */
 TupleDesc
 BuildDescForRelation(List *schema)
@@ -501,12 +475,11 @@ BuildDescForRelation(List *schema)
 	List	   *p;
 	TupleDesc	desc;
 	AttrDefault *attrdef = NULL;
-	TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+	TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
 	char	   *attname;
 	int32		atttypmod;
 	int			attdim;
 	int			ndef = 0;
-	bool		attisset;
 
 	/*
 	 * allocate a new tuple descriptor
@@ -529,13 +502,18 @@ BuildDescForRelation(List *schema)
 		attnum++;
 
 		attname = entry->colname;
-		attisset = entry->typename->setof;
 		atttypmod = entry->typename->typmod;
 		attdim = length(entry->typename->arrayBounds);
 
+		if (entry->typename->setof)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("column \"%s\" cannot be declared SETOF",
+							attname)));
+
 		TupleDescInitEntry(desc, attnum, attname,
 						   typenameTypeId(entry->typename),
-						   atttypmod, attdim, attisset);
+						   atttypmod, attdim);
 
 		/* Fill in additional stuff not handled by TupleDescInitEntry */
 		if (entry->is_not_null)
@@ -586,6 +564,7 @@ BuildDescForRelation(List *schema)
 		pfree(constr);
 		desc->constr = NULL;
 	}
+
 	return desc;
 }
 
@@ -603,7 +582,7 @@ RelationNameGetTupleDesc(const char *relname)
 	TupleDesc	tupdesc;
 	List	   *relname_list;
 
-	/* Open relation and get the tuple description */
+	/* Open relation and copy the tuple description */
 	relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
 	relvar = makeRangeVarFromNameList(relname_list);
 	rel = relation_openrv(relvar, AccessShareLock);
@@ -620,7 +599,8 @@ RelationNameGetTupleDesc(const char *relname)
  *
  * If the type is composite, *and* a colaliases List is provided, *and*
  * the List is of natts length, use the aliases instead of the relation
- * attnames.
+ * attnames.  (NB: this usage is deprecated since it may result in
+ * creation of unnecessary transient record types.)
  *
  * If the type is a base type, a single item alias List is required.
  */
@@ -635,22 +615,12 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 	 */
 	if (functyptype == 'c')
 	{
-		/* Composite data type, i.e. a table's row type */
-		Oid			relid = typeidTypeRelid(typeoid);
-		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);
-		/* XXX should we hold the lock to ensure table doesn't change? */
+		/* Composite data type, e.g. a table's row type */
+		tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(typeoid, -1));
 
 		if (colaliases != NIL)
 		{
+			int			natts = tupdesc->natts;
 			int			varattno;
 
 			/* does the list length match the number of attributes? */
@@ -667,6 +637,10 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 				if (label != NULL)
 					namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
 			}
+
+			/* The tuple type is now an anonymous record type */
+			tupdesc->tdtypeid = RECORDOID;
+			tupdesc->tdtypmod = -1;
 		}
 	}
 	else if (functyptype == 'b' || functyptype == 'd')
@@ -695,13 +669,15 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 						   attname,
 						   typeoid,
 						   -1,
-						   0,
-						   false);
+						   0);
 	}
-	else if (functyptype == 'p' && typeoid == RECORDOID)
+	else if (typeoid == RECORDOID)
+	{
+		/* XXX can't support this because typmod wasn't passed in ... */
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("could not determine row description for function returning record")));
+	}
 	else
 	{
 		/* crummy error message, but parser should have caught this */
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 902f0621377d89620d00aa6fd2c16d072ab1f634..695ef36b5098833c452e5cfc429f3b5ef90c844d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.163 2004/03/11 01:47:35 ishii Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.164 2004/04/01 21:28:43 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1116,6 +1116,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
 	tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmin(tup->t_data, cid);
+	HeapTupleHeaderSetCmax(tup->t_data, 0);	/* zero out Datum fields */
 	tup->t_tableOid = relation->rd_id;
 
 	/*
@@ -1576,6 +1577,7 @@ l2:
 	newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
 	HeapTupleHeaderSetXmin(newtup->t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmin(newtup->t_data, cid);
+	HeapTupleHeaderSetCmax(newtup->t_data, 0);	/* zero out Datum fields */
 
 	/*
 	 * If the toaster needs to be activated, OR if the new tuple will not
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index f18fece398251bda4c66cd869a73b286974fdd1b..c7fcb40087331dac925077bd0fc302635ed5ce38 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.177 2004/02/25 19:41:22 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.178 2004/04/01 21:28:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,34 +107,55 @@ struct typinfo
 	Oid			oid;
 	Oid			elem;
 	int16		len;
+	bool		byval;
+	char		align;
+	char		storage;
 	Oid			inproc;
 	Oid			outproc;
 };
 
-static struct typinfo Procid[] = {
-	{"bool", BOOLOID, 0, 1, F_BOOLIN, F_BOOLOUT},
-	{"bytea", BYTEAOID, 0, -1, F_BYTEAIN, F_BYTEAOUT},
-	{"char", CHAROID, 0, 1, F_CHARIN, F_CHAROUT},
-	{"name", NAMEOID, 0, NAMEDATALEN, F_NAMEIN, F_NAMEOUT},
-	{"int2", INT2OID, 0, 2, F_INT2IN, F_INT2OUT},
-	{"int2vector", INT2VECTOROID, 0, INDEX_MAX_KEYS * 2, F_INT2VECTORIN, F_INT2VECTOROUT},
-	{"int4", INT4OID, 0, 4, F_INT4IN, F_INT4OUT},
-	{"regproc", REGPROCOID, 0, 4, F_REGPROCIN, F_REGPROCOUT},
-	{"regclass", REGCLASSOID, 0, 4, F_REGCLASSIN, F_REGCLASSOUT},
-	{"regtype", REGTYPEOID, 0, 4, F_REGTYPEIN, F_REGTYPEOUT},
-	{"text", TEXTOID, 0, -1, F_TEXTIN, F_TEXTOUT},
-	{"oid", OIDOID, 0, 4, F_OIDIN, F_OIDOUT},
-	{"tid", TIDOID, 0, 6, F_TIDIN, F_TIDOUT},
-	{"xid", XIDOID, 0, 4, F_XIDIN, F_XIDOUT},
-	{"cid", CIDOID, 0, 4, F_CIDIN, F_CIDOUT},
-	{"oidvector", OIDVECTOROID, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT},
-	{"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT},
-	{"_int4", 1007, INT4OID, -1, F_ARRAY_IN, F_ARRAY_OUT},
-	{"_text", 1009, TEXTOID, -1, F_ARRAY_IN, F_ARRAY_OUT},
-	{"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT}
+static const struct typinfo TypInfo[] = {
+	{"bool", BOOLOID, 0, 1, true, 'c', 'p',
+	 F_BOOLIN, F_BOOLOUT},
+	{"bytea", BYTEAOID, 0, -1, false, 'i', 'x',
+	 F_BYTEAIN, F_BYTEAOUT},
+	{"char", CHAROID, 0, 1, true, 'c', 'p',
+	 F_CHARIN, F_CHAROUT},
+	{"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'i', 'p',
+	 F_NAMEIN, F_NAMEOUT},
+	{"int2", INT2OID, 0, 2, true, 's', 'p',
+	 F_INT2IN, F_INT2OUT},
+	{"int4", INT4OID, 0, 4, true, 'i', 'p',
+	 F_INT4IN, F_INT4OUT},
+	{"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
+	 F_REGPROCIN, F_REGPROCOUT},
+	{"regclass", REGCLASSOID, 0, 4, true, 'i', 'p',
+	 F_REGCLASSIN, F_REGCLASSOUT},
+	{"regtype", REGTYPEOID, 0, 4, true, 'i', 'p',
+	 F_REGTYPEIN, F_REGTYPEOUT},
+	{"text", TEXTOID, 0, -1, false, 'i', 'x',
+	 F_TEXTIN, F_TEXTOUT},
+	{"oid", OIDOID, 0, 4, true, 'i', 'p',
+	 F_OIDIN, F_OIDOUT},
+	{"tid", TIDOID, 0, 6, false, 's', 'p',
+	 F_TIDIN, F_TIDOUT},
+	{"xid", XIDOID, 0, 4, true, 'i', 'p',
+	 F_XIDIN, F_XIDOUT},
+	{"cid", CIDOID, 0, 4, true, 'i', 'p',
+	 F_CIDIN, F_CIDOUT},
+	{"int2vector", INT2VECTOROID, INT2OID, INDEX_MAX_KEYS * 2, false, 's', 'p',
+	 F_INT2VECTORIN, F_INT2VECTOROUT},
+	{"oidvector", OIDVECTOROID, OIDOID, INDEX_MAX_KEYS * 4, false, 'i', 'p',
+	 F_OIDVECTORIN, F_OIDVECTOROUT},
+	{"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x',
+	 F_ARRAY_IN, F_ARRAY_OUT},
+	{"_text", 1009, TEXTOID, -1, false, 'i', 'x',
+	 F_ARRAY_IN, F_ARRAY_OUT},
+	{"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x',
+	 F_ARRAY_IN, F_ARRAY_OUT}
 };
 
-static int	n_types = sizeof(Procid) / sizeof(struct typinfo);
+static const int	n_types = sizeof(TypInfo) / sizeof(struct typinfo);
 
 struct typmap
 {								/* a hack */
@@ -697,44 +718,13 @@ DefineAttr(char *name, char *type, int attnum)
 	}
 	else
 	{
-		attrtypes[attnum]->atttypid = Procid[typeoid].oid;
-		attlen = attrtypes[attnum]->attlen = Procid[typeoid].len;
-
-		/*
-		 * Cheat like mad to fill in these items from the length only.
-		 * This only has to work for types that appear in Procid[].
-		 */
-		switch (attlen)
-		{
-			case 1:
-				attrtypes[attnum]->attbyval = true;
-				attrtypes[attnum]->attstorage = 'p';
-				attrtypes[attnum]->attalign = 'c';
-				break;
-			case 2:
-				attrtypes[attnum]->attbyval = true;
-				attrtypes[attnum]->attstorage = 'p';
-				attrtypes[attnum]->attalign = 's';
-				break;
-			case 4:
-				attrtypes[attnum]->attbyval = true;
-				attrtypes[attnum]->attstorage = 'p';
-				attrtypes[attnum]->attalign = 'i';
-				break;
-			case -1:
-				attrtypes[attnum]->attbyval = false;
-				attrtypes[attnum]->attstorage = 'x';
-				attrtypes[attnum]->attalign = 'i';
-				break;
-			default:
-				/* TID and fixed-length arrays, such as oidvector */
-				attrtypes[attnum]->attbyval = false;
-				attrtypes[attnum]->attstorage = 'p';
-				attrtypes[attnum]->attalign = 'i';
-				break;
-		}
+		attrtypes[attnum]->atttypid = TypInfo[typeoid].oid;
+		attlen = attrtypes[attnum]->attlen = TypInfo[typeoid].len;
+		attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
+		attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
+		attrtypes[attnum]->attalign = TypInfo[typeoid].align;
 		/* if an array type, assume 1-dimensional attribute */
-		if (Procid[typeoid].elem != InvalidOid && attlen < 0)
+		if (TypInfo[typeoid].elem != InvalidOid && attlen < 0)
 			attrtypes[attnum]->attndims = 1;
 		else
 			attrtypes[attnum]->attndims = 0;
@@ -844,19 +834,19 @@ InsertOneValue(char *value, int i)
 	{
 		for (typeindex = 0; typeindex < n_types; typeindex++)
 		{
-			if (Procid[typeindex].oid == attrtypes[i]->atttypid)
+			if (TypInfo[typeindex].oid == attrtypes[i]->atttypid)
 				break;
 		}
 		if (typeindex >= n_types)
 			elog(ERROR, "type oid %u not found", attrtypes[i]->atttypid);
 		elog(DEBUG4, "Typ == NULL, typeindex = %u", typeindex);
-		values[i] = OidFunctionCall3(Procid[typeindex].inproc,
+		values[i] = OidFunctionCall3(TypInfo[typeindex].inproc,
 									 CStringGetDatum(value),
-								ObjectIdGetDatum(Procid[typeindex].elem),
+								ObjectIdGetDatum(TypInfo[typeindex].elem),
 									 Int32GetDatum(-1));
-		prt = DatumGetCString(OidFunctionCall3(Procid[typeindex].outproc,
+		prt = DatumGetCString(OidFunctionCall3(TypInfo[typeindex].outproc,
 											   values[i],
-								ObjectIdGetDatum(Procid[typeindex].elem),
+								ObjectIdGetDatum(TypInfo[typeindex].elem),
 											   Int32GetDatum(-1)));
 		elog(DEBUG4, " -> %s", prt);
 		pfree(prt);
@@ -930,9 +920,9 @@ cleanup(void)
 /* ----------------
  *		gettype
  *
- * NB: this is really ugly; it will return an integer index into Procid[],
+ * NB: this is really ugly; it will return an integer index into TypInfo[],
  * and not an OID at all, until the first reference to a type not known in
- * Procid[].  At that point it will read and cache pg_type in the Typ array,
+ * TypInfo[].  At that point it will read and cache pg_type in the Typ array,
  * and subsequently return a real OID (and set the global pointer Ap to
  * point at the found row in Typ).	So caller must check whether Typ is
  * still NULL to determine what the return value is!
@@ -962,7 +952,7 @@ gettype(char *type)
 	{
 		for (i = 0; i < n_types; i++)
 		{
-			if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0)
+			if (strncmp(type, TypInfo[i].name, NAMEDATALEN) == 0)
 				return i;
 		}
 		elog(DEBUG4, "external type: %s", type);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ed68f9124d595cffe352f7f0cf7f9ff5cd7f76d8..087e2d7f23cd312fbe3951ba98b6556ff0047619 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.261 2004/03/23 19:35:16 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.262 2004/04/01 21:28:43 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -97,37 +97,37 @@ static void SetRelationNumChecks(Relation rel, int numchecks);
 static FormData_pg_attribute a1 = {
 	0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
 	SelfItemPointerAttributeNumber, 0, -1, -1,
-	false, 'p', false, 'i', true, false, false, true, 0
+	false, 'p', 's', true, false, false, true, 0
 };
 
 static FormData_pg_attribute a2 = {
 	0, {"oid"}, OIDOID, 0, sizeof(Oid),
 	ObjectIdAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 static FormData_pg_attribute a3 = {
 	0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
 	MinTransactionIdAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 static FormData_pg_attribute a4 = {
 	0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
 	MinCommandIdAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 static FormData_pg_attribute a5 = {
 	0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
 	MaxTransactionIdAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 static FormData_pg_attribute a6 = {
 	0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
 	MaxCommandIdAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 /*
@@ -139,7 +139,7 @@ static FormData_pg_attribute a6 = {
 static FormData_pg_attribute a7 = {
 	0, {"tableoid"}, OIDOID, 0, sizeof(Oid),
 	TableOidAttributeNumber, 0, -1, -1,
-	true, 'p', false, 'i', true, false, false, true, 0
+	true, 'p', 'i', true, false, false, true, 0
 };
 
 static Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -633,6 +633,8 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_reltup->reltype = new_type_oid;
 	new_rel_reltup->relkind = relkind;
 
+	new_rel_desc->rd_att->tdtypeid = new_type_oid;
+
 	/* ----------------
 	 *	now form a tuple to add to pg_class
 	 *	XXX Natts_pg_class_fixed is a hack - see pg_class.h
@@ -660,7 +662,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 /* --------------------------------
  *		AddNewRelationType -
  *
- *		define a complex type corresponding to the new relation
+ *		define a composite type corresponding to the new relation
  * --------------------------------
  */
 static void
@@ -670,27 +672,12 @@ AddNewRelationType(const char *typeName,
 				   char new_rel_kind,
 				   Oid new_type_oid)
 {
-	/*
-	 * We set the I/O procedures of a complex type to record_in and
-	 * record_out, so that a user will get an error message not a weird
-	 * number if he tries to SELECT a complex type.
-	 *
-	 * OLD and probably obsolete comments:
-	 *
-	 * The sizes are set to oid size because it makes implementing sets MUCH
-	 * easier, and no one (we hope) uses these fields to figure out how
-	 * much space to allocate for the type. An oid is the type used for a
-	 * set definition.	When a user requests a set, what they actually get
-	 * is the oid of a tuple in the pg_proc catalog, so the size of the
-	 * "set" is the size of an oid. Similarly, byval being true makes sets
-	 * much easier, and it isn't used by anything else.
-	 */
 	TypeCreate(typeName,		/* type name */
 			   typeNamespace,	/* type namespace */
 			   new_type_oid,	/* preassigned oid for type */
 			   new_rel_oid,		/* relation oid */
 			   new_rel_kind,	/* relation kind */
-			   sizeof(Oid),		/* internal size */
+			   -1,				/* internal size (varlena) */
 			   'c',				/* type-type (complex) */
 			   ',',				/* default array delimiter */
 			   F_RECORD_IN,		/* input procedure */
@@ -702,9 +689,9 @@ AddNewRelationType(const char *typeName,
 			   InvalidOid,		/* domain base type - irrelevant */
 			   NULL,			/* default type value - none */
 			   NULL,			/* default type binary representation */
-			   true,			/* passed by value */
-			   'i',				/* default alignment - same as for OID */
-			   'p',				/* Not TOASTable */
+			   false,			/* passed by reference */
+			   'd',				/* alignment - must be the largest! */
+			   'x',				/* fully TOASTable */
 			   -1,				/* typmod */
 			   0,				/* array dimensions for typBaseType */
 			   false);			/* Type NOT NULL */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 6fe64eadd0d9ba459dadb40723a778009d9466db..0640aacbe5a735133b9228eb69bfbcd9924bc5cc 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.113 2004/03/21 22:29:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.114 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,7 +32,6 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
-#include "utils/sets.h"
 #include "utils/syscache.h"
 
 
@@ -137,44 +136,6 @@ ProcedureCreate(const char *procedureName,
 	/* Process param names, if given */
 	namesarray = create_parameternames_array(parameterCount, parameterNames);
 
-	if (languageObjectId == SQLlanguageId)
-	{
-		/*
-		 * If this call is defining a set, check if the set is already
-		 * defined by looking to see whether this call's function text
-		 * matches a function already in pg_proc.  If so just return the
-		 * OID of the existing set.
-		 */
-		if (strcmp(procedureName, GENERICSETNAME) == 0)
-		{
-#ifdef SETS_FIXED
-
-			/*
-			 * The code below doesn't work any more because the PROSRC
-			 * system cache and the pg_proc_prosrc_index have been
-			 * removed. Instead a sequential heap scan or something better
-			 * must get implemented. The reason for removing is that
-			 * nbtree index crashes if sources exceed 2K --- what's likely
-			 * for procedural languages.
-			 *
-			 * 1999/09/30 Jan
-			 */
-			text	   *prosrctext;
-
-			prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
-											   CStringGetDatum(prosrc)));
-			retval = GetSysCacheOid(PROSRC,
-									PointerGetDatum(prosrctext),
-									0, 0, 0);
-			pfree(prosrctext);
-			if (OidIsValid(retval))
-				return retval;
-#else
-			elog(ERROR, "lookup for procedure by source needs fix (Jan)");
-#endif   /* SETS_FIXED */
-		}
-	}
-
 	/*
 	 * don't allow functions of complex types that have the same name as
 	 * existing attributes of the type
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 3c134a5c16606faf6436bb0532bfd61a3818b3de..e2f3d4aa8136d10497418db50b134399e11be6fc 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.119 2004/01/31 05:09:40 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.120 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,7 +125,7 @@ ExplainResultDesc(ExplainStmt *stmt)
 	/* need a tuple descriptor representing a single TEXT column */
 	tupdesc = CreateTemplateTupleDesc(1, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
-					   TEXTOID, -1, 0, false);
+					   TEXTOID, -1, 0);
 	return tupdesc;
 }
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1a6bd2fdfc0d004dc14a565b5cdbaa2f2ffec0b7..a6e3a93d349512634f89184c89ec2d4484c14472 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.101 2004/03/23 19:35:16 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.102 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1796,7 +1796,6 @@ AlterTableAddColumn(Oid myrelid,
 	attribute->attnum = i;
 	attribute->attbyval = tform->typbyval;
 	attribute->attndims = attndims;
-	attribute->attisset = (bool) (tform->typtype == 'c');
 	attribute->attstorage = tform->typstorage;
 	attribute->attalign = tform->typalign;
 	attribute->attnotnull = colDef->is_not_null;
@@ -4084,15 +4083,15 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1,
 					   "chunk_id",
 					   OIDOID,
-					   -1, 0, false);
+					   -1, 0);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 2,
 					   "chunk_seq",
 					   INT4OID,
-					   -1, 0, false);
+					   -1, 0);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 3,
 					   "chunk_data",
 					   BYTEAOID,
-					   -1, 0, false);
+					   -1, 0);
 
 	/*
 	 * Ensure that the toast table doesn't itself get toasted, or we'll be
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 4c6f95a9a6f04e9dc27b4b32a0d7aae42ab4da0d..b27e86122bc871255c997441d99e85276d3c21fb 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.157 2004/03/24 22:40:28 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 /* static function decls */
@@ -110,7 +111,7 @@ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
 static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
 										 ExprContext *econtext,
 										 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalFieldSelect(GenericExprState *fstate,
+static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
 					ExprContext *econtext,
 					bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
@@ -420,16 +421,25 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 		*isDone = ExprSingleResult;
 
 	/*
-	 * get the slot we want
+	 * Get the slot and attribute number we want
+	 *
+	 * The asserts check that references to system attributes only appear
+	 * at the level of a relation scan; at higher levels, system attributes
+	 * must be treated as ordinary variables (since we no longer have access
+	 * to the original tuple).
 	 */
+	attnum = variable->varattno;
+
 	switch (variable->varno)
 	{
 		case INNER:				/* get the tuple from the inner node */
 			slot = econtext->ecxt_innertuple;
+			Assert(attnum > 0);
 			break;
 
 		case OUTER:				/* get the tuple from the outer node */
 			slot = econtext->ecxt_outertuple;
+			Assert(attnum > 0);
 			break;
 
 		default:				/* get the tuple from the relation being
@@ -444,8 +454,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 	heapTuple = slot->val;
 	tuple_type = slot->ttc_tupleDescriptor;
 
-	attnum = variable->varattno;
-
 	/*
 	 * Some checks that are only applied for user attribute numbers
 	 * (bogus system attnums will be caught inside heap_getattr).
@@ -481,38 +489,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
 		Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid);
 	}
 
-	/*
-	 * If the attribute number is invalid, then we are supposed to return
-	 * the entire tuple; we give back a whole slot so that callers know
-	 * what the tuple looks like.
-	 *
-	 * XXX this is a horrid crock: since the pointer to the slot might live
-	 * longer than the current evaluation context, we are forced to copy
-	 * the tuple and slot into a long-lived context --- we use the
-	 * econtext's per-query memory which should be safe enough.  This
-	 * represents a serious memory leak if many such tuples are processed
-	 * in one command, however.  We ought to redesign the representation
-	 * of whole-tuple datums so that this is not necessary.
-	 *
-	 * We assume it's OK to point to the existing tupleDescriptor, rather
-	 * than copy that too.
-	 */
-	if (attnum == InvalidAttrNumber)
-	{
-		MemoryContext oldContext;
-		TupleTableSlot *tempSlot;
-		HeapTuple	tup;
-
-		oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-		tempSlot = MakeTupleTableSlot();
-		tup = heap_copytuple(heapTuple);
-		ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
-		ExecSetSlotDescriptor(tempSlot, tuple_type, false);
-		MemoryContextSwitchTo(oldContext);
-		*isNull = false;
-		return PointerGetDatum(tempSlot);
-	}
-
 	result = heap_getattr(heapTuple,	/* tuple containing attribute */
 						  attnum,		/* attribute number of desired
 										 * attribute */
@@ -656,17 +632,23 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
  *		GetAttributeByName
  *		GetAttributeByNum
  *
- *		These are functions which return the value of the
- *		named attribute out of the tuple from the arg slot.  User defined
+ *		These functions return the value of the requested attribute
+ *		out of the given tuple Datum.
  *		C functions which take a tuple as an argument are expected
- *		to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
+ *		to use these.  Ex: overpaid(EMP) might call GetAttributeByNum().
+ *		Note: these are actually rather slow because they do a typcache
+ *		lookup on each call.
  */
 Datum
-GetAttributeByNum(TupleTableSlot *slot,
+GetAttributeByNum(HeapTupleHeader tuple,
 				  AttrNumber attrno,
 				  bool *isNull)
 {
-	Datum		retval;
+	Datum		result;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	HeapTupleData tmptup;
 
 	if (!AttributeNumberIsValid(attrno))
 		elog(ERROR, "invalid attribute number %d", attrno);
@@ -674,29 +656,43 @@ GetAttributeByNum(TupleTableSlot *slot,
 	if (isNull == NULL)
 		elog(ERROR, "a NULL isNull pointer was passed");
 
-	if (TupIsNull(slot))
+	if (tuple == NULL)
 	{
+		/* Kinda bogus but compatible with old behavior... */
 		*isNull = true;
 		return (Datum) 0;
 	}
 
-	retval = heap_getattr(slot->val,
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+	tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+	/*
+	 * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set
+	 * all the fields in the struct just in case user tries to inspect
+	 * system columns.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	ItemPointerSetInvalid(&(tmptup.t_self));
+	tmptup.t_tableOid = InvalidOid;
+	tmptup.t_data = tuple;
+
+	result = heap_getattr(&tmptup,
 						  attrno,
-						  slot->ttc_tupleDescriptor,
+						  tupDesc,
 						  isNull);
-	if (*isNull)
-		return (Datum) 0;
-
-	return retval;
+	return result;
 }
 
 Datum
-GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
+GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
 {
 	AttrNumber	attrno;
-	TupleDesc	tupdesc;
-	Datum		retval;
-	int			natts;
+	Datum		result;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	HeapTupleData tmptup;
 	int			i;
 
 	if (attname == NULL)
@@ -705,21 +701,23 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 	if (isNull == NULL)
 		elog(ERROR, "a NULL isNull pointer was passed");
 
-	if (TupIsNull(slot))
+	if (tuple == NULL)
 	{
+		/* Kinda bogus but compatible with old behavior... */
 		*isNull = true;
 		return (Datum) 0;
 	}
 
-	tupdesc = slot->ttc_tupleDescriptor;
-	natts = slot->val->t_data->t_natts;
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+	tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
 	attrno = InvalidAttrNumber;
-	for (i = 0; i < tupdesc->natts; i++)
+	for (i = 0; i < tupDesc->natts; i++)
 	{
-		if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0)
+		if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
 		{
-			attrno = tupdesc->attrs[i]->attnum;
+			attrno = tupDesc->attrs[i]->attnum;
 			break;
 		}
 	}
@@ -727,14 +725,21 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 	if (attrno == InvalidAttrNumber)
 		elog(ERROR, "attribute \"%s\" does not exist", attname);
 
-	retval = heap_getattr(slot->val,
+	/*
+	 * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set
+	 * all the fields in the struct just in case user tries to inspect
+	 * system columns.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	ItemPointerSetInvalid(&(tmptup.t_self));
+	tmptup.t_tableOid = InvalidOid;
+	tmptup.t_data = tuple;
+
+	result = heap_getattr(&tmptup,
 						  attrno,
-						  tupdesc,
+						  tupDesc,
 						  isNull);
-	if (*isNull)
-		return (Datum) 0;
-
-	return retval;
+	return result;
 }
 
 /*
@@ -1133,14 +1138,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 	Tuplestorestate *tupstore = NULL;
 	TupleDesc	tupdesc = NULL;
 	Oid			funcrettype;
+	bool		returnsTuple;
 	FunctionCallInfoData fcinfo;
 	ReturnSetInfo rsinfo;
+	HeapTupleData tmptup;
 	MemoryContext callerContext;
 	MemoryContext oldcontext;
-	TupleTableSlot *slot;
 	bool		direct_function_call;
 	bool		first_time = true;
-	bool		returnsTuple = false;
 
 	/*
 	 * Normally the passed expression tree will be a FuncExprState, since
@@ -1216,6 +1221,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 
 	funcrettype = exprType((Node *) funcexpr->expr);
 
+	returnsTuple = (funcrettype == RECORDOID ||
+					get_typtype(funcrettype) == 'c');
+
 	/*
 	 * Prepare a resultinfo node for communication.  We always do this
 	 * even if not expecting a set result, so that we can pass
@@ -1281,6 +1289,16 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 			if (rsinfo.isDone == ExprEndResult)
 				break;
 
+			/*
+			 * Can't do anything useful with NULL rowtype values.  Currently
+			 * we raise an error, but another alternative is to just ignore
+			 * the result and "continue" to get another row.
+			 */
+			if (returnsTuple && fcinfo.isnull)
+				ereport(ERROR,
+						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+						 errmsg("function returning row cannot return null value")));
+
 			/*
 			 * If first time through, build tupdesc and tuplestore for
 			 * result
@@ -1288,25 +1306,18 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 			if (first_time)
 			{
 				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-				if (funcrettype == RECORDOID ||
-					get_typtype(funcrettype) == 'c')
+				if (returnsTuple)
 				{
 					/*
-					 * Composite type, so function should have returned a
-					 * TupleTableSlot; use its descriptor
+					 * Use the type info embedded in the rowtype Datum to
+					 * look up the needed tupdesc.  Make a copy for the query.
 					 */
-					slot = (TupleTableSlot *) DatumGetPointer(result);
-					if (fcinfo.isnull || !slot)
-						ereport(ERROR,
-								(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-								 errmsg("function returning row cannot return null value")));
-					if (!IsA(slot, TupleTableSlot) ||
-						!slot->ttc_tupleDescriptor)
-						ereport(ERROR,
-								(errcode(ERRCODE_DATATYPE_MISMATCH),
-								 errmsg("function returning row did not return a valid tuple slot")));
-					tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
-					returnsTuple = true;
+					HeapTupleHeader	td;
+
+					td = DatumGetHeapTupleHeader(result);
+					tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td),
+													 HeapTupleHeaderGetTypMod(td));
+					tupdesc = CreateTupleDescCopy(tupdesc);
 				}
 				else
 				{
@@ -1319,8 +1330,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 									   "column",
 									   funcrettype,
 									   -1,
-									   0,
-									   false);
+									   0);
 				}
 				tupstore = tuplestore_begin_heap(true, false, work_mem);
 				MemoryContextSwitchTo(oldcontext);
@@ -1333,15 +1343,17 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
 			 */
 			if (returnsTuple)
 			{
-				slot = (TupleTableSlot *) DatumGetPointer(result);
-				if (fcinfo.isnull ||
-					!slot ||
-					!IsA(slot, TupleTableSlot) ||
-					TupIsNull(slot))
-					ereport(ERROR,
-							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-							 errmsg("function returning row cannot return null value")));
-				tuple = slot->val;
+				HeapTupleHeader	td;
+
+				td = DatumGetHeapTupleHeader(result);
+
+				/*
+				 * tuplestore_puttuple needs a HeapTuple not a bare
+				 * HeapTupleHeader, but it doesn't need all the fields.
+				 */
+				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+				tmptup.t_data = td;
+				tuple = &tmptup;
 			}
 			else
 			{
@@ -2415,26 +2427,62 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalFieldSelect(GenericExprState *fstate,
+ExecEvalFieldSelect(FieldSelectState *fstate,
 					ExprContext *econtext,
 					bool *isNull,
 					ExprDoneCond *isDone)
 {
 	FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
 	Datum		result;
-	TupleTableSlot *resSlot;
+	Datum		tupDatum;
+	HeapTupleHeader tuple;
+	Oid			tupType;
+	int32		tupTypmod;
+	TupleDesc	tupDesc;
+	HeapTupleData tmptup;
 
-	result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
 
 	/* this test covers the isDone exception too: */
 	if (*isNull)
-		return result;
+		return tupDatum;
+
+	tuple = DatumGetHeapTupleHeader(tupDatum);
+
+	tupType = HeapTupleHeaderGetTypeId(tuple);
+	tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+	/* Lookup tupdesc if first time through or if type changes */
+	tupDesc = fstate->argdesc;
+	if (tupDesc == NULL ||
+		tupType != tupDesc->tdtypeid ||
+		tupTypmod != tupDesc->tdtypmod)
+	{
+		MemoryContext oldcontext;
+
+		tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+		/* Copy the tupdesc into query storage for safety */
+		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+		tupDesc = CreateTupleDescCopy(tupDesc);
+		if (fstate->argdesc)
+			FreeTupleDesc(fstate->argdesc);
+		fstate->argdesc = tupDesc;
+		MemoryContextSwitchTo(oldcontext);
+	}
 
-	resSlot = (TupleTableSlot *) DatumGetPointer(result);
-	Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
-	result = heap_getattr(resSlot->val,
+	/*
+	 * heap_getattr needs a HeapTuple not a bare HeapTupleHeader.  We set
+	 * all the fields in the struct just in case user tries to inspect
+	 * system columns.
+	 */
+	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+	ItemPointerSetInvalid(&(tmptup.t_self));
+	tmptup.t_tableOid = InvalidOid;
+	tmptup.t_data = tuple;
+
+	result = heap_getattr(&tmptup,
 						  fselect->fieldnum,
-						  resSlot->ttc_tupleDescriptor,
+						  tupDesc,
 						  isNull);
 	return result;
 }
@@ -2703,11 +2751,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
 		case T_FieldSelect:
 			{
 				FieldSelect *fselect = (FieldSelect *) node;
-				GenericExprState *gstate = makeNode(GenericExprState);
+				FieldSelectState *fstate = makeNode(FieldSelectState);
 
-				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
-				gstate->arg = ExecInitExpr(fselect->arg, parent);
-				state = (ExprState *) gstate;
+				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
+				fstate->arg = ExecInitExpr(fselect->arg, parent);
+				fstate->argdesc = NULL;
+				state = (ExprState *) fstate;
 			}
 			break;
 		case T_RelabelType:
@@ -3088,8 +3137,6 @@ ExecTargetList(List *targetlist,
 	List	   *tl;
 	bool		isNull;
 	bool		haveDoneSets;
-	static struct tupleDesc NullTupleDesc;		/* we assume this inits to
-												 * zeroes */
 
 	/*
 	 * debugging stuff
@@ -3106,13 +3153,8 @@ ExecTargetList(List *targetlist,
 	/*
 	 * There used to be some klugy and demonstrably broken code here that
 	 * special-cased the situation where targetlist == NIL.  Now we just
-	 * fall through and return an empty-but-valid tuple.  We do, however,
-	 * have to cope with the possibility that targettype is NULL ---
-	 * heap_formtuple won't like that, so pass a dummy descriptor with
-	 * natts = 0 to deal with it.
+	 * fall through and return an empty-but-valid tuple.
 	 */
-	if (targettype == NULL)
-		targettype = &NullTupleDesc;
 
 	/*
 	 * evaluate all the expressions in the target list
@@ -3285,8 +3327,8 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 	/*
 	 * store the tuple in the projection slot and return the slot.
 	 */
-	return ExecStoreTuple(newTuple,		/* tuple to store */
-						  slot, /* slot to store in */
-						  InvalidBuffer,		/* tuple has no buffer */
+	return ExecStoreTuple(newTuple,			/* tuple to store */
+						  slot,				/* slot to store in */
+						  InvalidBuffer,	/* tuple has no buffer */
 						  true);
 }
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 0bb59d26b11758c755fa7d2ff934a4ff43092194..faf910b736f9828f43d90c37dab839ee02bf9e46 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.75 2004/01/07 18:56:26 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,8 +109,11 @@
 
 #include "funcapi.h"
 #include "access/heapam.h"
+#include "catalog/pg_type.h"
 #include "executor/executor.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
 
 static TupleDesc ExecTypeFromTLInternal(List *targetList,
 										bool hasoid, bool skipjunk);
@@ -144,16 +147,11 @@ ExecCreateTupleTable(int initialSize)	/* initial number of slots in
 
 	/*
 	 * Now allocate our new table along with space for the pointers to the
-	 * tuples.
+	 * tuples.  Zero out the slots.
 	 */
 
 	newtable = (TupleTable) palloc(sizeof(TupleTableData));
-	array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
-
-	/*
-	 * clean out the slots we just allocated
-	 */
-	MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
+	array = (TupleTableSlot *) palloc0(initialSize * sizeof(TupleTableSlot));
 
 	/*
 	 * initialize the new table and return it to the caller.
@@ -514,6 +512,10 @@ TupleTableSlot *
 ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 {
 	TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
+	struct tupleDesc nullTupleDesc;
+	HeapTuple	nullTuple;
+	Datum		values[1];
+	char		nulls[1];
 
 	/*
 	 * Since heap_getattr() will treat attributes beyond a tuple's t_natts
@@ -521,15 +523,12 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 	 * of zero length.	However, the slot descriptor must match the real
 	 * tupType.
 	 */
-	HeapTuple	nullTuple;
-	Datum		values[1];
-	char		nulls[1];
-	static struct tupleDesc NullTupleDesc;		/* we assume this inits to
-												 * zeroes */
+	nullTupleDesc = *tupType;
+	nullTupleDesc.natts = 0;
 
-	ExecSetSlotDescriptor(slot, tupType, false);
+	nullTuple = heap_formtuple(&nullTupleDesc, values, nulls);
 
-	nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
+	ExecSetSlotDescriptor(slot, tupType, false);
 
 	return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
 }
@@ -590,21 +589,45 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
 						   resdom->resname,
 						   resdom->restype,
 						   resdom->restypmod,
-						   0,
-						   false);
+						   0);
 	}
 
 	return typeInfo;
 }
 
+/*
+ * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
+ *
+ * Rowtype Datums returned by a function must contain valid type information.
+ * This happens "for free" if the tupdesc came from a relcache entry, but
+ * not if we have manufactured a tupdesc for a transient RECORD datatype.
+ * In that case we have to notify typcache.c of the existence of the type.
+ */
+TupleDesc
+BlessTupleDesc(TupleDesc tupdesc)
+{
+	if (tupdesc->tdtypeid == RECORDOID &&
+		tupdesc->tdtypmod < 0)
+		assign_record_type_typmod(tupdesc);
+
+	return tupdesc;				/* just for notational convenience */
+}
+
 /*
  * TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
+ *
+ * Note: this is obsolete; it is sufficient to call BlessTupleDesc on
+ * the tupdesc.  We keep it around just for backwards compatibility with
+ * existing user-written SRFs.
  */
 TupleTableSlot *
 TupleDescGetSlot(TupleDesc tupdesc)
 {
 	TupleTableSlot *slot;
 
+	/* The useful work is here */
+	BlessTupleDesc(tupdesc);
+
 	/* Make a standalone slot */
 	slot = MakeTupleTableSlot();
 
@@ -634,6 +657,9 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
 
 	attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
 
+	/* "Bless" the tupledesc so that we can make rowtype datums with it */
+	attinmeta->tupdesc = BlessTupleDesc(tupdesc);
+
 	/*
 	 * Gather info needed later to call the "in" function for each
 	 * attribute
@@ -653,7 +679,6 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc)
 			atttypmods[i] = tupdesc->attrs[i]->atttypmod;
 		}
 	}
-	attinmeta->tupdesc = tupdesc;
 	attinmeta->attinfuncs = attinfuncinfo;
 	attinmeta->attelems = attelems;
 	attinmeta->atttypmods = atttypmods;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 8dec6131fb4ea7326eabdbccb666814c764c9e54..aa7652e07ef567eabcb72e1b82cee8c276330003 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.78 2004/03/21 22:29:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.79 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,8 +24,10 @@
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /*
@@ -61,10 +63,6 @@ typedef struct
 	bool		returnsTuple;	/* true if return type is a tuple */
 	bool		shutdown_reg;	/* true if registered shutdown callback */
 
-	TupleTableSlot *funcSlot;	/* if one result we need to copy it before
-								 * we end execution of the function and
-								 * free stuff */
-
 	ParamListInfo paramLI;		/* Param list representing current args */
 
 	/* head of linked list of execution_state records */
@@ -196,34 +194,9 @@ init_sql_fcache(FmgrInfo *finfo)
 	 * get the type length and by-value flag from the type tuple
 	 */
 	fcache->typlen = typeStruct->typlen;
-
-	if (typeStruct->typtype != 'c' && rettype != RECORDOID)
-	{
-		/* The return type is not a composite type, so just use byval */
-		fcache->typbyval = typeStruct->typbyval;
-		fcache->returnsTuple = false;
-	}
-	else
-	{
-		/*
-		 * This is a hack.	We assume here that any function returning a
-		 * tuple returns it by reference.  This needs to be fixed, since
-		 * actually the mechanism isn't quite like return-by-reference.
-		 */
-		fcache->typbyval = false;
-		fcache->returnsTuple = true;
-	}
-
-	/*
-	 * If we are returning exactly one result then we have to copy tuples
-	 * and by reference results because we have to end the execution
-	 * before we return the results.  When you do this everything
-	 * allocated by the executor (i.e. slots and tuples) is freed.
-	 */
-	if (!finfo->fn_retset && !fcache->typbyval)
-		fcache->funcSlot = MakeTupleTableSlot();
-	else
-		fcache->funcSlot = NULL;
+	fcache->typbyval = typeStruct->typbyval;
+	fcache->returnsTuple = (typeStruct->typtype == 'c' ||
+							rettype == RECORDOID);
 
 	/*
 	 * Parse and plan the queries.	We need the argument type info to pass
@@ -366,39 +339,6 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
 	fcache->paramLI = paramLI;
 }
 
-static TupleTableSlot *
-copy_function_result(SQLFunctionCachePtr fcache,
-					 TupleTableSlot *resultSlot)
-{
-	TupleTableSlot *funcSlot;
-	TupleDesc	resultTd;
-	HeapTuple	resultTuple;
-	HeapTuple	newTuple;
-
-	Assert(!TupIsNull(resultSlot));
-	resultTuple = resultSlot->val;
-
-	funcSlot = fcache->funcSlot;
-
-	if (funcSlot == NULL)
-		return resultSlot;		/* no need to copy result */
-
-	/*
-	 * If first time through, we have to initialize the funcSlot's tuple
-	 * descriptor.
-	 */
-	if (funcSlot->ttc_tupleDescriptor == NULL)
-	{
-		resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
-		ExecSetSlotDescriptor(funcSlot, resultTd, true);
-		ExecSetSlotDescriptorIsNew(funcSlot, true);
-	}
-
-	newTuple = heap_copytuple(resultTuple);
-
-	return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
-}
-
 static Datum
 postquel_execute(execution_state *es,
 				 FunctionCallInfo fcinfo,
@@ -429,43 +369,51 @@ postquel_execute(execution_state *es,
 
 	if (LAST_POSTQUEL_COMMAND(es))
 	{
-		TupleTableSlot *resSlot;
-
 		/*
-		 * Copy the result.  copy_function_result is smart enough to do
-		 * nothing when no action is called for.  This helps reduce the
-		 * logic and code redundancy here.
+		 * Set up to return the function value.
 		 */
-		resSlot = copy_function_result(fcache, slot);
+		HeapTuple	tup = slot->val;
+		TupleDesc	tupDesc = slot->ttc_tupleDescriptor;
 
-		/*
-		 * If we are supposed to return a tuple, we return the tuple slot
-		 * pointer converted to Datum.	If we are supposed to return a
-		 * simple value, then project out the first attribute of the
-		 * result tuple (ie, take the first result column of the final
-		 * SELECT).
-		 */
 		if (fcache->returnsTuple)
 		{
 			/*
+			 * We are returning the whole tuple, so copy it into current
+			 * execution context and make sure it is a valid Datum.
+			 *
 			 * XXX do we need to remove junk attrs from the result tuple?
 			 * Probably OK to leave them, as long as they are at the end.
 			 */
-			value = PointerGetDatum(resSlot);
+			HeapTupleHeader	dtup;
+
+			dtup = (HeapTupleHeader) palloc(tup->t_len);
+			memcpy((char *) dtup, (char *) tup->t_data, tup->t_len);
+
+			/*
+			 * For RECORD results, make sure a typmod has been assigned.
+			 */
+			if (tupDesc->tdtypeid == RECORDOID &&
+				tupDesc->tdtypmod < 0)
+				assign_record_type_typmod(tupDesc);
+
+			HeapTupleHeaderSetDatumLength(dtup, tup->t_len);
+			HeapTupleHeaderSetTypeId(dtup, tupDesc->tdtypeid);
+			HeapTupleHeaderSetTypMod(dtup, tupDesc->tdtypmod);
+
+			value = PointerGetDatum(dtup);
 			fcinfo->isnull = false;
 		}
 		else
 		{
-			value = heap_getattr(resSlot->val,
-								 1,
-								 resSlot->ttc_tupleDescriptor,
-								 &(fcinfo->isnull));
-
 			/*
-			 * Note: if result type is pass-by-reference then we are
-			 * returning a pointer into the tuple copied by
-			 * copy_function_result.  This is OK.
+			 * Returning a scalar, which we have to extract from the
+			 * first column of the SELECT result, and then copy into current
+			 * execution context if needed.
 			 */
+			value = heap_getattr(tup, 1, tupDesc, &(fcinfo->isnull));
+
+			if (!fcinfo->isnull)
+				value = datumCopy(value, fcache->typbyval, fcache->typlen);
 		}
 
 		/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index f3fa17c888167190c46861e6d80575802447e246..7847b24ffe20f2e316e847cf2fefe1a36c4ad6f7 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.23 2003/11/29 19:51:48 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.24 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_type.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 static TupleTableSlot *FunctionNext(FunctionScanState *node);
@@ -194,25 +195,12 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
 
 	if (functyptype == 'c')
 	{
-		/*
-		 * Composite data type, i.e. a table's row type
-		 */
-		Oid			funcrelid;
-		Relation	rel;
-
-		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);
+		/* Composite data type, e.g. a table's row type */
+		tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(funcrettype, -1));
 	}
 	else if (functyptype == 'b' || functyptype == 'd')
 	{
-		/*
-		 * Must be a base data type, i.e. scalar
-		 */
+		/* Must be a base data type, i.e. scalar */
 		char	   *attname = strVal(lfirst(rte->eref->colnames));
 
 		tupdesc = CreateTemplateTupleDesc(1, false);
@@ -221,14 +209,11 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
 						   attname,
 						   funcrettype,
 						   -1,
-						   0,
-						   false);
+						   0);
 	}
-	else if (functyptype == 'p' && funcrettype == RECORDOID)
+	else if (funcrettype == RECORDOID)
 	{
-		/*
-		 * Must be a pseudo type, i.e. record
-		 */
+		/* Must be a pseudo type, i.e. record */
 		tupdesc = BuildDescForRelation(rte->coldeflist);
 	}
 	else
@@ -237,6 +222,14 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
 		elog(ERROR, "function in FROM has unsupported return type");
 	}
 
+	/*
+	 * For RECORD results, make sure a typmod has been assigned.  (The
+	 * function should do this for itself, but let's cover things in case
+	 * it doesn't.)
+	 */
+	if (tupdesc->tdtypeid == RECORDOID && tupdesc->tdtypmod < 0)
+		assign_record_type_typmod(tupdesc);
+
 	scanstate->tupdesc = tupdesc;
 	ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
 						  tupdesc, false);
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 1fb60fff02d60f16f4c5f2d98f9202a4546c17ba..bcf7f8d52b68cffd345aee4f0f3c580840a1c4f8 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.112 2004/03/21 22:29:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.113 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,7 @@
 #include "executor/spi_priv.h"
 #include "tcop/tcopprot.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 uint32		SPI_processed = 0;
@@ -380,40 +381,11 @@ SPI_copytuple(HeapTuple tuple)
 	return ctuple;
 }
 
-TupleDesc
-SPI_copytupledesc(TupleDesc tupdesc)
+HeapTupleHeader
+SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
 {
 	MemoryContext oldcxt = NULL;
-	TupleDesc	ctupdesc;
-
-	if (tupdesc == NULL)
-	{
-		SPI_result = SPI_ERROR_ARGUMENT;
-		return NULL;
-	}
-
-	if (_SPI_curid + 1 == _SPI_connected)		/* connected */
-	{
-		if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
-			elog(ERROR, "SPI stack corrupted");
-		oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
-	}
-
-	ctupdesc = CreateTupleDescCopy(tupdesc);
-
-	if (oldcxt)
-		MemoryContextSwitchTo(oldcxt);
-
-	return ctupdesc;
-}
-
-TupleTableSlot *
-SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
-{
-	MemoryContext oldcxt = NULL;
-	TupleTableSlot *cslot;
-	HeapTuple	ctuple;
-	TupleDesc	ctupdesc;
+	HeapTupleHeader	dtup;
 
 	if (tuple == NULL || tupdesc == NULL)
 	{
@@ -421,6 +393,11 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
 		return NULL;
 	}
 
+	/* For RECORD results, make sure a typmod has been assigned */
+	if (tupdesc->tdtypeid == RECORDOID &&
+		tupdesc->tdtypmod < 0)
+		assign_record_type_typmod(tupdesc);
+
 	if (_SPI_curid + 1 == _SPI_connected)		/* connected */
 	{
 		if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
@@ -428,17 +405,17 @@ SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
 		oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
 	}
 
-	ctuple = heap_copytuple(tuple);
-	ctupdesc = CreateTupleDescCopy(tupdesc);
+	dtup = (HeapTupleHeader) palloc(tuple->t_len);
+	memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
 
-	cslot = MakeTupleTableSlot();
-	ExecSetSlotDescriptor(cslot, ctupdesc, true);
-	cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
+	HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
+	HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
+	HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
 
 	if (oldcxt)
 		MemoryContextSwitchTo(oldcxt);
 
-	return cslot;
+	return dtup;
 }
 
 HeapTuple
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 24b2fefe16af3ef0b1c01f67d71701a00a6a6cbb..1677493abbcea97c925275b8662adfedc8e08644 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.165 2004/01/07 18:56:27 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -170,9 +170,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 			 * tuple from the relation.  We build a special VarNode to
 			 * reflect this -- it has varno set to the correct range table
 			 * entry, but has varattno == 0 to signal that the whole tuple
-			 * is the argument.  Also, it has typmod set to
-			 * sizeof(Pointer) to signal that the runtime representation
-			 * will be a pointer not an Oid.
+			 * is the argument.
 			 */
 			switch (rte->rtekind)
 			{
@@ -185,7 +183,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 					lfirst(i) = makeVar(vnum,
 										InvalidAttrNumber,
 										toid,
-										sizeof(Pointer),
+										-1,
 										sublevels_up);
 					break;
 				case RTE_FUNCTION:
@@ -196,7 +194,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 						lfirst(i) = makeVar(vnum,
 											InvalidAttrNumber,
 											toid,
-											sizeof(Pointer),
+											-1,
 											sublevels_up);
 					}
 					else
@@ -214,6 +212,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 					/*
 					 * RTE is a join or subselect; must fail for lack of a
 					 * named tuple type
+					 *
+					 * XXX FIXME
 					 */
 					if (is_column)
 						unknown_attribute(schemaname, relname,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index e66eb905f563a198f485b9872e80ffb95eaea900..d83e8ac45805bec8661681b51c9cefc552a45b20 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.133 2004/01/14 23:01:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.134 2004/04/01 21:28:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -523,16 +523,7 @@ build_column_default(Relation rel, int attrno)
 		 * No per-column default, so look for a default for the type
 		 * itself.
 		 */
-		if (att_tup->attisset)
-		{
-			/*
-			 * Set attributes are represented as OIDs no matter what the
-			 * set element type is, and the element type's default is
-			 * irrelevant too.
-			 */
-		}
-		else
-			expr = get_typdefault(atttype);
+		expr = get_typdefault(atttype);
 	}
 
 	if (expr == NULL)
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 5223e33816ec12757e85e4ffe58464a1240ef55b..427d6334cd43850c1e5f38757e60ca04a4c6b68f 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for utils/adt
 #
-# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.56 2003/11/29 19:51:57 pgsql Exp $
+# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.57 2004/04/01 21:28:45 tgl Exp $
 #
 
 subdir = src/backend/utils/adt
@@ -19,8 +19,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
 	cash.o char.o date.o datetime.o datum.o float.o format_type.o \
 	geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
 	misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
-	oid.o oracle_compat.o pseudotypes.o \
-	regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
+	oid.o oracle_compat.o pseudotypes.o rowtypes.o \
+	regexp.o regproc.o ruleutils.o selfuncs.o \
 	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
 	network.o mac.o inet_net_ntop.o inet_net_pton.o \
 	ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index b009e167a3fc4c4a3e9abc8eec894fa738270217..bc4901aa5463c634533340e8e874219e1277c1ec 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.12 2003/11/29 19:51:58 pgsql Exp $
+ *		$PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,22 +52,22 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		/* build tupdesc for result tuples */
-		/* this had better match pg_locks view in initdb.sh */
+		/* this had better match pg_locks view in system_views.sql */
 		tupdesc = CreateTemplateTupleDesc(6, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
-						   OIDOID, -1, 0, false);
+						   OIDOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
-						   OIDOID, -1, 0, false);
+						   OIDOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
-						   XIDOID, -1, 0, false);
+						   XIDOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
-						   INT4OID, -1, 0, false);
+						   INT4OID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
-						   BOOLOID, -1, 0, false);
+						   BOOLOID, -1, 0);
 
-		funcctx->slot = TupleDescGetSlot(tupdesc);
+		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 
 		/*
 		 * Collect all the locking information that we will format and
@@ -173,9 +173,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
 								 CStringGetDatum(GetLockmodeName(mode)));
 		values[5] = BoolGetDatum(granted);
 
-		tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
-							   values, nulls);
-		result = TupleGetDatum(funcctx->slot, tuple);
+		tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
+		result = HeapTupleGetDatum(tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 71b99553bc16128bcc243fbcd136dfd2bd416d77..0526f52e67784bf7aef35bfa68bc0a248d1c3b9d 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.12 2003/11/29 19:51:59 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,59 +27,6 @@
 #include "utils/builtins.h"
 
 
-/*
- * record_in		- input routine for pseudo-type RECORD.
- */
-Datum
-record_in(PG_FUNCTION_ARGS)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot accept a value of type record")));
-
-	PG_RETURN_VOID();			/* keep compiler quiet */
-}
-
-/*
- * record_out		- output routine for pseudo-type RECORD.
- */
-Datum
-record_out(PG_FUNCTION_ARGS)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot display a value of type record")));
-
-	PG_RETURN_VOID();			/* keep compiler quiet */
-}
-
-/*
- * record_recv		- binary input routine for pseudo-type RECORD.
- */
-Datum
-record_recv(PG_FUNCTION_ARGS)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot accept a value of type record")));
-
-	PG_RETURN_VOID();			/* keep compiler quiet */
-}
-
-/*
- * record_send		- binary output routine for pseudo-type RECORD.
- */
-Datum
-record_send(PG_FUNCTION_ARGS)
-{
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("cannot display a value of type record")));
-
-	PG_RETURN_VOID();			/* keep compiler quiet */
-}
-
-
 /*
  * cstring_in		- input routine for pseudo-type CSTRING.
  *
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
new file mode 100644
index 0000000000000000000000000000000000000000..b487dfc9047e7afaa54466897af27905d86dc07d
--- /dev/null
+++ b/src/backend/utils/adt/rowtypes.c
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * rowtypes.c
+ *	  I/O functions for generic composite types.
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.1 2004/04/01 21:28:45 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+
+
+/*
+ * record_in		- input routine for any composite type.
+ */
+Datum
+record_in(PG_FUNCTION_ARGS)
+{
+	/* Need to decide on external format before we can write this */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("input of composite types not implemented yet")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * record_out		- output routine for any composite type.
+ */
+Datum
+record_out(PG_FUNCTION_ARGS)
+{
+	/* Need to decide on external format before we can write this */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("output of composite types not implemented yet")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * record_recv		- binary input routine for any composite type.
+ */
+Datum
+record_recv(PG_FUNCTION_ARGS)
+{
+	/* Need to decide on external format before we can write this */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("input of composite types not implemented yet")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * record_send		- binary output routine for any composite type.
+ */
+Datum
+record_send(PG_FUNCTION_ARGS)
+{
+	/* Need to decide on external format before we can write this */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("output of composite types not implemented yet")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
deleted file mode 100644
index 33f1ed1a4a84265d3819bb17e93d45ac4c6f2488..0000000000000000000000000000000000000000
--- a/src/backend/utils/adt/sets.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sets.c
- *	  Functions for sets, which are defined by queries.
- *	  Example:	 a set is defined as being the result of the query
- *			retrieve (X.all)
- *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/sets.c,v 1.62 2004/01/06 23:55:18 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/indexing.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_proc.h"
-#include "executor/executor.h"
-#include "utils/fmgroids.h"
-#include "utils/sets.h"
-#include "utils/syscache.h"
-
-
-/*
- *	  SetDefine		   - converts query string defining set to an oid
- *
- *	  We create an SQL function having the given querystring as its body.
- *	  The name of the function is then changed to use the OID of its tuple
- *	  in pg_proc.
- */
-Oid
-SetDefine(char *querystr, Oid elemType)
-{
-	Oid			setoid;
-	char	   *procname = GENERICSETNAME;
-	char	   *fileName = "-";
-	char		realprocname[NAMEDATALEN];
-	HeapTuple	tup,
-				newtup = NULL;
-	Form_pg_proc proc;
-	Relation	procrel;
-	int			i;
-	Datum		replValue[Natts_pg_proc];
-	char		replNull[Natts_pg_proc];
-	char		repl[Natts_pg_proc];
-
-	setoid = ProcedureCreate(procname,	/* changed below, after oid known */
-							 PG_CATALOG_NAMESPACE,		/* XXX wrong */
-							 false,		/* don't replace */
-							 true,		/* returnsSet */
-							 elemType,	/* returnType */
-							 SQLlanguageId,		/* language */
-							 F_FMGR_SQL_VALIDATOR,
-							 querystr,	/* prosrc */
-							 fileName,	/* probin */
-							 false,		/* not aggregate */
-							 false,		/* security invoker */
-							 false,		/* isStrict (irrelevant, no args) */
-							 PROVOLATILE_VOLATILE,		/* assume unsafe */
-							 0,			/* parameterCount */
-							 NULL,		/* parameterTypes */
-							 NULL);		/* parameterNames */
-
-	/*
-	 * Since we're still inside this command of the transaction, we can't
-	 * see the results of the procedure definition unless we pretend we've
-	 * started the next command.  (Postgres's solution to the Halloween
-	 * problem is to not allow you to see the results of your command
-	 * until you start the next command.)
-	 */
-	CommandCounterIncrement();
-
-	procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
-	tup = SearchSysCache(PROCOID,
-						 ObjectIdGetDatum(setoid),
-						 0, 0, 0);
-	if (!HeapTupleIsValid(tup))
-		elog(ERROR, "cache lookup failed for function %u", setoid);
-
-	/*
-	 * We can tell whether the set was already defined by checking the
-	 * name.   If it's GENERICSETNAME, the set is new.  If it's "set<some
-	 * oid>" it's already defined.
-	 */
-	proc = (Form_pg_proc) GETSTRUCT(tup);
-	if (strcmp(procname, NameStr(proc->proname)) == 0)
-	{
-		/* make the real proc name */
-		snprintf(realprocname, sizeof(realprocname), "set%u", setoid);
-
-		/* set up the attributes to be modified or kept the same */
-		repl[0] = 'r';
-		for (i = 1; i < Natts_pg_proc; i++)
-			repl[i] = ' ';
-		replValue[0] = (Datum) realprocname;
-		for (i = 1; i < Natts_pg_proc; i++)
-			replValue[i] = (Datum) 0;
-		for (i = 0; i < Natts_pg_proc; i++)
-			replNull[i] = ' ';
-
-		/* change the pg_proc tuple */
-		newtup = heap_modifytuple(tup,
-								  procrel,
-								  replValue,
-								  replNull,
-								  repl);
-
-		simple_heap_update(procrel, &newtup->t_self, newtup);
-
-		setoid = HeapTupleGetOid(newtup);
-
-		CatalogUpdateIndexes(procrel, newtup);
-
-		heap_freetuple(newtup);
-	}
-
-	ReleaseSysCache(tup);
-
-	heap_close(procrel, RowExclusiveLock);
-
-	return setoid;
-}
-
-/*
- * This function executes set evaluation.  The parser sets up a set reference
- * as a call to this function with the OID of the set to evaluate as argument.
- *
- * We build a new fcache for execution of the set's function and run the
- * function until it says "no mas".  The fn_extra field of the call's
- * FmgrInfo record is a handy place to hold onto the fcache.  (Since this
- * is a built-in function, there is no competing use of fn_extra.)
- */
-Datum
-seteval(PG_FUNCTION_ARGS)
-{
-	Oid			funcoid = PG_GETARG_OID(0);
-	FuncExprState *fcache;
-	Datum		result;
-	bool		isNull;
-	ExprDoneCond isDone;
-
-	/*
-	 * If this is the first call, we need to set up the fcache for the
-	 * target set's function.
-	 */
-	fcache = (FuncExprState *) fcinfo->flinfo->fn_extra;
-	if (fcache == NULL)
-	{
-		MemoryContext oldcontext;
-		FuncExpr   *func;
-
-		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-
-		func = makeNode(FuncExpr);
-		func->funcid = funcoid;
-		func->funcresulttype = InvalidOid;		/* nothing will look at
-												 * this */
-		func->funcretset = true;
-		func->funcformat = COERCE_EXPLICIT_CALL;
-		func->args = NIL;		/* there are no arguments */
-
-		fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL);
-
-		MemoryContextSwitchTo(oldcontext);
-
-		init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt);
-
-		fcinfo->flinfo->fn_extra = (void *) fcache;
-	}
-
-	/*
-	 * Evaluate the function.  NOTE: we need no econtext because there are
-	 * no arguments to evaluate.
-	 */
-
-	/* ExecMakeFunctionResult assumes these are initialized at call: */
-	isNull = false;
-	isDone = ExprSingleResult;
-
-	result = ExecMakeFunctionResult(fcache,
-									NULL,		/* no econtext, see above */
-									&isNull,
-									&isDone);
-
-	/*
-	 * Return isNull/isDone status.
-	 */
-	fcinfo->isnull = isNull;
-
-	if (isDone != ExprSingleResult)
-	{
-		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
-		if (rsi && IsA(rsi, ReturnSetInfo))
-			rsi->isDone = isDone;
-		else
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("set-valued function called in context that "
-							"cannot accept a set")));
-	}
-
-	PG_RETURN_DATUM(result);
-}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 5c302e18d39630ed8c6badffa3f02253211ca181..85ad6ffe78867490ac8ad45a0c75991dbe351c35 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.200 2004/03/16 05:05:58 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.201 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,6 +63,7 @@
 #include "utils/lsyscache.h"
 #include "utils/relcache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /*
@@ -432,7 +433,10 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 	AttrDefault *attrdef = NULL;
 	int			ndef = 0;
 
-	relation->rd_att->tdhasoid = RelationGetForm(relation)->relhasoids;
+	/* copy some fields from pg_class row to rd_att */
+	relation->rd_att->tdtypeid = relation->rd_rel->reltype;
+	relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
+	relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
 
 	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
 												sizeof(TupleConstr));
@@ -1312,9 +1316,12 @@ formrdesc(const char *relationName,
 	 * Unlike the case with the relation tuple, this data had better be right
 	 * because it will never be replaced.  The input values must be
 	 * correctly defined by macros in src/include/catalog/ headers.
+	 *
+	 * Note however that rd_att's tdtypeid, tdtypmod, tdhasoid fields are
+	 * not right at this point.  They will be fixed later when the real
+	 * pg_class row is loaded.
 	 */
-	relation->rd_att = CreateTemplateTupleDesc(natts,
-										   relation->rd_rel->relhasoids);
+	relation->rd_att = CreateTemplateTupleDesc(natts, false);
 
 	/*
 	 * initialize tuple desc info
@@ -1595,6 +1602,7 @@ RelationReloadClassinfo(Relation relation)
 static void
 RelationClearRelation(Relation relation, bool rebuild)
 {
+	Oid			old_reltype = relation->rd_rel->reltype;
 	MemoryContext oldcxt;
 
 	/*
@@ -1679,6 +1687,7 @@ RelationClearRelation(Relation relation, bool rebuild)
 	if (!rebuild)
 	{
 		/* ok to zap remaining substructure */
+		flush_rowtype_cache(old_reltype);
 		FreeTupleDesc(relation->rd_att);
 		if (relation->rd_rulescxt)
 			MemoryContextDelete(relation->rd_rulescxt);
@@ -1704,6 +1713,7 @@ RelationClearRelation(Relation relation, bool rebuild)
 		if (RelationBuildDesc(buildinfo, relation) != relation)
 		{
 			/* Should only get here if relation was deleted */
+			flush_rowtype_cache(old_reltype);
 			FreeTupleDesc(old_att);
 			if (old_rulescxt)
 				MemoryContextDelete(old_rulescxt);
@@ -1715,11 +1725,15 @@ RelationClearRelation(Relation relation, bool rebuild)
 		relation->rd_isnew = old_isnew;
 		if (equalTupleDescs(old_att, relation->rd_att))
 		{
+			/* needn't flush typcache here */
 			FreeTupleDesc(relation->rd_att);
 			relation->rd_att = old_att;
 		}
 		else
+		{
+			flush_rowtype_cache(old_reltype);
 			FreeTupleDesc(old_att);
+		}
 		if (equalRuleLocks(old_rules, relation->rd_rules))
 		{
 			if (relation->rd_rulescxt)
@@ -2329,6 +2343,12 @@ RelationCacheInitializePhase2(void)
 			 */
 			Assert(relation->rd_rel != NULL);
 			memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
+
+			/*
+			 * Also update the derived fields in rd_att.
+			 */
+			relation->rd_att->tdtypeid = relp->reltype;
+			relation->rd_att->tdtypmod = -1;	/* unnecessary, but... */
 			relation->rd_att->tdhasoid = relp->relhasoids;
 
 			ReleaseSysCache(htup);
@@ -2918,6 +2938,8 @@ load_relcache_init_file(void)
 		/* initialize attribute tuple forms */
 		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
 											  relform->relhasoids);
+		rel->rd_att->tdtypeid = relform->reltype;
+		rel->rd_att->tdtypmod = -1;			/* unnecessary, but... */
 
 		/* next read all the attribute tuple form data entries */
 		has_not_null = false;
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index d6e560c34c502c388f7dbc9953c460bc17eab2cf..7a8e67c83c8f82944b421130b2b5db0209833239 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -28,12 +28,15 @@
  * doesn't cope with opclasses changing under it, either, so this seems
  * a low-priority problem.
  *
+ * We do support clearing the tuple descriptor part of a rowtype's cache
+ * entry, since that may need to change as a consequence of ALTER TABLE.
+ *
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.4 2003/11/29 19:52:00 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.5 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,11 +56,42 @@
 #include "utils/fmgroids.h"
 #include "utils/hsearch.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/typcache.h"
 
 
+/* The main type cache hashtable searched by lookup_type_cache */
 static HTAB *TypeCacheHash = NULL;
 
+/*
+ * We use a separate table for storing the definitions of non-anonymous
+ * record types.  Once defined, a record type will be remembered for the
+ * life of the backend.  Subsequent uses of the "same" record type (where
+ * sameness means equalTupleDescs) will refer to the existing table entry.
+ *
+ * Stored record types are remembered in a linear array of TupleDescs,
+ * which can be indexed quickly with the assigned typmod.  There is also
+ * a hash table to speed searches for matching TupleDescs.  The hash key
+ * uses just the first N columns' type OIDs, and so we may have multiple
+ * entries with the same hash key.
+ */
+#define REC_HASH_KEYS	16		/* use this many columns in hash key */
+
+typedef struct RecordCacheEntry
+{
+	/* the hash lookup key MUST BE FIRST */
+	Oid			hashkey[REC_HASH_KEYS];	/* column type IDs, zero-filled */
+
+	/* list of TupleDescs for record types with this hashkey */
+	List	   *tupdescs;
+} RecordCacheEntry;
+
+static HTAB *RecordCacheHash = NULL;
+
+static TupleDesc *RecordCacheArray = NULL;
+static int32 RecordCacheArrayLen = 0;	/* allocated length of array */
+static int32 NextRecordTypmod = 0;		/* number of entries used */
+
 
 static Oid lookup_default_opclass(Oid type_id, Oid am_id);
 
@@ -102,16 +136,26 @@ lookup_type_cache(Oid type_id, int flags)
 	if (typentry == NULL)
 	{
 		/*
-		 * If we didn't find one, we want to make one.  But first get the
-		 * required info from the pg_type row, just to make sure we don't
-		 * make a cache entry for an invalid type OID.
+		 * If we didn't find one, we want to make one.  But first look up
+		 * the pg_type row, just to make sure we don't make a cache entry
+		 * for an invalid type OID.
 		 */
-		int16	typlen;
-		bool	typbyval;
-		char	typalign;
+		HeapTuple	tp;
+		Form_pg_type typtup;
 
-		get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
+		tp = SearchSysCache(TYPEOID,
+							ObjectIdGetDatum(type_id),
+							0, 0, 0);
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup failed for type %u", type_id);
+		typtup = (Form_pg_type) GETSTRUCT(tp);
+		if (!typtup->typisdefined)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("type \"%s\" is only a shell",
+							NameStr(typtup->typname))));
 
+		/* Now make the typcache entry */
 		typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
 												  (void *) &type_id,
 												  HASH_ENTER, &found);
@@ -123,13 +167,20 @@ lookup_type_cache(Oid type_id, int flags)
 
 		MemSet(typentry, 0, sizeof(TypeCacheEntry));
 		typentry->type_id = type_id;
-		typentry->typlen = typlen;
-		typentry->typbyval = typbyval;
-		typentry->typalign = typalign;
+		typentry->typlen = typtup->typlen;
+		typentry->typbyval = typtup->typbyval;
+		typentry->typalign = typtup->typalign;
+		typentry->typtype = typtup->typtype;
+		typentry->typrelid = typtup->typrelid;
+
+		ReleaseSysCache(tp);
 	}
 
 	/* If we haven't already found the opclass, try to do so */
-	if (flags != 0 && typentry->btree_opc == InvalidOid)
+	if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
+				  TYPECACHE_CMP_PROC |
+				  TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
+		typentry->btree_opc == InvalidOid)
 	{
 		typentry->btree_opc = lookup_default_opclass(type_id,
 													 BTREE_AM_OID);
@@ -215,6 +266,30 @@ lookup_type_cache(Oid type_id, int flags)
 					  CacheMemoryContext);
 	}
 
+	/*
+	 * If it's a composite type (row type), get tupdesc if requested
+	 */
+	if ((flags & TYPECACHE_TUPDESC) &&
+		typentry->tupDesc == NULL &&
+		typentry->typtype == 'c')
+	{
+		Relation	rel;
+
+		if (!OidIsValid(typentry->typrelid)) /* should not happen */
+			elog(ERROR, "invalid typrelid for composite type %u",
+				 typentry->type_id);
+		rel = relation_open(typentry->typrelid, AccessShareLock);
+		Assert(rel->rd_rel->reltype == typentry->type_id);
+		/*
+		 * Notice that we simply store a link to the relcache's tupdesc.
+		 * Since we are relying on relcache to detect cache flush events,
+		 * there's not a lot of point to maintaining an independent copy.
+		 */
+		typentry->tupDesc = RelationGetDescr(rel);
+
+		relation_close(rel, AccessShareLock);
+	}
+
 	return typentry;
 }
 
@@ -296,3 +371,172 @@ lookup_default_opclass(Oid type_id, Oid am_id)
 
 	return InvalidOid;
 }
+
+
+/*
+ * lookup_rowtype_tupdesc
+ *
+ * Given a typeid/typmod that should describe a known composite type,
+ * return the tuple descriptor for the type.  Will ereport on failure.
+ *
+ * Note: returned TupleDesc points to cached copy; caller must copy it
+ * if intending to scribble on it or keep a reference for a long time.
+ */
+TupleDesc
+lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
+{
+	if (type_id != RECORDOID)
+	{
+		/*
+		 * It's a named composite type, so use the regular typcache.
+		 */
+		TypeCacheEntry *typentry;
+
+		typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
+		/* this should not happen unless caller messed up: */
+		if (typentry->tupDesc == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("type %u is not composite",
+							type_id)));
+		return typentry->tupDesc;
+	}
+	else
+	{
+		/*
+		 * It's a transient record type, so look in our record-type table.
+		 */
+		if (typmod < 0 || typmod >= NextRecordTypmod)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("record type has not been registered")));
+		}
+		return RecordCacheArray[typmod];
+	}
+}
+
+
+/*
+ * assign_record_type_typmod
+ *
+ * Given a tuple descriptor for a RECORD type, find or create a cache entry
+ * for the type, and set the tupdesc's tdtypmod field to a value that will
+ * identify this cache entry to lookup_rowtype_tupdesc.
+ */
+void
+assign_record_type_typmod(TupleDesc tupDesc)
+{
+	RecordCacheEntry *recentry;
+	TupleDesc	entDesc;
+	Oid			hashkey[REC_HASH_KEYS];
+	bool		found;
+	int			i;
+	List	   *l;
+	int32		newtypmod;
+	MemoryContext oldcxt;
+
+	Assert(tupDesc->tdtypeid == RECORDOID);
+
+	if (RecordCacheHash == NULL)
+	{
+		/* First time through: initialize the hash table */
+		HASHCTL		ctl;
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		MemSet(&ctl, 0, sizeof(ctl));
+		ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
+		ctl.entrysize = sizeof(RecordCacheEntry);
+		ctl.hash = tag_hash;
+		RecordCacheHash = hash_create("Record information cache", 64,
+									  &ctl, HASH_ELEM | HASH_FUNCTION);
+	}
+
+	/* Find or create a hashtable entry for this hash class */
+	MemSet(hashkey, 0, sizeof(hashkey));
+	for (i = 0; i < tupDesc->natts; i++)
+	{
+		if (i >= REC_HASH_KEYS)
+			break;
+		hashkey[i] = tupDesc->attrs[i]->atttypid;
+	}
+	recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
+												(void *) hashkey,
+												HASH_ENTER, &found);
+	if (recentry == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+	if (!found)
+	{
+		/* New entry ... hash_search initialized only the hash key */
+		recentry->tupdescs = NIL;
+	}
+
+	/* Look for existing record cache entry */
+	foreach(l, recentry->tupdescs)
+	{
+		entDesc = (TupleDesc) lfirst(l);
+		if (equalTupleDescs(tupDesc, entDesc))
+		{
+			tupDesc->tdtypmod = entDesc->tdtypmod;
+			return;
+		}
+	}
+
+	/* Not present, so need to manufacture an entry */
+	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+	if (RecordCacheArray == NULL)
+	{
+		RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
+		RecordCacheArrayLen = 64;
+	}
+	else if (NextRecordTypmod >= RecordCacheArrayLen)
+	{
+		int32 newlen = RecordCacheArrayLen * 2;
+
+		RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
+												  newlen * sizeof(TupleDesc));
+		RecordCacheArrayLen = newlen;
+	}
+
+	/* if fail in subrs, no damage except possibly some wasted memory... */
+	entDesc = CreateTupleDescCopy(tupDesc);
+	recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+	/* now it's safe to advance NextRecordTypmod */
+	newtypmod = NextRecordTypmod++;
+	entDesc->tdtypmod = newtypmod;
+	RecordCacheArray[newtypmod] = entDesc;
+
+	/* report to caller as well */
+	tupDesc->tdtypmod = newtypmod;
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * flush_rowtype_cache
+ *
+ * If a typcache entry exists for a rowtype, delete the entry's cached
+ * tuple descriptor link.  This is called from relcache.c when a cached
+ * relation tupdesc is about to be dropped.
+ */
+void
+flush_rowtype_cache(Oid type_id)
+{
+	TypeCacheEntry *typentry;
+
+	if (TypeCacheHash == NULL)
+		return;					/* no table, so certainly no entry */
+
+	typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
+											  (void *) &type_id,
+											  HASH_FIND, NULL);
+	if (typentry == NULL)
+		return;					/* no matching entry */
+
+	typentry->tupDesc = NULL;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e6ef3fcec3c9e9c7e82c8d239436a3023fc0950e..dac4a8916e14c8042e9ae672a5ded426e3d2913e 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.13 2003/12/19 00:02:11 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.14 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,9 +49,8 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 		 * Allocate suitably long-lived space and zero it
 		 */
 		retval = (FuncCallContext *)
-			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-							   sizeof(FuncCallContext));
-		MemSet(retval, 0, sizeof(FuncCallContext));
+			MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
+								   sizeof(FuncCallContext));
 
 		/*
 		 * initialize the elements
@@ -61,6 +60,7 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 		retval->slot = NULL;
 		retval->user_fctx = NULL;
 		retval->attinmeta = NULL;
+		retval->tuple_desc = NULL;
 		retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
 
 		/*
@@ -104,8 +104,11 @@ per_MultiFuncCall(PG_FUNCTION_ARGS)
 	 * FuncCallContext is pointing to it), but in most usage patterns the
 	 * tuples stored in it will be in the function's per-tuple context. So
 	 * at the beginning of each call, the Slot will hold a dangling
-	 * pointer to an already-recycled tuple.  We clear it out here.  (See
-	 * also the definition of TupleGetDatum() in funcapi.h!)
+	 * pointer to an already-recycled tuple.  We clear it out here.
+	 *
+	 * Note: use of retval->slot is obsolete as of 7.5, and we expect that
+	 * it will always be NULL.  This is just here for backwards compatibility
+	 * in case someone creates a slot anyway.
 	 */
 	if (retval->slot != NULL)
 		ExecClearTuple(retval->slot);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bc2499ed3afc6956a967c02104a4152f38836e47..26f4210ad0e035f0ed128717b08b8d2556a4fd7d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.194 2004/04/01 14:25:47 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.195 2004/04/01 21:28:45 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -3453,9 +3453,9 @@ GetPGVariableResultDesc(const char *name)
 		/* need a tuple descriptor representing two TEXT columns */
 		tupdesc = CreateTemplateTupleDesc(2, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 	}
 	else
 	{
@@ -3467,7 +3467,7 @@ GetPGVariableResultDesc(const char *name)
 		/* need a tuple descriptor representing a single TEXT column */
 		tupdesc = CreateTemplateTupleDesc(1, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 	}
 	return tupdesc;
 }
@@ -3507,7 +3507,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
 	/* need a tuple descriptor representing a single TEXT column */
 	tupdesc = CreateTemplateTupleDesc(1, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
-					   TEXTOID, -1, 0, false);
+					   TEXTOID, -1, 0);
 
 	/* prepare for projection of tuples */
 	tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3532,9 +3532,9 @@ ShowAllGUCConfig(DestReceiver *dest)
 	/* need a tuple descriptor representing two TEXT columns */
 	tupdesc = CreateTemplateTupleDesc(2, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
-					   TEXTOID, -1, 0, false);
+					   TEXTOID, -1, 0);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
-					   TEXTOID, -1, 0, false);
+					   TEXTOID, -1, 0);
 
 	/* prepare for projection of tuples */
 	tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3740,7 +3740,6 @@ show_all_settings(PG_FUNCTION_ARGS)
 	TupleDesc	tupdesc;
 	int			call_cntr;
 	int			max_calls;
-	TupleTableSlot *slot;
 	AttInMetadata *attinmeta;
 	MemoryContext oldcontext;
 
@@ -3762,31 +3761,25 @@ show_all_settings(PG_FUNCTION_ARGS)
 		 */
 		tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "category",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "short_desc",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "extra_desc",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "context",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "vartype",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "source",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "min_val",
-						   TEXTOID, -1, 0, false);
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "max_val",
-						   TEXTOID, -1, 0, false);
-
-		/* allocate a slot for a tuple with this tupdesc */
-		slot = TupleDescGetSlot(tupdesc);
-
-		/* assign slot to function context */
-		funcctx->slot = slot;
+						   TEXTOID, -1, 0);
 
 		/*
 		 * Generate attribute metadata needed later to produce tuples from
@@ -3806,7 +3799,6 @@ show_all_settings(PG_FUNCTION_ARGS)
 
 	call_cntr = funcctx->call_cntr;
 	max_calls = funcctx->max_calls;
-	slot = funcctx->slot;
 	attinmeta = funcctx->attinmeta;
 
 	if (call_cntr < max_calls)	/* do when there is more left to send */
@@ -3837,7 +3829,7 @@ show_all_settings(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = TupleGetDatum(slot, tuple);
+		result = HeapTupleGetDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index c6579ea246282c2adeb1b7a55c52558947251ad9..9f91d81107ddb3cc032d7493ffb5bdc482846c08 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.87 2004/03/11 01:47:41 ishii Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.88 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,11 +111,12 @@ extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
 				fastgetattr((tup), (attnum), (tupleDesc), (isnull)) \
 		) \
 		: \
-			heap_getsysattr((tup), (attnum), (isnull)) \
+			heap_getsysattr((tup), (attnum), (tupleDesc), (isnull)) \
 	) \
 )
 
-extern Datum heap_getsysattr(HeapTuple tup, int attnum, bool *isnull);
+extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
+							 bool *isnull);
 
 
 /* ----------------
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index a47e668f9b26d1e242a881452ea3fb94ca8480aa..3d48b5f45a3ad5657647881150453152cfb367b4 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.64 2004/01/16 20:51:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.65 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,10 +47,19 @@
 #define MaxHeapAttributeNumber	1600	/* 8 * 200 */
 
 /*----------
- * On-disk heap tuple header.  Currently this is also used as the header
- * format for tuples formed in memory, although in principle they could
- * be different.  To avoid wasting space, the fields should be layed out
- * in such a way to avoid structure padding.
+ * Heap tuple header.  To avoid wasting space, the fields should be
+ * layed out in such a way to avoid structure padding.
+ *
+ * Datums of composite types (row types) share the same general structure
+ * as on-disk tuples, so that the same routines can be used to build and
+ * examine them.  However the requirements are slightly different: a Datum
+ * does not need any transaction visibility information, and it does need
+ * a length word and some embedded type information.  We can achieve this
+ * by overlaying the xmin/cmin/xmax/cmax/xvac fields of a heap tuple
+ * with the fields needed in the Datum case.  Typically, all tuples built
+ * in-memory will be initialized with the Datum fields; but when a tuple is
+ * about to be inserted in a table, the transaction fields will be filled,
+ * overwriting the datum fields.
  *
  * The overall structure of a heap tuple looks like:
  *			fixed fields (HeapTupleHeaderData struct)
@@ -96,7 +105,8 @@
  * MAXALIGN.
  *----------
  */
-typedef struct HeapTupleHeaderData
+
+typedef struct HeapTupleFields
 {
 	TransactionId t_xmin;		/* inserting xact ID */
 
@@ -111,6 +121,28 @@ typedef struct HeapTupleHeaderData
 		CommandId	t_cmax;		/* deleting command ID */
 		TransactionId t_xvac;	/* VACUUM FULL xact ID */
 	}			t_field3;
+} HeapTupleFields;
+
+typedef struct DatumTupleFields
+{
+	int32		datum_len;		/* required to be a varlena type */
+
+	int32		datum_typmod;	/* -1, or identifier of a record type */
+
+	Oid			datum_typeid;	/* composite type OID, or RECORDOID */
+	/*
+	 * Note: field ordering is chosen with thought that Oid might someday
+	 * widen to 64 bits.
+	 */
+} DatumTupleFields;
+
+typedef struct HeapTupleHeaderData
+{
+	union
+	{
+		HeapTupleFields		t_heap;
+		DatumTupleFields	t_datum;
+	}			t_choice;
 
 	ItemPointerData t_ctid;		/* current TID of this or newer tuple */
 
@@ -169,31 +201,31 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 
 #define HeapTupleHeaderGetXmin(tup) \
 ( \
-	(tup)->t_xmin \
+	(tup)->t_choice.t_heap.t_xmin \
 )
 
 #define HeapTupleHeaderSetXmin(tup, xid) \
 ( \
-	TransactionIdStore((xid), &(tup)->t_xmin) \
+	TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmin) \
 )
 
 #define HeapTupleHeaderGetXmax(tup) \
 ( \
 	((tup)->t_infomask & HEAP_XMAX_IS_XMIN) ? \
-		(tup)->t_xmin \
+		(tup)->t_choice.t_heap.t_xmin \
 	: \
-		(tup)->t_field2.t_xmax \
+		(tup)->t_choice.t_heap.t_field2.t_xmax \
 )
 
 #define HeapTupleHeaderSetXmax(tup, xid) \
 do { \
 	TransactionId	_newxid = (xid); \
-	if (TransactionIdEquals((tup)->t_xmin, _newxid)) \
+	if (TransactionIdEquals((tup)->t_choice.t_heap.t_xmin, _newxid)) \
 		(tup)->t_infomask |= HEAP_XMAX_IS_XMIN; \
 	else \
 	{ \
 		(tup)->t_infomask &= ~HEAP_XMAX_IS_XMIN; \
-		TransactionIdStore(_newxid, &(tup)->t_field2.t_xmax); \
+		TransactionIdStore(_newxid, &(tup)->t_choice.t_heap.t_field2.t_xmax); \
 	} \
 } while (0)
 
@@ -207,13 +239,13 @@ do { \
  */
 #define HeapTupleHeaderGetCmin(tup) \
 ( \
-	(tup)->t_field2.t_cmin \
+	(tup)->t_choice.t_heap.t_field2.t_cmin \
 )
 
 #define HeapTupleHeaderSetCmin(tup, cid) \
 do { \
 	Assert((tup)->t_infomask & HEAP_XMAX_INVALID); \
-	(tup)->t_field2.t_cmin = (cid); \
+	(tup)->t_choice.t_heap.t_field2.t_cmin = (cid); \
 } while (0)
 
 /*
@@ -222,19 +254,19 @@ do { \
  */
 #define HeapTupleHeaderGetCmax(tup) \
 ( \
-	(tup)->t_field3.t_cmax \
+	(tup)->t_choice.t_heap.t_field3.t_cmax \
 )
 
 #define HeapTupleHeaderSetCmax(tup, cid) \
 do { \
 	Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-	(tup)->t_field3.t_cmax = (cid); \
+	(tup)->t_choice.t_heap.t_field3.t_cmax = (cid); \
 } while (0)
 
 #define HeapTupleHeaderGetXvac(tup) \
 ( \
 	((tup)->t_infomask & HEAP_MOVED) ? \
-		(tup)->t_field3.t_xvac \
+		(tup)->t_choice.t_heap.t_field3.t_xvac \
 	: \
 		InvalidTransactionId \
 )
@@ -242,9 +274,39 @@ do { \
 #define HeapTupleHeaderSetXvac(tup, xid) \
 do { \
 	Assert((tup)->t_infomask & HEAP_MOVED); \
-	TransactionIdStore((xid), &(tup)->t_field3.t_xvac); \
+	TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_field3.t_xvac); \
 } while (0)
 
+#define HeapTupleHeaderGetDatumLength(tup) \
+( \
+	(tup)->t_choice.t_datum.datum_len \
+)
+
+#define HeapTupleHeaderSetDatumLength(tup, len) \
+( \
+	(tup)->t_choice.t_datum.datum_len = (len) \
+)
+
+#define HeapTupleHeaderGetTypeId(tup) \
+( \
+	(tup)->t_choice.t_datum.datum_typeid \
+)
+
+#define HeapTupleHeaderSetTypeId(tup, typeid) \
+( \
+	(tup)->t_choice.t_datum.datum_typeid = (typeid) \
+)
+
+#define HeapTupleHeaderGetTypMod(tup) \
+( \
+	(tup)->t_choice.t_datum.datum_typmod \
+)
+
+#define HeapTupleHeaderSetTypMod(tup, typmod) \
+( \
+	(tup)->t_choice.t_datum.datum_typmod = (typmod) \
+)
+
 #define HeapTupleHeaderGetOid(tup) \
 ( \
 	((tup)->t_infomask & HEAP_HASOID) ? \
@@ -261,95 +323,10 @@ do { \
 
 
 /*
- * WAL record definitions for heapam.c's WAL operations
- *
- * XLOG allows to store some information in high 4 bits of log
- * record xl_info field
- */
-#define XLOG_HEAP_INSERT	0x00
-#define XLOG_HEAP_DELETE	0x10
-#define XLOG_HEAP_UPDATE	0x20
-#define XLOG_HEAP_MOVE		0x30
-#define XLOG_HEAP_CLEAN		0x40
-#define XLOG_HEAP_OPMASK	0x70
-/*
- * When we insert 1st item on new page in INSERT/UPDATE
- * we can (and we do) restore entire page in redo
- */
-#define XLOG_HEAP_INIT_PAGE 0x80
-
-/*
- * All what we need to find changed tuple (14 bytes)
- *
- * NB: on most machines, sizeof(xl_heaptid) will include some trailing pad
- * bytes for alignment.  We don't want to store the pad space in the XLOG,
- * so use SizeOfHeapTid for space calculations.  Similar comments apply for
- * the other xl_FOO structs.
- */
-typedef struct xl_heaptid
-{
-	RelFileNode node;
-	ItemPointerData tid;		/* changed tuple id */
-} xl_heaptid;
-
-#define SizeOfHeapTid		(offsetof(xl_heaptid, tid) + SizeOfIptrData)
-
-/* This is what we need to know about delete */
-typedef struct xl_heap_delete
-{
-	xl_heaptid	target;			/* deleted tuple id */
-} xl_heap_delete;
-
-#define SizeOfHeapDelete	(offsetof(xl_heap_delete, target) + SizeOfHeapTid)
-
-/*
- * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
- * or updated tuple in WAL; we can save a few bytes by reconstructing the
- * fields that are available elsewhere in the WAL record, or perhaps just
- * plain needn't be reconstructed.  These are the fields we must store.
- * NOTE: t_hoff could be recomputed, but we may as well store it because
- * it will come for free due to alignment considerations.
+ * BITMAPLEN(NATTS) -
+ *		Computes size of null bitmap given number of data columns.
  */
-typedef struct xl_heap_header
-{
-	int16		t_natts;
-	uint16		t_infomask;
-	uint8		t_hoff;
-} xl_heap_header;
-
-#define SizeOfHeapHeader	(offsetof(xl_heap_header, t_hoff) + sizeof(uint8))
-
-/* This is what we need to know about insert */
-typedef struct xl_heap_insert
-{
-	xl_heaptid	target;			/* inserted tuple id */
-	/* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */
-} xl_heap_insert;
-
-#define SizeOfHeapInsert	(offsetof(xl_heap_insert, target) + SizeOfHeapTid)
-
-/* This is what we need to know about update|move */
-typedef struct xl_heap_update
-{
-	xl_heaptid	target;			/* deleted tuple id */
-	ItemPointerData newtid;		/* new inserted tuple id */
-	/* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */
-	/* and TUPLE DATA FOLLOWS AT END OF STRUCT */
-} xl_heap_update;
-
-#define SizeOfHeapUpdate	(offsetof(xl_heap_update, newtid) + SizeOfIptrData)
-
-/* This is what we need to know about page cleanup */
-typedef struct xl_heap_clean
-{
-	RelFileNode node;
-	BlockNumber block;
-	/* UNUSED OFFSET NUMBERS FOLLOW AT THE END */
-} xl_heap_clean;
-
-#define SizeOfHeapClean (offsetof(xl_heap_clean, block) + sizeof(BlockNumber))
-
-
+#define BITMAPLEN(NATTS)	(((int)(NATTS) + 7) / 8)
 
 /*
  * MaxTupleSize is the maximum allowed size of a tuple, including header and
@@ -388,6 +365,7 @@ typedef struct xl_heap_clean
 #define TableOidAttributeNumber					(-7)
 #define FirstLowInvalidHeapAttributeNumber		(-8)
 
+
 /*
  * HeapTupleData is an in-memory data structure that points to a tuple.
  *
@@ -417,22 +395,13 @@ typedef HeapTupleData *HeapTuple;
 
 #define HEAPTUPLESIZE	MAXALIGN(sizeof(HeapTupleData))
 
-
 /*
  * GETSTRUCT - given a HeapTuple pointer, return address of the user data
  */
 #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
 
-
 /*
- * BITMAPLEN(NATTS) -
- *		Computes size of null bitmap given number of data columns.
- */
-#define BITMAPLEN(NATTS)	(((int)(NATTS) + 7) / 8)
-
-/*
- * HeapTupleIsValid
- *		True iff the heap tuple is valid.
+ * Accessor macros to be used with HeapTuple pointers.
  */
 #define HeapTupleIsValid(tuple) PointerIsValid(tuple)
 
@@ -463,4 +432,94 @@ typedef HeapTupleData *HeapTuple;
 #define HeapTupleSetOid(tuple, oid) \
 		HeapTupleHeaderSetOid((tuple)->t_data, (oid))
 
+
+/*
+ * WAL record definitions for heapam.c's WAL operations
+ *
+ * XLOG allows to store some information in high 4 bits of log
+ * record xl_info field
+ */
+#define XLOG_HEAP_INSERT	0x00
+#define XLOG_HEAP_DELETE	0x10
+#define XLOG_HEAP_UPDATE	0x20
+#define XLOG_HEAP_MOVE		0x30
+#define XLOG_HEAP_CLEAN		0x40
+#define XLOG_HEAP_OPMASK	0x70
+/*
+ * When we insert 1st item on new page in INSERT/UPDATE
+ * we can (and we do) restore entire page in redo
+ */
+#define XLOG_HEAP_INIT_PAGE 0x80
+
+/*
+ * All what we need to find changed tuple (14 bytes)
+ *
+ * NB: on most machines, sizeof(xl_heaptid) will include some trailing pad
+ * bytes for alignment.  We don't want to store the pad space in the XLOG,
+ * so use SizeOfHeapTid for space calculations.  Similar comments apply for
+ * the other xl_FOO structs.
+ */
+typedef struct xl_heaptid
+{
+	RelFileNode node;
+	ItemPointerData tid;		/* changed tuple id */
+} xl_heaptid;
+
+#define SizeOfHeapTid		(offsetof(xl_heaptid, tid) + SizeOfIptrData)
+
+/* This is what we need to know about delete */
+typedef struct xl_heap_delete
+{
+	xl_heaptid	target;			/* deleted tuple id */
+} xl_heap_delete;
+
+#define SizeOfHeapDelete	(offsetof(xl_heap_delete, target) + SizeOfHeapTid)
+
+/*
+ * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted
+ * or updated tuple in WAL; we can save a few bytes by reconstructing the
+ * fields that are available elsewhere in the WAL record, or perhaps just
+ * plain needn't be reconstructed.  These are the fields we must store.
+ * NOTE: t_hoff could be recomputed, but we may as well store it because
+ * it will come for free due to alignment considerations.
+ */
+typedef struct xl_heap_header
+{
+	int16		t_natts;
+	uint16		t_infomask;
+	uint8		t_hoff;
+} xl_heap_header;
+
+#define SizeOfHeapHeader	(offsetof(xl_heap_header, t_hoff) + sizeof(uint8))
+
+/* This is what we need to know about insert */
+typedef struct xl_heap_insert
+{
+	xl_heaptid	target;			/* inserted tuple id */
+	/* xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT */
+} xl_heap_insert;
+
+#define SizeOfHeapInsert	(offsetof(xl_heap_insert, target) + SizeOfHeapTid)
+
+/* This is what we need to know about update|move */
+typedef struct xl_heap_update
+{
+	xl_heaptid	target;			/* deleted tuple id */
+	ItemPointerData newtid;		/* new inserted tuple id */
+	/* NEW TUPLE xl_heap_header (PLUS xmax & xmin IF MOVE OP) */
+	/* and TUPLE DATA FOLLOWS AT END OF STRUCT */
+} xl_heap_update;
+
+#define SizeOfHeapUpdate	(offsetof(xl_heap_update, newtid) + SizeOfIptrData)
+
+/* This is what we need to know about page cleanup */
+typedef struct xl_heap_clean
+{
+	RelFileNode node;
+	BlockNumber block;
+	/* UNUSED OFFSET NUMBERS FOLLOW AT THE END */
+} xl_heap_clean;
+
+#define SizeOfHeapClean (offsetof(xl_heap_clean, block) + sizeof(BlockNumber))
+
 #endif   /* HTUP_H */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index bf4e3aa105528f78930a4ca7e9403de9275b4fa8..c7e560c0c65e53c5c50fa446bbce05cc9861aa6f 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.42 2003/11/29 22:40:55 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.43 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,15 @@ typedef struct tupleConstr
 /*
  * This structure contains all information (i.e. from Classes
  * pg_attribute, pg_attrdef, pg_constraint) for the structure of a tuple.
+ *
+ * Note that only user attributes, not system attributes, are mentioned in
+ * TupleDesc; with the exception that tdhasoid indicates if OID is present.
+ *
+ * If the tuple is known to correspond to a named rowtype (such as a table's
+ * rowtype) then tdtypeid identifies that type and tdtypmod is -1.  Otherwise
+ * tdtypeid is RECORDOID, and tdtypmod can be either -1 for a fully anonymous
+ * row type, or a value >= 0 to allow the rowtype to be looked up in the
+ * typcache.c type cache.
  */
 typedef struct tupleDesc
 {
@@ -51,6 +60,8 @@ typedef struct tupleDesc
 	Form_pg_attribute *attrs;
 	/* attrs[N] is a pointer to the description of Attribute Number N+1.  */
 	TupleConstr *constr;
+	Oid			tdtypeid;		/* composite type ID for tuple type */
+	int32		tdtypmod;		/* typmod for tuple type */
 	bool		tdhasoid;		/* Tuple has oid attribute in its header */
 }	*TupleDesc;
 
@@ -73,8 +84,7 @@ extern void TupleDescInitEntry(TupleDesc desc,
 				   const char *attributeName,
 				   Oid oidtypeid,
 				   int32 typmod,
-				   int attdim,
-				   bool attisset);
+				   int attdim);
 
 extern TupleDesc BuildDescForRelation(List *schema);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 5344d7afe8690d9564a60d14a0b15658ad11280f..82900e55f340db36b18a6e3698e18e78821d9053 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.222 2004/03/22 01:38:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.223 2004/04/01 21:28:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200403211
+#define CATALOG_VERSION_NO	200403291
 
 #endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 754878f0148be7680d3e42e4b8a8d076eb7647fe..896a06ada86eaf112baed40da976b149695aa109 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.108 2004/02/12 23:41:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.109 2004/04/01 21:28:45 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -129,9 +129,6 @@ CATALOG(pg_attribute) BOOTSTRAP BKI_WITHOUT_OIDS
 	 */
 	char		attstorage;
 
-	/* This flag indicates that the attribute is really a set */
-	bool		attisset;
-
 	/*
 	 * attalign is a copy of the typalign field from pg_type for this
 	 * attribute.  See atttypid comments above.
@@ -174,7 +171,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
  * ----------------
  */
 
-#define Natts_pg_attribute				18
+#define Natts_pg_attribute				17
 #define Anum_pg_attribute_attrelid		1
 #define Anum_pg_attribute_attname		2
 #define Anum_pg_attribute_atttypid		3
@@ -186,13 +183,12 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 #define Anum_pg_attribute_atttypmod		9
 #define Anum_pg_attribute_attbyval		10
 #define Anum_pg_attribute_attstorage	11
-#define Anum_pg_attribute_attisset		12
-#define Anum_pg_attribute_attalign		13
-#define Anum_pg_attribute_attnotnull	14
-#define Anum_pg_attribute_atthasdef		15
-#define Anum_pg_attribute_attisdropped	16
-#define Anum_pg_attribute_attislocal	17
-#define Anum_pg_attribute_attinhcount	18
+#define Anum_pg_attribute_attalign		12
+#define Anum_pg_attribute_attnotnull	13
+#define Anum_pg_attribute_atthasdef		14
+#define Anum_pg_attribute_attisdropped	15
+#define Anum_pg_attribute_attislocal	16
+#define Anum_pg_attribute_attinhcount	17
 
 
 
@@ -226,278 +222,276 @@ typedef FormData_pg_attribute *Form_pg_attribute;
  * ----------------
  */
 #define Schema_pg_type \
-{ 1247, {"typname"},	   19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typnamespace"},  26, -1,	4,	2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typowner"},	   23, -1,	4,	3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typlen"},		   21, -1,	2,	4, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1247, {"typbyval"},	   16, -1,	1,	5, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typtype"},	   18, -1,	1,	6, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typisdefined"},  16, -1,	1,	7, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typdelim"},	   18, -1,	1,	8, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typrelid"},	   26, -1,	4,	9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typelem"},	   26, -1,	4, 10, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typinput"},	   24, -1,	4, 11, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typoutput"},	   24, -1,	4, 12, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typreceive"},    24, -1,	4, 13, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typsend"},	   24, -1,	4, 14, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typanalyze"},	   24, -1,	4, 15, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typalign"},	   18, -1,	1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typstorage"},    18, -1,	1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnotnull"},    16, -1,	1, 18, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1247, {"typbasetype"},   26, -1,	4, 19, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typtypmod"},	   23, -1,	4, 20, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typndims"},	   23, -1,	4, 21, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1247, {"typdefault"},    25, -1, -1, 23, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-
-DATA(insert ( 1247 typname			19 -1 NAMEDATALEN	1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1247 typnamespace		26 -1 4   2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typowner			23 -1 4   3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typlen			21 -1 2   4 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1247 typbyval			16 -1 1   5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typtype			18 -1 1   6 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typisdefined		16 -1 1   7 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typdelim			18 -1 1   8 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typrelid			26 -1 4   9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typelem			26 -1 4  10 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typinput			24 -1 4  11 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typoutput		24 -1 4  12 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typreceive		24 -1 4  13 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typsend			24 -1 4  14 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typanalyze		24 -1 4  15 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typalign			18 -1 1  16 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typstorage		18 -1 1  17 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typnotnull		16 -1 1  18 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1247 typbasetype		26 -1 4  19 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typtypmod		23 -1 4  20 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typndims			23 -1 4  21 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 typdefaultbin	25 -1 -1 22 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1247 typdefault		25 -1 -1 23 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1247 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1247 oid				26 0  4  -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1247 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+{ 1247, {"typname"},	   19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typnamespace"},  26, -1,	4,	2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typowner"},	   23, -1,	4,	3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typlen"},		   21, -1,	2,	4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1247, {"typbyval"},	   16, -1,	1,	5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typtype"},	   18, -1,	1,	6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typisdefined"},  16, -1,	1,	7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typdelim"},	   18, -1,	1,	8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typrelid"},	   26, -1,	4,	9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typelem"},	   26, -1,	4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typinput"},	   24, -1,	4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typoutput"},	   24, -1,	4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typreceive"},    24, -1,	4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typsend"},	   24, -1,	4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typanalyze"},	   24, -1,	4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typalign"},	   18, -1,	1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typstorage"},    18, -1,	1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typnotnull"},    16, -1,	1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typbasetype"},   26, -1,	4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typtypmod"},	   23, -1,	4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typndims"},	   23, -1,	4, 21, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1247, {"typdefault"},    25, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+
+DATA(insert ( 1247 typname			19 -1 NAMEDATALEN	1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1247 typnamespace		26 -1 4   2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typowner			23 -1 4   3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typlen			21 -1 2   4 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1247 typbyval			16 -1 1   5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typtype			18 -1 1   6 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typisdefined		16 -1 1   7 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typdelim			18 -1 1   8 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typrelid			26 -1 4   9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typelem			26 -1 4  10 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typinput			24 -1 4  11 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typoutput		24 -1 4  12 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typreceive		24 -1 4  13 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typsend			24 -1 4  14 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typanalyze		24 -1 4  15 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typalign			18 -1 1  16 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typstorage		18 -1 1  17 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typnotnull		16 -1 1  18 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typbasetype		26 -1 4  19 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typtypmod		23 -1 4  20 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typndims			23 -1 4  21 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typdefaultbin	25 -1 -1 22 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 typdefault		25 -1 -1 23 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1247 oid				26 0  4  -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_database
  * ----------------
  */
-DATA(insert ( 1262 datname			19 -1 NAMEDATALEN   1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 datdba			23 -1 4   2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 encoding			23 -1 4   3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datistemplate	16 -1 1   4 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1262 datallowconn		16 -1 1   5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1262 datlastsysoid	26 -1 4   6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datvacuumxid		28 -1 4   7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 datfrozenxid		28 -1 4   8 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1262 datname			19 -1 NAMEDATALEN   1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1262 datdba			23 -1 4   2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 encoding			23 -1 4   3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datistemplate	16 -1 1   4 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1262 datallowconn		16 -1 1   5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1262 datlastsysoid	26 -1 4   6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datvacuumxid		28 -1 4   7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 datfrozenxid		28 -1 4   8 0 -1 -1 t p i t f f t 0));
 /* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */
-DATA(insert ( 1262 datpath			25 -1 -1  9 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 datconfig	  1009 -1 -1 10 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1262 datacl		  1034 -1 -1 11 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1262 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1262 oid				26 0  4  -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1262 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1262 datpath			25 -1 -1  9 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1262 datconfig	  1009 -1 -1 10 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1262 datacl		  1034 -1 -1 11 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1262 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1262 oid				26 0  4  -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1262 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_proc
  * ----------------
  */
 #define Schema_pg_proc \
-{ 1255, {"proname"},			19, -1, NAMEDATALEN,  1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"pronamespace"},		26, -1, 4,	2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proowner"},			23, -1,	4,	3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"prolang"},			26, -1,	4,	4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proisagg"},			16, -1, 1,	5, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"prosecdef"},			16, -1,	1,	6, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"proisstrict"},		16, -1,	1,	7, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"proretset"},			16, -1,	1,	8, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"provolatile"},		18, -1,	1,	9, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1255, {"pronargs"},			21, -1,	2, 10, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1255, {"prorettype"},			26, -1,	4, 11, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proargtypes"},		30, -1, INDEX_MAX_KEYS*4, 12, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1255, {"proargnames"},	  1009, -1, -1, 13, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"prosrc"},				25, -1, -1, 14, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"probin"},				17, -1, -1, 15, 0, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }, \
-{ 1255, {"proacl"},			  1034, -1, -1, 16, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1255 proname			19 -1 NAMEDATALEN	1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 pronamespace		26 -1 4   2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proowner			23 -1 4   3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 prolang			26 -1 4   4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proisagg			16 -1 1   5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 prosecdef		16 -1 1   6 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 proisstrict		16 -1 1   7 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 proretset		16 -1 1   8 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 provolatile		18 -1 1   9 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1255 pronargs			21 -1 2  10 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1255 prorettype		26 -1 4  11 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 proargtypes		30 -1 INDEX_MAX_KEYS*4 12 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 proargnames	  1009 -1 -1 13 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 prosrc			25 -1 -1 14 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 probin			17 -1 -1 15 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 proacl		  1034 -1 -1 16 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1255 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1255 oid				26 0  4  -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1255 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+{ 1255, {"proname"},			19, -1, NAMEDATALEN,  1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"pronamespace"},		26, -1, 4,	2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proowner"},			23, -1,	4,	3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"prolang"},			26, -1,	4,	4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proisagg"},			16, -1, 1,	5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"prosecdef"},			16, -1,	1,	6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"proisstrict"},		16, -1,	1,	7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"proretset"},			16, -1,	1,	8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"provolatile"},		18, -1,	1,	9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1255, {"pronargs"},			21, -1,	2, 10, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1255, {"prorettype"},			26, -1,	4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proargtypes"},		30, -1, INDEX_MAX_KEYS*4, 12, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1255, {"proargnames"},	  1009, -1, -1, 13, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"prosrc"},				25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"probin"},				17, -1, -1, 15, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1255, {"proacl"},			  1034, -1, -1, 16, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+DATA(insert ( 1255 proname			19 -1 NAMEDATALEN	1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1255 pronamespace		26 -1 4   2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proowner			23 -1 4   3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 prolang			26 -1 4   4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proisagg			16 -1 1   5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 prosecdef		16 -1 1   6 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 proisstrict		16 -1 1   7 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 proretset		16 -1 1   8 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 provolatile		18 -1 1   9 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1255 pronargs			21 -1 2  10 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1255 prorettype		26 -1 4  11 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 proargtypes		30 -1 INDEX_MAX_KEYS*4 12 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1255 proargnames	  1009 -1 -1 13 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 prosrc			25 -1 -1 14 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 probin			17 -1 -1 15 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 proacl		  1034 -1 -1 16 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1255 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1255 oid				26 0  4  -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_shadow
  * ----------------
  */
-DATA(insert ( 1260 usename			19	-1 NAMEDATALEN	1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1260 usesysid			23	-1	4	2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 usecreatedb		16	-1	1	3 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 usesuper			16	-1	1	4 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 usecatupd		16	-1	1	5 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1260 passwd			25	-1 -1	6 0 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1260 valuntil		   702	-1	4	7 0 -1 -1 t p f i f f f t 0));
-DATA(insert ( 1260 useconfig	  1009	-1 -1	8 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1260 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
+DATA(insert ( 1260 usename			19	-1 NAMEDATALEN	1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1260 usesysid			23	-1	4	2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 usecreatedb		16	-1	1	3 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 usesuper			16	-1	1	4 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 usecatupd		16	-1	1	5 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1260 passwd			25	-1 -1	6 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1260 valuntil		   702	-1	4	7 0 -1 -1 t p i f f f t 0));
+DATA(insert ( 1260 useconfig	  1009	-1 -1	8 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1260 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
 /* no OIDs in pg_shadow */
-DATA(insert ( 1260 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1260 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1260 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1260 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_group
  * ----------------
  */
-DATA(insert ( 1261 groname			19 -1 NAMEDATALEN  1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1261 grosysid			23 -1  4   2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 grolist		  1007 -1 -1   3 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1261 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
+DATA(insert ( 1261 groname			19 -1 NAMEDATALEN  1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1261 grosysid			23 -1  4   2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 grolist		  1007 -1 -1   3 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1261 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
 /* no OIDs in pg_group */
-DATA(insert ( 1261 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1261 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1261 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1261 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_attribute
  * ----------------
  */
 #define Schema_pg_attribute \
-{ 1249, {"attrelid"},	  26, -1,	4,	1, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attname"},	  19, -1, NAMEDATALEN,	2, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"atttypid"},	  26, -1,	4,	3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attstattarget"}, 23, -1,	4,	4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attlen"},		  21, -1,	2,	5, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1249, {"attnum"},		  21, -1,	2,	6, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1249, {"attndims"},	  23, -1,	4,	7, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attcacheoff"},  23, -1,	4,	8, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"atttypmod"},	  23, -1,	4,	9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1249, {"attbyval"},	  16, -1,	1, 10, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attstorage"},   18, -1,	1, 11, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attisset"},	  16, -1,	1, 12, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attalign"},	  18, -1,	1, 13, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attnotnull"},   16, -1,	1, 14, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"atthasdef"},	  16, -1,	1, 15, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attisdropped"}, 16, -1,	1, 16, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attislocal"},   16, -1,	1, 17, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1249, {"attinhcount"},  23, -1,	4, 18, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }
-
-DATA(insert ( 1249 attrelid			26 -1  4   1 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attname			19 -1 NAMEDATALEN  2 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1249 atttypid			26 -1  4   3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attstattarget	23 -1  4   4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attlen			21 -1  2   5 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1249 attnum			21 -1  2   6 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1249 attndims			23 -1  4   7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attcacheoff		23 -1  4   8 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 atttypmod		23 -1  4   9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 attbyval			16 -1  1  10 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attstorage		18 -1  1  11 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attisset			16 -1  1  12 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attalign			18 -1  1  13 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attnotnull		16 -1  1  14 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 atthasdef		16 -1  1  15 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attisdropped		16 -1  1  16 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attislocal		16 -1  1  17 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1249 attinhcount		23 -1  4  18 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
+{ 1249, {"attrelid"},	  26, -1,	4,	1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attname"},	  19, -1, NAMEDATALEN,	2, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"atttypid"},	  26, -1,	4,	3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attstattarget"}, 23, -1,	4,	4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attlen"},		  21, -1,	2,	5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1249, {"attnum"},		  21, -1,	2,	6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1249, {"attndims"},	  23, -1,	4,	7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attcacheoff"},  23, -1,	4,	8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"atttypmod"},	  23, -1,	4,	9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1249, {"attbyval"},	  16, -1,	1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attstorage"},   18, -1,	1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attalign"},	  18, -1,	1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attnotnull"},   16, -1,	1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"atthasdef"},	  16, -1,	1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attisdropped"}, 16, -1,	1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attislocal"},   16, -1,	1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1249, {"attinhcount"},  23, -1,	4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }
+
+DATA(insert ( 1249 attrelid			26 -1  4   1 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attname			19 -1 NAMEDATALEN  2 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1249 atttypid			26 -1  4   3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attstattarget	23 -1  4   4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attlen			21 -1  2   5 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1249 attnum			21 -1  2   6 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1249 attndims			23 -1  4   7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attcacheoff		23 -1  4   8 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 atttypmod		23 -1  4   9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 attbyval			16 -1  1  10 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attstorage		18 -1  1  11 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attalign			18 -1  1  12 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attnotnull		16 -1  1  13 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 atthasdef		16 -1  1  14 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attisdropped		16 -1  1  15 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attislocal		16 -1  1  16 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1249 attinhcount		23 -1  4  17 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
 /* no OIDs in pg_attribute */
-DATA(insert ( 1249 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1249 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 1249 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_class
  * ----------------
  */
 #define Schema_pg_class \
-{ 1259, {"relname"},	   19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relnamespace"},  26, -1,	4,	2, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltype"},	   26, -1,	4,	3, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relowner"},	   23, -1,	4,	4, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relam"},		   26, -1,	4,	5, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relfilenode"},   26, -1,	4,	6, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relpages"},	   23, -1,	4,	7, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltuples"},	   700, -1,	4,	8, 0, -1, -1, false, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastrelid"}, 26, -1,	4,	9, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"reltoastidxid"}, 26, -1,	4, 10, 0, -1, -1, true, 'p', false, 'i', true, false, false, true, 0 }, \
-{ 1259, {"relhasindex"},   16, -1,	1, 11, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relisshared"},   16, -1,	1, 12, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relkind"},	   18, -1,	1, 13, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relnatts"},	   21, -1,	2, 14, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relchecks"},	   21, -1,	2, 15, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"reltriggers"},   21, -1,	2, 16, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relukeys"},	   21, -1,	2, 17, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relfkeys"},	   21, -1,	2, 18, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relrefs"},	   21, -1,	2, 19, 0, -1, -1, true, 'p', false, 's', true, false, false, true, 0 }, \
-{ 1259, {"relhasoids"},    16, -1,	1, 20, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhaspkey"},    16, -1,	1, 21, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhasrules"},   16, -1,	1, 22, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relhassubclass"},16, -1,	1, 23, 0, -1, -1, true, 'p', false, 'c', true, false, false, true, 0 }, \
-{ 1259, {"relacl"},		 1034, -1, -1, 24, 1, -1, -1, false, 'x', false, 'i', false, false, false, true, 0 }
-
-DATA(insert ( 1259 relname			19 -1 NAMEDATALEN	1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 relnamespace		26 -1 4   2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltype			26 -1 4   3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relowner			23 -1 4   4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relam			26 -1 4   5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relfilenode		26 -1 4   6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relpages			23 -1 4   7 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltuples	   700 -1 4   8 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 reltoastrelid	26 -1 4   9 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 reltoastidxid	26 -1 4  10 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 relhasindex		16 -1 1  11 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relisshared		16 -1 1  12 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relkind			18 -1 1  13 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relnatts			21 -1 2  14 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relchecks		21 -1 2  15 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 reltriggers		21 -1 2  16 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relukeys			21 -1 2  17 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relfkeys			21 -1 2  18 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relrefs			21 -1 2  19 0 -1 -1 t p f s t f f t 0));
-DATA(insert ( 1259 relhasoids		16 -1 1  20 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhaspkey		16 -1 1  21 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhasrules		16 -1 1  22 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relhassubclass	16 -1 1  23 0 -1 -1 t p f c t f f t 0));
-DATA(insert ( 1259 relacl		  1034 -1 -1 24 1 -1 -1 f x f i f f f t 0));
-DATA(insert ( 1259 ctid				27 0  6  -1 0 -1 -1 f p f i t f f t 0));
-DATA(insert ( 1259 oid				26 0  4  -2 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 xmin				28 0  4  -3 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 cmin				29 0  4  -4 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 xmax				28 0  4  -5 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 cmax				29 0  4  -6 0 -1 -1 t p f i t f f t 0));
-DATA(insert ( 1259 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
+{ 1259, {"relname"},	   19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relnamespace"},  26, -1,	4,	2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltype"},	   26, -1,	4,	3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relowner"},	   23, -1,	4,	4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relam"},		   26, -1,	4,	5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relfilenode"},   26, -1,	4,	6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relpages"},	   23, -1,	4,	7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltuples"},	   700, -1,	4,	8, 0, -1, -1, false, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastrelid"}, 26, -1,	4,	9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"reltoastidxid"}, 26, -1,	4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1259, {"relhasindex"},   16, -1,	1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relisshared"},   16, -1,	1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relkind"},	   18, -1,	1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relnatts"},	   21, -1,	2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relchecks"},	   21, -1,	2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"reltriggers"},   21, -1,	2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relukeys"},	   21, -1,	2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relfkeys"},	   21, -1,	2, 18, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relrefs"},	   21, -1,	2, 19, 0, -1, -1, true, 'p', 's', true, false, false, true, 0 }, \
+{ 1259, {"relhasoids"},    16, -1,	1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhaspkey"},    16, -1,	1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhasrules"},   16, -1,	1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relhassubclass"},16, -1,	1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1259, {"relacl"},		 1034, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+
+DATA(insert ( 1259 relname			19 -1 NAMEDATALEN	1 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1259 relnamespace		26 -1 4   2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltype			26 -1 4   3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relowner			23 -1 4   4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relam			26 -1 4   5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relfilenode		26 -1 4   6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relpages			23 -1 4   7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltuples	   700 -1 4   8 0 -1 -1 f p i t f f t 0));
+DATA(insert ( 1259 reltoastrelid	26 -1 4   9 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 reltoastidxid	26 -1 4  10 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 relhasindex		16 -1 1  11 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relisshared		16 -1 1  12 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relkind			18 -1 1  13 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relnatts			21 -1 2  14 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relchecks		21 -1 2  15 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 reltriggers		21 -1 2  16 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relukeys			21 -1 2  17 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relfkeys			21 -1 2  18 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relrefs			21 -1 2  19 0 -1 -1 t p s t f f t 0));
+DATA(insert ( 1259 relhasoids		16 -1 1  20 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhaspkey		16 -1 1  21 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhasrules		16 -1 1  22 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relhassubclass	16 -1 1  23 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1259 relacl		  1034 -1 -1 24 1 -1 -1 f x i f f f t 0));
+DATA(insert ( 1259 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
+DATA(insert ( 1259 oid				26 0  4  -2 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 cmin				29 0  4  -4 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 xmax				28 0  4  -5 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 cmax				29 0  4  -6 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 tableoid			26 0  4  -7 0 -1 -1 t p i t f f t 0));
 
 /* ----------------
  *		pg_xactlock - this is not a real relation, but is a placeholder
@@ -507,6 +501,6 @@ DATA(insert ( 1259 tableoid			26 0  4  -7 0 -1 -1 t p f i t f f t 0));
  *				  table; and this entry is just to link to that one.
  * ----------------
  */
-DATA(insert ( 376 xactlockfoo		26 0  4   1 0 -1 -1 t p f i t f f t 0));
+DATA(insert ( 376 xactlockfoo		26 0  4   1 0 -1 -1 t p i t f f t 0));
 
 #endif   /* PG_ATTRIBUTE_H */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index eafe5ceb3285641c273afcd2a6a663fb1d7d41fc..8f044b0459ae02408d80018855c1313be9d9b214 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.80 2004/02/12 23:41:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.81 2004/04/01 21:28:45 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -136,7 +136,7 @@ typedef FormData_pg_class *Form_pg_class;
 
 DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 18 0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 17 0 0 0 0 0 f f f f _null_ ));
 DESCR("");
 DATA(insert OID = 1255 (  pg_proc		PGNSP 81 PGUID 0 1255 0 0 0 0 f f r 16 0 0 0 0 0 t f f f _null_ ));
 DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 739e1b0da64f73287e006e3be1d65f2ad6f9ae18..7da13b33c9082fd370fc2fbf65255b3f489e2f37 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.322 2004/03/22 01:38:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.323 2004/04/01 21:28:45 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1334,8 +1334,6 @@ DATA(insert OID = 1062 (  aclitemeq		   PGNSP PGUID 12 f f t f i 2 16 "1033 1033
 DESCR("equality operator for ACL items");
 DATA(insert OID = 1365 (  makeaclitem	   PGNSP PGUID 12 f f t f i 5 1033 "23 23 23 25 16" _null_ makeaclitem - _null_ ));
 DESCR("make ACL item");
-DATA(insert OID = 1038 (  seteval		   PGNSP PGUID 12 f f t t v 1 23 "26" _null_  seteval - _null_ ));
-DESCR("internal function supporting PostQuel-style sets");
 DATA(insert OID = 1044 (  bpcharin		   PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" _null_ bpcharin - _null_ ));
 DESCR("I/O");
 DATA(insert OID = 1045 (  bpcharout		   PGNSP PGUID 12 f f t f i 1 2275 "1042" _null_	bpcharout - _null_ ));
@@ -3155,9 +3153,9 @@ DATA(insert OID = 2273 (  has_schema_privilege		   PGNSP PGUID 12 f f t f s 2 16
 DESCR("current user privilege on schema by schema oid");
 
 
-DATA(insert OID = 2290 (  record_in			PGNSP PGUID 12 f f t f i 1 2249 "2275" _null_	record_in - _null_ ));
+DATA(insert OID = 2290 (  record_in			PGNSP PGUID 12 f f t f v 1 2249 "2275" _null_	record_in - _null_ ));
 DESCR("I/O");
-DATA(insert OID = 2291 (  record_out		PGNSP PGUID 12 f f t f i 1 2275 "2249" _null_	record_out - _null_ ));
+DATA(insert OID = 2291 (  record_out		PGNSP PGUID 12 f f t f v 1 2275 "2249" _null_	record_out - _null_ ));
 DESCR("I/O");
 DATA(insert OID = 2292 (  cstring_in		PGNSP PGUID 12 f f t f i 1 2275 "2275" _null_	cstring_in - _null_ ));
 DESCR("I/O");
@@ -3298,9 +3296,9 @@ DATA(insert OID = 2400 (  array_recv		   PGNSP PGUID 12 f f t f s 2 2277 "2281 2
 DESCR("I/O");
 DATA(insert OID = 2401 (  array_send		   PGNSP PGUID 12 f f t f s 2 17 "2277 26" _null_	array_send - _null_ ));
 DESCR("I/O");
-DATA(insert OID = 2402 (  record_recv		   PGNSP PGUID 12 f f t f i 1 2249 "2281" _null_  record_recv - _null_ ));
+DATA(insert OID = 2402 (  record_recv		   PGNSP PGUID 12 f f t f v 1 2249 "2281" _null_  record_recv - _null_ ));
 DESCR("I/O");
-DATA(insert OID = 2403 (  record_send		   PGNSP PGUID 12 f f t f i 1 17 "2249" _null_  record_send - _null_ ));
+DATA(insert OID = 2403 (  record_send		   PGNSP PGUID 12 f f t f v 1 17 "2249" _null_  record_send - _null_ ));
 DESCR("I/O");
 DATA(insert OID = 2404 (  int2recv			   PGNSP PGUID 12 f f t f i 1 21 "2281" _null_  int2recv - _null_ ));
 DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 8cbc0aa8e67a5ddb1a00e6d645ddb678d684c253..9f68e22fdb8064e301496a46c3c5be77b88cf283 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.151 2004/03/19 18:58:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.152 2004/04/01 21:28:45 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -268,7 +268,7 @@ DATA(insert OID = 21 (	int2	   PGNSP PGUID	2 t b t \054 0	 0 int2in int2out int2
 DESCR("-32 thousand to 32 thousand, 2-byte storage");
 #define INT2OID			21
 
-DATA(insert OID = 22 (	int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0  21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 (	int2vector PGNSP PGUID INDEX_MAX_KEYS*2 f b t \054 0  21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - s p f 0 -1 0 _null_ _null_ ));
 DESCR("array of INDEX_MAX_KEYS int2 integers, used in system tables");
 #define INT2VECTOROID	22
 
@@ -288,7 +288,7 @@ DATA(insert OID = 26 (	oid		   PGNSP PGUID	4 t b t \054 0	 0 oidin oidout oidrec
 DESCR("object identifier(oid), maximum 4 billion");
 #define OIDOID			26
 
-DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend - s p f 0 -1 0 _null_ _null_ ));
 DESCR("(Block, offset), physical location of tuple");
 #define TIDOID		27
 
@@ -307,13 +307,13 @@ DESCR("array of INDEX_MAX_KEYS oids, used in system tables");
 DATA(insert OID = 32 (	SET		   PGNSP PGUID -1 f b t \054 0	 0 unknownin unknownout - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("set of tuples");
 
-DATA(insert OID = 71 (	pg_type		 PGNSP PGUID 4 t c t \054 1247 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 (	pg_attribute PGNSP PGUID 4 t c t \054 1249 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 (	pg_proc		 PGNSP PGUID 4 t c t \054 1255 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 (	pg_class	 PGNSP PGUID 4 t c t \054 1259 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 86 (	pg_shadow	 PGNSP PGUID 4 t c t \054 1260 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 87 (	pg_group	 PGNSP PGUID 4 t c t \054 1261 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 88 (	pg_database  PGNSP PGUID 4 t c t \054 1262 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 (	pg_type		 PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 (	pg_attribute PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 (	pg_proc		 PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 (	pg_class	 PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 86 (	pg_shadow	 PGNSP PGUID -1 f c t \054 1260 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 87 (	pg_group	 PGNSP PGUID -1 f c t \054 1261 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 88 (	pg_database  PGNSP PGUID -1 f c t \054 1262 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 100 - 199 */
 
@@ -526,7 +526,7 @@ DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in a
  * argument and result types (if supported by the function's implementation
  * language).
  */
-DATA(insert OID = 2249 ( record			PGNSP PGUID  4 t p t \054 0 0 record_in record_out record_recv record_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record			PGNSP PGUID -1 f p t \054 0 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
 #define RECORDOID		2249
 DATA(insert OID = 2275 ( cstring		PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - c p f 0 -1 0 _null_ _null_ ));
 #define CSTRINGOID		2275
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 1eb158440b1c11680ee856e020efbea1f523c864..a3088ca7f628483ff645d73daf7669a4ddcca9af 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.108 2004/03/17 01:02:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.109 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,9 +120,9 @@ extern void ExecEndNode(PlanState *node);
 /*
  * prototypes from functions in execQual.c
  */
-extern Datum GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
+extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
 				  bool *isNull);
-extern Datum GetAttributeByName(TupleTableSlot *slot, char *attname,
+extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
 				   bool *isNull);
 extern void init_fcache(Oid foid, FuncExprState *fcache,
 			MemoryContext fcacheCxt);
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index ac2b494b53231fa3d2a561c650ce426396784730..2e477e70f87c2c6953dab682396f37526ab4e981 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -2,7 +2,7 @@
  *
  * spi.h
  *
- * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.43 2004/03/17 01:05:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.44 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -95,9 +95,7 @@ extern int SPI_getargcount(void *plan);
 extern bool SPI_is_cursor_plan(void *plan);
 
 extern HeapTuple SPI_copytuple(HeapTuple tuple);
-extern TupleDesc SPI_copytupledesc(TupleDesc tupdesc);
-extern TupleTableSlot *SPI_copytupleintoslot(HeapTuple tuple,
-					  TupleDesc tupdesc);
+extern HeapTupleHeader SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc);
 extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
 				int *attnum, Datum *Values, const char *Nulls);
 extern int	SPI_fnumber(TupleDesc tupdesc, const char *fname);
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 6ed389e8f95acc8aa742bf2e7b40e43b2964e8b7..acdcb4ff4754a47324a72938c57899e0eff6fa26 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.33 2004/01/19 02:06:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.34 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -192,11 +192,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define DatumGetTextP(X)			((text *) PG_DETOAST_DATUM(X))
 #define DatumGetBpCharP(X)			((BpChar *) PG_DETOAST_DATUM(X))
 #define DatumGetVarCharP(X)			((VarChar *) PG_DETOAST_DATUM(X))
+#define DatumGetHeapTupleHeader(X)	((HeapTupleHeader) PG_DETOAST_DATUM(X))
 /* And we also offer variants that return an OK-to-write copy */
 #define DatumGetByteaPCopy(X)		((bytea *) PG_DETOAST_DATUM_COPY(X))
 #define DatumGetTextPCopy(X)		((text *) PG_DETOAST_DATUM_COPY(X))
 #define DatumGetBpCharPCopy(X)		((BpChar *) PG_DETOAST_DATUM_COPY(X))
 #define DatumGetVarCharPCopy(X)		((VarChar *) PG_DETOAST_DATUM_COPY(X))
+#define DatumGetHeapTupleHeaderCopy(X)	((HeapTupleHeader) PG_DETOAST_DATUM_COPY(X))
 /* Variants which return n bytes starting at pos. m */
 #define DatumGetByteaPSlice(X,m,n)	((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n))
 #define DatumGetTextPSlice(X,m,n)	((text *) PG_DETOAST_DATUM_SLICE(X,m,n))
@@ -207,11 +209,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define PG_GETARG_TEXT_P(n)			DatumGetTextP(PG_GETARG_DATUM(n))
 #define PG_GETARG_BPCHAR_P(n)		DatumGetBpCharP(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P(n)		DatumGetVarCharP(PG_GETARG_DATUM(n))
+#define PG_GETARG_HEAPTUPLEHEADER(n)	DatumGetHeapTupleHeader(PG_GETARG_DATUM(n))
 /* And we also offer variants that return an OK-to-write copy */
 #define PG_GETARG_BYTEA_P_COPY(n)	DatumGetByteaPCopy(PG_GETARG_DATUM(n))
 #define PG_GETARG_TEXT_P_COPY(n)	DatumGetTextPCopy(PG_GETARG_DATUM(n))
 #define PG_GETARG_BPCHAR_P_COPY(n)	DatumGetBpCharPCopy(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P_COPY(n) DatumGetVarCharPCopy(PG_GETARG_DATUM(n))
+#define PG_GETARG_HEAPTUPLEHEADER_COPY(n)	DatumGetHeapTupleHeaderCopy(PG_GETARG_DATUM(n))
 /* And a b-byte slice from position a -also OK to write */
 #define PG_GETARG_BYTEA_P_SLICE(n,a,b) DatumGetByteaPSlice(PG_GETARG_DATUM(n),a,b)
 #define PG_GETARG_TEXT_P_SLICE(n,a,b)  DatumGetTextPSlice(PG_GETARG_DATUM(n),a,b)
@@ -246,6 +250,7 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define PG_RETURN_TEXT_P(x)    PG_RETURN_POINTER(x)
 #define PG_RETURN_BPCHAR_P(x)  PG_RETURN_POINTER(x)
 #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
+#define PG_RETURN_HEAPTUPLEHEADER(x)  PG_RETURN_POINTER(x)
 
 
 /*-------------------------------------------------------------------------
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index a8051fc27f0399a43da930768b463578bc88e113..fd1660423e1eb1a06933170ef8afde82540ceaee 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,7 +9,7 @@
  *
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.10 2003/11/29 22:40:53 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.11 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,7 +57,7 @@ typedef struct AttInMetadata
 typedef struct FuncCallContext
 {
 	/*
-	 * Number of times we've been called before.
+	 * Number of times we've been called before
 	 *
 	 * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
 	 * incremented for you every time SRF_RETURN_NEXT() is called.
@@ -67,7 +67,7 @@ typedef struct FuncCallContext
 	/*
 	 * OPTIONAL maximum number of calls
 	 *
-	 * max_calls is here for convenience ONLY and setting it is OPTIONAL. If
+	 * max_calls is here for convenience only and setting it is optional. If
 	 * not set, you must provide alternative means to know when the
 	 * function is done.
 	 */
@@ -76,41 +76,50 @@ typedef struct FuncCallContext
 	/*
 	 * OPTIONAL pointer to result slot
 	 *
-	 * slot is for use when returning tuples (i.e. composite data types) and
-	 * is not needed when returning base (i.e. scalar) data types.
+	 * This is obsolete and only present for backwards compatibility, viz,
+	 * user-defined SRFs that use the deprecated TupleDescGetSlot().
 	 */
 	TupleTableSlot *slot;
 
 	/*
-	 * OPTIONAL pointer to misc user provided context info
+	 * OPTIONAL pointer to miscellaneous user-provided context information
 	 *
 	 * user_fctx is for use as a pointer to your own struct to retain
-	 * arbitrary context information between calls for your function.
+	 * arbitrary context information between calls of your function.
 	 */
 	void	   *user_fctx;
 
 	/*
-	 * OPTIONAL pointer to struct containing arrays of attribute type
-	 * input metainfo
+	 * OPTIONAL pointer to struct containing attribute type input metadata
 	 *
 	 * attinmeta is for use when returning tuples (i.e. composite data types)
-	 * and is not needed when returning base (i.e. scalar) data types. It
-	 * is ONLY needed if you intend to use BuildTupleFromCStrings() to
-	 * create the return tuple.
+	 * and is not used when returning base data types. It is only needed
+	 * if you intend to use BuildTupleFromCStrings() to create the return
+	 * tuple.
 	 */
 	AttInMetadata *attinmeta;
 
 	/*
-	 * memory context used for structures which must live for multiple
-	 * calls
+	 * memory context used for structures that must live for multiple calls
 	 *
 	 * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
 	 * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
-	 * context for any memory that is to be re-used across multiple calls
+	 * context for any memory that is to be reused across multiple calls
 	 * of the SRF.
 	 */
 	MemoryContext multi_call_memory_ctx;
 
+	/*
+	 * OPTIONAL pointer to struct containing tuple description
+	 *
+	 * tuple_desc is for use when returning tuples (i.e. composite data types)
+	 * and is only needed if you are going to build the tuples with
+	 * heap_formtuple() rather than with BuildTupleFromCStrings().  Note that
+	 * the TupleDesc pointer stored here should usually have been run through
+	 * BlessTupleDesc() first.
+	 */
+	TupleDesc tuple_desc;
+
 } FuncCallContext;
 
 /*----------
@@ -122,38 +131,43 @@ typedef struct FuncCallContext
  * TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use 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.
- * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
- *		given a TupleDesc.
+ * TupleDesc BlessTupleDesc(TupleDesc tupdesc) - "Bless" a completed tuple
+ *		descriptor so that it can be used to return properly labeled tuples.
+ *		You need to call this if you are going to use heap_formtuple directly.
+ *		TupleDescGetAttInMetadata does it for you, however, so no need to call
+ *		it if you call TupleDescGetAttInMetadata.
  * 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.
+ *		tuple.
  * 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.
  *
  * Macro declarations:
+ * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
+ *
+ * Obsolete routines and macros:
+ * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Builds a
+ *		TupleTableSlot, which is not needed anymore.
  * TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
  *		given a tuple and a slot.
  *----------
  */
 
+#define HeapTupleGetDatum(_tuple)		PointerGetDatum((_tuple)->t_data)
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple)	PointerGetDatum((_tuple)->t_data)
+
 /* from tupdesc.c */
 extern TupleDesc RelationNameGetTupleDesc(const char *relname);
 extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
 
 /* from execTuples.c */
-extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
+extern TupleDesc BlessTupleDesc(TupleDesc tupdesc);
 extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
 extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
-
-/*
- * Note we pass shouldFree = false; this is needed because the tuple will
- * typically be in a shorter-lived memory context than the TupleTableSlot.
- */
-#define TupleGetDatum(_slot, _tuple) \
-	PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, false))
+extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
 
 
 /*----------
@@ -176,8 +190,7 @@ extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
  *		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  *		<user defined code>
  *		<if returning composite>
- *			<obtain slot>
- *			funcctx->slot = slot;
+ *			<build TupleDesc, and perhaps AttInMetaData>
  *		<endif returning composite>
  *		<user defined code>
  *		// return to original context when allocating transient memory
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 440fcc4d576d7035b982e3c9114edb84e3acf8b6..8a0fbf7be0f38473edd2c16d92bfee615a906f6c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.114 2004/03/17 20:48:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.115 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -563,6 +563,17 @@ typedef struct SubPlanState
 	FmgrInfo   *hashfunctions;	/* lookup data for hash functions */
 } SubPlanState;
 
+/* ----------------
+ *		FieldSelectState node
+ * ----------------
+ */
+typedef struct FieldSelectState
+{
+	ExprState	xprstate;
+	ExprState  *arg;			/* input expression */
+	TupleDesc	argdesc;		/* tupdesc for most recent input */
+} FieldSelectState;
+
 /* ----------------
  *		CaseExprState node
  * ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5c34b8d8559a3042790b3b3107bf9e141a99c4ea..d5d4f0832a7b8e157fbe5ab2fb1924f513415ab2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.151 2004/03/17 20:48:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.152 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,6 +141,7 @@ typedef enum NodeTag
 	T_ScalarArrayOpExprState,
 	T_BoolExprState,
 	T_SubPlanState,
+	T_FieldSelectState,
 	T_CaseExprState,
 	T_CaseWhenState,
 	T_ArrayExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index d94196d740069b2507e083147ec9f6eee58808d6..567310fa1c3bae831fdb1c338ac71484bcc7eb13 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.96 2004/03/17 20:48:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.97 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -525,9 +525,8 @@ typedef struct SubPlan
  * FieldSelect
  *
  * FieldSelect represents the operation of extracting one field from a tuple
- * value.  At runtime, the input expression is expected to yield a Datum
- * that contains a pointer-to-TupleTableSlot.  The specified field number
- * is extracted and returned as a Datum.
+ * value.  At runtime, the input expression is expected to yield a rowtype
+ * Datum.  The specified field number is extracted and returned as a Datum.
  * ----------------
  */
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b7c1f5fea7741d270a4a3d3cfa533b2ea46f0204..02e6cbca49d814072c598952548ef3f4fb1207f1 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.235 2004/03/15 03:29:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.236 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -384,10 +384,6 @@ extern Datum oidvectorge(PG_FUNCTION_ARGS);
 extern Datum oidvectorgt(PG_FUNCTION_ARGS);
 
 /* pseudotypes.c */
-extern Datum record_in(PG_FUNCTION_ARGS);
-extern Datum record_out(PG_FUNCTION_ARGS);
-extern Datum record_recv(PG_FUNCTION_ARGS);
-extern Datum record_send(PG_FUNCTION_ARGS);
 extern Datum cstring_in(PG_FUNCTION_ARGS);
 extern Datum cstring_out(PG_FUNCTION_ARGS);
 extern Datum cstring_recv(PG_FUNCTION_ARGS);
@@ -452,6 +448,12 @@ extern List *stringToQualifiedNameList(const char *string, const char *caller);
 extern char *format_procedure(Oid procedure_oid);
 extern char *format_operator(Oid operator_oid);
 
+/* rowtypes.c */
+extern Datum record_in(PG_FUNCTION_ARGS);
+extern Datum record_out(PG_FUNCTION_ARGS);
+extern Datum record_recv(PG_FUNCTION_ARGS);
+extern Datum record_send(PG_FUNCTION_ARGS);
+
 /* ruleutils.c */
 extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
 extern Datum pg_get_ruledef_ext(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/sets.h b/src/include/utils/sets.h
deleted file mode 100644
index 82350f2806acde4a590bc2ae97ca39d602b1c5c8..0000000000000000000000000000000000000000
--- a/src/include/utils/sets.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sets.h
- *
- *
- *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $PostgreSQL: pgsql/src/include/utils/sets.h,v 1.16 2003/11/29 22:41:16 pgsql Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef SETS_H
-#define SETS_H
-
-#include "fmgr.h"
-
-
-/* Temporary name of a set function, before SetDefine changes it. */
-#define GENERICSETNAME "ZYX#Set#ZYX"
-
-extern Oid	SetDefine(char *querystr, Oid elemType);
-
-extern Datum seteval(PG_FUNCTION_ARGS);
-
-#endif   /* SETS_H */
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 93f3f163c46ef5701d8706e1d2a273ba4fda710d..8b85f51700024b7eb84cc0aa9f2cec2371a873ac 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -9,13 +9,14 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.2 2003/11/29 22:41:16 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/utils/typcache.h,v 1.3 2004/04/01 21:28:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef TYPCACHE_H
 #define TYPCACHE_H
 
+#include "access/tupdesc.h"
 #include "fmgr.h"
 
 
@@ -28,6 +29,8 @@ typedef struct TypeCacheEntry
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typtype;
+	Oid			typrelid;
 
 	/*
 	 * Information obtained from opclass entries
@@ -51,6 +54,13 @@ typedef struct TypeCacheEntry
 	 */
 	FmgrInfo	eq_opr_finfo;
 	FmgrInfo	cmp_proc_finfo;
+
+	/*
+	 * Tuple descriptor if it's a composite type (row type).  NULL if not
+	 * composite or information hasn't yet been requested.  (NOTE: this
+	 * is actually just a link to information maintained by relcache.c.)
+	 */
+	TupleDesc	tupDesc;
 } TypeCacheEntry;
 
 /* Bit flags to indicate which fields a given caller needs to have set */
@@ -60,7 +70,14 @@ typedef struct TypeCacheEntry
 #define TYPECACHE_CMP_PROC			0x0008
 #define TYPECACHE_EQ_OPR_FINFO		0x0010
 #define TYPECACHE_CMP_PROC_FINFO	0x0020
+#define TYPECACHE_TUPDESC			0x0040
 
 extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);
 
+extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
+
+extern void assign_record_type_typmod(TupleDesc tupDesc);
+
+extern void flush_rowtype_cache(Oid type_id);
+
 #endif   /* TYPCACHE_H */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index f7b690f71cef3381dbd7a2390a38fdde10b5eccd..8c3680ae19cced6ff5a1c17bfe0fbbd889cfd268 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -33,7 +33,7 @@
  *	  ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.42 2004/01/06 23:55:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.43 2004/04/01 21:28:46 tgl Exp $
  *
  **********************************************************************/
 
@@ -45,15 +45,16 @@
 #include <setjmp.h>
 
 /* postgreSQL stuff */
-#include "executor/spi.h"
-#include "commands/trigger.h"
-#include "fmgr.h"
 #include "access/heapam.h"
-#include "tcop/tcopprot.h"
-#include "utils/syscache.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "commands/trigger.h"
+#include "executor/spi.h"
+#include "fmgr.h"
+#include "tcop/tcopprot.h"
+#include "utils/syscache.h"
+#include "utils/typcache.h"
 
 /* perl stuff */
 #include "EXTERN.h"
@@ -82,7 +83,7 @@ typedef struct plperl_proc_desc
 	int			nargs;
 	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];
 	Oid			arg_out_elem[FUNC_MAX_ARGS];
-	int			arg_is_rel[FUNC_MAX_ARGS];
+	bool		arg_is_rowtype[FUNC_MAX_ARGS];
 	SV		   *reference;
 }	plperl_proc_desc;
 
@@ -388,19 +389,34 @@ plperl_call_perl_func(plperl_proc_desc * desc, FunctionCallInfo fcinfo)
 	PUSHMARK(SP);
 	for (i = 0; i < desc->nargs; i++)
 	{
-		if (desc->arg_is_rel[i])
+		if (desc->arg_is_rowtype[i])
 		{
-			TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-			SV		   *hashref;
-
-			Assert(slot != NULL && !fcinfo->argnull[i]);
-
-			/*
-			 * plperl_build_tuple_argument better return a mortal SV.
-			 */
-			hashref = plperl_build_tuple_argument(slot->val,
-											  slot->ttc_tupleDescriptor);
-			XPUSHs(hashref);
+			if (fcinfo->argnull[i])
+				XPUSHs(&PL_sv_undef);
+			else
+			{
+				HeapTupleHeader td;
+				Oid			tupType;
+				int32		tupTypmod;
+				TupleDesc	tupdesc;
+				HeapTupleData tmptup;
+				SV		   *hashref;
+
+				td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+				/* Extract rowtype info and find a tupdesc */
+				tupType = HeapTupleHeaderGetTypeId(td);
+				tupTypmod = HeapTupleHeaderGetTypMod(td);
+				tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+				/* Build a temporary HeapTuple control structure */
+				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+				tmptup.t_data = td;
+
+				/*
+				 * plperl_build_tuple_argument better return a mortal SV.
+				 */
+				hashref = plperl_build_tuple_argument(&tmptup, tupdesc);
+				XPUSHs(hashref);
+			}
 		}
 		else
 		{
@@ -645,7 +661,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 				}
 			}
 
-			if (typeStruct->typrelid != InvalidOid)
+			if (typeStruct->typtype == 'c')
 			{
 				free(prodesc->proname);
 				free(prodesc);
@@ -692,13 +708,16 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 						   format_type_be(procStruct->proargtypes[i]))));
 				}
 
-				if (typeStruct->typrelid != InvalidOid)
-					prodesc->arg_is_rel[i] = 1;
+				if (typeStruct->typtype == 'c')
+					prodesc->arg_is_rowtype[i] = true;
 				else
-					prodesc->arg_is_rel[i] = 0;
+				{
+					prodesc->arg_is_rowtype[i] = false;
+					perm_fmgr_info(typeStruct->typoutput,
+								   &(prodesc->arg_out_func[i]));
+					prodesc->arg_out_elem[i] = typeStruct->typelem;
+				}
 
-				perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
-				prodesc->arg_out_elem[i] = typeStruct->typelem;
 				ReleaseSysCache(typeTup);
 			}
 		}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b984f0932bbf55a5a459eaaf1e42d30d937651b7..b71a71bbaac23e42a11a5622610adba87920353a 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.98 2004/03/17 20:48:43 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.99 2004/04/01 21:28:46 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -52,6 +52,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 static const char *const raise_skip_msg = "RAISE";
@@ -258,16 +259,26 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 			case PLPGSQL_DTYPE_ROW:
 				{
 					PLpgSQL_row *row = (PLpgSQL_row *) estate.datums[n];
-					TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-					HeapTuple	tup;
-					TupleDesc	tupdesc;
 
 					if (!fcinfo->argnull[i])
 					{
-						Assert(slot != NULL);
-						tup = slot->val;
-						tupdesc = slot->ttc_tupleDescriptor;
-						exec_move_row(&estate, NULL, row, tup, tupdesc);
+						HeapTupleHeader td;
+						Oid			tupType;
+						int32		tupTypmod;
+						TupleDesc	tupdesc;
+						HeapTupleData tmptup;
+
+						td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+						/* Extract rowtype info and find a tupdesc */
+						tupType = HeapTupleHeaderGetTypeId(td);
+						tupTypmod = HeapTupleHeaderGetTypMod(td);
+						tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+						/* Build a temporary HeapTuple control structure */
+						tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+						ItemPointerSetInvalid(&(tmptup.t_self));
+						tmptup.t_tableOid = InvalidOid;
+						tmptup.t_data = td;
+						exec_move_row(&estate, NULL, row, &tmptup, tupdesc);
 					}
 					else
 					{
@@ -371,11 +382,10 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 	{
 		if (estate.retistuple)
 		{
-			/* Copy tuple to upper executor memory */
-			/* Here we need to return a TupleTableSlot not just a tuple */
-			estate.retval = (Datum)
-				SPI_copytupleintoslot((HeapTuple) (estate.retval),
-									  estate.rettupdesc);
+			/* Copy tuple to upper executor memory, as a tuple Datum */
+			estate.retval =
+				PointerGetDatum(SPI_returntuple((HeapTuple) (estate.retval),
+												estate.rettupdesc));
 		}
 		else
 		{
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 0da1399d836cd2089c2c979c13a560007a2cd6ec..010f1e1645ec541261e5be0dee406e175c26732c 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -29,26 +29,19 @@
  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.45 2004/01/07 18:56:30 neilc Exp $
+ *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.46 2004/04/01 21:28:46 tgl Exp $
  *
  *********************************************************************
  */
 
 #include "postgres.h"
 
-/* system stuff
- */
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
+/* system stuff */
 #include <unistd.h>
 #include <fcntl.h>
-#include <string.h>
 #include <setjmp.h>
 
-/* postgreSQL stuff
- */
+/* postgreSQL stuff */
 #include "access/heapam.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
@@ -59,6 +52,7 @@
 #include "parser/parse_type.h"
 #include "tcop/tcopprot.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 #include <Python.h>
 #include <compile.h>
@@ -121,7 +115,14 @@ typedef struct PLyTypeInfo
 {
 	PLyTypeInput in;
 	PLyTypeOutput out;
-	int			is_rel;
+	int			is_rowtype;
+	/*
+	 * is_rowtype can be:
+	 *		-1	not known yet (initial state)
+	 *		 0	scalar datatype
+	 *		 1	rowtype
+	 *		 2	rowtype, but I/O functions not set up yet
+	 */
 }	PLyTypeInfo;
 
 
@@ -916,16 +917,40 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 	args = PyList_New(proc->nargs);
 	for (i = 0; i < proc->nargs; i++)
 	{
-		if (proc->args[i].is_rel == 1)
+		if (proc->args[i].is_rowtype > 0)
 		{
-			TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
-			arg = PLyDict_FromTuple(&(proc->args[i]), slot->val,
-									slot->ttc_tupleDescriptor);
+			if (fcinfo->argnull[i])
+				arg = NULL;
+			else
+			{
+				HeapTupleHeader td;
+				Oid			tupType;
+				int32		tupTypmod;
+				TupleDesc	tupdesc;
+				HeapTupleData tmptup;
+
+				td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+				/* Extract rowtype info and find a tupdesc */
+				tupType = HeapTupleHeaderGetTypeId(td);
+				tupTypmod = HeapTupleHeaderGetTypMod(td);
+				tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+				/* Set up I/O funcs if not done yet */
+				if (proc->args[i].is_rowtype != 1)
+					PLy_input_tuple_funcs(&(proc->args[i]), tupdesc);
+
+				/* Build a temporary HeapTuple control structure */
+				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+				tmptup.t_data = td;
+
+				arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+			}
 		}
 		else
 		{
-			if (!fcinfo->argnull[i])
+			if (fcinfo->argnull[i])
+				arg = NULL;
+			else
 			{
 				char	   *ct;
 				Datum		dt;
@@ -938,8 +963,6 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 				arg = (proc->args[i].in.d.func) (ct);
 				pfree(ct);
 			}
-			else
-				arg = NULL;
 		}
 
 		if (arg == NULL)
@@ -1096,7 +1119,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 				 procStruct->prorettype);
 
 		rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
-		if (rvTypeStruct->typrelid == InvalidOid)
+		if (rvTypeStruct->typtype != 'c')
 			PLy_output_datum_func(&proc->result, rvTypeStruct);
 		else
 			ereport(ERROR,
@@ -1135,17 +1158,12 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 				 procStruct->proargtypes[i]);
 		argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
 
-		if (argTypeStruct->typrelid == InvalidOid)
+		if (argTypeStruct->typtype != 'c')
 			PLy_input_datum_func(&(proc->args[i]),
 								 procStruct->proargtypes[i],
 								 argTypeStruct);
 		else
-		{
-			TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
-			PLy_input_tuple_funcs(&(proc->args[i]),
-								  slot->ttc_tupleDescriptor);
-		}
+			proc->args[i].is_rowtype = 2; /* still need to set I/O funcs */
 
 		ReleaseSysCache(argTypeTup);
 	}
@@ -1279,7 +1297,7 @@ PLy_procedure_delete(PLyProcedure * proc)
 	if (proc->pyname)
 		PLy_free(proc->pyname);
 	for (i = 0; i < proc->nargs; i++)
-		if (proc->args[i].is_rel == 1)
+		if (proc->args[i].is_rowtype == 1)
 		{
 			if (proc->args[i].in.r.atts)
 				PLy_free(proc->args[i].in.r.atts);
@@ -1300,10 +1318,10 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 
 	enter();
 
-	if (arg->is_rel == 0)
+	if (arg->is_rowtype == 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
 
-	arg->is_rel = 1;
+	arg->is_rowtype = 1;
 	arg->in.r.natts = desc->natts;
 	arg->in.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb));
 
@@ -1338,10 +1356,10 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 
 	enter();
 
-	if (arg->is_rel == 0)
+	if (arg->is_rowtype == 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
 
-	arg->is_rel = 1;
+	arg->is_rowtype = 1;
 	arg->out.r.natts = desc->natts;
 	arg->out.r.atts = malloc(desc->natts * sizeof(PLyDatumToOb));
 
@@ -1372,9 +1390,9 @@ PLy_output_datum_func(PLyTypeInfo * arg, Form_pg_type typeStruct)
 {
 	enter();
 
-	if (arg->is_rel == 1)
+	if (arg->is_rowtype > 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
-	arg->is_rel = 0;
+	arg->is_rowtype = 0;
 	PLy_output_datum_func2(&(arg->out.d), typeStruct);
 }
 
@@ -1393,9 +1411,9 @@ PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, Form_pg_type typeStruct)
 {
 	enter();
 
-	if (arg->is_rel == 1)
+	if (arg->is_rowtype > 0)
 		elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
-	arg->is_rel = 0;
+	arg->is_rowtype = 0;
 	PLy_input_datum_func2(&(arg->in.d), typeOid, typeStruct);
 }
 
@@ -1434,7 +1452,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, Form_pg_type typeStruct)
 void
 PLy_typeinfo_init(PLyTypeInfo * arg)
 {
-	arg->is_rel = -1;
+	arg->is_rowtype = -1;
 	arg->in.r.natts = arg->out.r.natts = 0;
 	arg->in.r.atts = NULL;
 	arg->out.r.atts = NULL;
@@ -1443,7 +1461,7 @@ PLy_typeinfo_init(PLyTypeInfo * arg)
 void
 PLy_typeinfo_dealloc(PLyTypeInfo * arg)
 {
-	if (arg->is_rel == 1)
+	if (arg->is_rowtype == 1)
 	{
 		if (arg->in.r.atts)
 			PLy_free(arg->in.r.atts);
@@ -1515,7 +1533,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 
 	enter();
 
-	if (info->is_rel != 1)
+	if (info->is_rowtype != 1)
 		elog(ERROR, "PLyTypeInfo structure describes a datum");
 
 	dict = PyDict_New();
@@ -2009,7 +2027,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 
 				plan->types[i] = HeapTupleGetOid(typeTup);
 				typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-				if (typeStruct->typrelid == InvalidOid)
+				if (typeStruct->typtype != 'c')
 					PLy_output_datum_func(&plan->args[i], typeStruct);
 				else
 				{
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 37e515ff66b590e42891d19a7689a354302c9058..d9382aff83bd73ca423edd2f756075dce333f8fd 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -31,7 +31,7 @@
  *	  ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.82 2004/01/24 23:06:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.83 2004/04/01 21:28:46 tgl Exp $
  *
  **********************************************************************/
 
@@ -60,6 +60,8 @@
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
+
 
 #if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
 	&& TCL_MINOR_VERSION > 0
@@ -107,7 +109,7 @@ typedef struct pltcl_proc_desc
 	int			nargs;
 	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];
 	Oid			arg_out_elem[FUNC_MAX_ARGS];
-	int			arg_is_rel[FUNC_MAX_ARGS];
+	bool		arg_is_rowtype[FUNC_MAX_ARGS];
 }	pltcl_proc_desc;
 
 
@@ -497,21 +499,35 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 	 ************************************************************/
 	for (i = 0; i < prodesc->nargs; i++)
 	{
-		if (prodesc->arg_is_rel[i])
+		if (prodesc->arg_is_rowtype[i])
 		{
 			/**************************************************
 			 * For tuple values, add a list for 'array set ...'
 			 **************************************************/
-			TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
-
-			Assert(slot != NULL && !fcinfo->argnull[i]);
-			Tcl_DStringInit(&list_tmp);
-			pltcl_build_tuple_argument(slot->val,
-									   slot->ttc_tupleDescriptor,
-									   &list_tmp);
-			Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp));
-			Tcl_DStringFree(&list_tmp);
-			Tcl_DStringInit(&list_tmp);
+			if (fcinfo->argnull[i])
+				Tcl_DStringAppendElement(&tcl_cmd, "");
+			else
+			{
+				HeapTupleHeader td;
+				Oid			tupType;
+				int32		tupTypmod;
+				TupleDesc	tupdesc;
+				HeapTupleData tmptup;
+
+				td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+				/* Extract rowtype info and find a tupdesc */
+				tupType = HeapTupleHeaderGetTypeId(td);
+				tupTypmod = HeapTupleHeaderGetTypMod(td);
+				tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+				/* Build a temporary HeapTuple control structure */
+				tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+				tmptup.t_data = td;
+
+				Tcl_DStringSetLength(&list_tmp, 0);
+				pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
+				Tcl_DStringAppendElement(&tcl_cmd,
+										 Tcl_DStringValue(&list_tmp));
+			}
 		}
 		else
 		{
@@ -1041,11 +1057,11 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		Form_pg_type typeStruct;
 		Tcl_DString proc_internal_def;
 		Tcl_DString proc_internal_body;
-		char		proc_internal_args[4096];
+		char		proc_internal_args[33 * FUNC_MAX_ARGS];
 		Datum		prosrcdatum;
 		bool		isnull;
 		char	   *proc_source;
-		char		buf[512];
+		char		buf[32];
 
 		/************************************************************
 		 * Allocate a new procedure description block
@@ -1124,7 +1140,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 				}
 			}
 
-			if (typeStruct->typrelid != InvalidOid)
+			if (typeStruct->typtype == 'c')
 			{
 				free(prodesc->proname);
 				free(prodesc);
@@ -1172,25 +1188,22 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 						   format_type_be(procStruct->proargtypes[i]))));
 				}
 
-				if (typeStruct->typrelid != InvalidOid)
+				if (typeStruct->typtype == 'c')
 				{
-					prodesc->arg_is_rel[i] = 1;
-					if (i > 0)
-						strcat(proc_internal_args, " ");
+					prodesc->arg_is_rowtype[i] = true;
 					snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
-					strcat(proc_internal_args, buf);
-					ReleaseSysCache(typeTup);
-					continue;
 				}
 				else
-					prodesc->arg_is_rel[i] = 0;
-
-				perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
-				prodesc->arg_out_elem[i] = typeStruct->typelem;
+				{
+					prodesc->arg_is_rowtype[i] = false;
+					perm_fmgr_info(typeStruct->typoutput,
+								   &(prodesc->arg_out_func[i]));
+					prodesc->arg_out_elem[i] = typeStruct->typelem;
+					snprintf(buf, sizeof(buf), "%d", i + 1);
+				}
 
 				if (i > 0)
 					strcat(proc_internal_args, " ");
-				snprintf(buf, sizeof(buf), "%d", i + 1);
 				strcat(proc_internal_args, buf);
 
 				ReleaseSysCache(typeTup);
@@ -1225,11 +1238,13 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		{
 			for (i = 0; i < prodesc->nargs; i++)
 			{
-				if (!prodesc->arg_is_rel[i])
-					continue;
-				snprintf(buf, sizeof(buf), "array set %d $__PLTcl_Tup_%d\n",
-						 i + 1, i + 1);
-				Tcl_DStringAppend(&proc_internal_body, buf, -1);
+				if (prodesc->arg_is_rowtype[i])
+				{
+					snprintf(buf, sizeof(buf),
+							 "array set %d $__PLTcl_Tup_%d\n",
+							 i + 1, i + 1);
+					Tcl_DStringAppend(&proc_internal_body, buf, -1);
+				}
 			}
 		}
 		else
diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source
index c10f73c5c657d732977674bd54f429af49c25166..ebf13626c7fabf3f74deb4696e19218cad48af13 100644
--- a/src/test/regress/input/misc.source
+++ b/src/test/regress/input/misc.source
@@ -212,10 +212,12 @@ SELECT name(equipment(p.hobbies)), name(p.hobbies), p.name FROM person* p;
 SELECT user_relns() AS user_relns
    ORDER BY user_relns;
 
---SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
+SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
 
 SELECT hobbies_by_name('basketball');
 
+SELECT name, overpaid(emp.*) FROM emp;
+
 --
 -- check that old-style C functions work properly with TOASTed values
 --
diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source
index c478dd71897478f69a07400f216369dd297b1683..3173f718c6c8268a141055801efde138d0c1ec95 100644
--- a/src/test/regress/output/misc.source
+++ b/src/test/regress/output/misc.source
@@ -663,13 +663,29 @@ SELECT user_relns() AS user_relns
  xacttest
 (97 rows)
 
---SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
+SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
 SELECT hobbies_by_name('basketball');
  hobbies_by_name 
 -----------------
  joe
 (1 row)
 
+SELECT name, overpaid(emp.*) FROM emp;
+  name  | overpaid 
+--------+----------
+ sharon | t
+ sam    | t
+ bill   | t
+ jeff   | f
+ cim    | f
+ linda  | f
+(6 rows)
+
 --
 -- check that old-style C functions work properly with TOASTed values
 --
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index b26eed5b371463feffbaf3fc8afc35a05b646c3f..31210a8e0d94c765d0e13c8e690a448c7a26b970 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.59 2003/11/29 19:52:14 pgsql Exp $
+ * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.60 2004/04/01 21:28:47 tgl Exp $
  */
 
 #include "postgres.h"
@@ -15,8 +15,6 @@
 #define RDELIM			')'
 #define DELIM			','
 
-typedef TupleTableSlot *TUPLE;
-
 extern Datum regress_dist_ptpath(PG_FUNCTION_ARGS);
 extern Datum regress_path_dist(PG_FUNCTION_ARGS);
 extern PATH *poly2path(POLYGON *poly);
@@ -196,7 +194,7 @@ PG_FUNCTION_INFO_V1(overpaid);
 Datum
 overpaid(PG_FUNCTION_ARGS)
 {
-	TUPLE		tuple = (TUPLE) PG_GETARG_POINTER(0);
+	HeapTupleHeader tuple = PG_GETARG_HEAPTUPLEHEADER(0);
 	bool		isnull;
 	int32		salary;
 
diff --git a/src/tutorial/funcs.c b/src/tutorial/funcs.c
index d2c123a0094be295d2f8986cdca0d1ece2a8edae..5e8ad6ee7074f6e06153125d43ebf0f8c9aa8212 100644
--- a/src/tutorial/funcs.c
+++ b/src/tutorial/funcs.c
@@ -22,7 +22,7 @@ float8	   *add_one_float8(float8 *arg);
 Point	   *makepoint(Point *pointx, Point *pointy);
 text	   *copytext(text *t);
 text	   *concat_text(text *arg1, text *arg2);
-bool c_overpaid(TupleTableSlot *t,	/* the current instance of EMP */
+bool c_overpaid(HeapTupleHeader t,	/* the current instance of EMP */
 		   int32 limit);
 
 
@@ -94,7 +94,7 @@ concat_text(text *arg1, text *arg2)
 /* Composite types */
 
 bool
-c_overpaid(TupleTableSlot *t,	/* the current instance of EMP */
+c_overpaid(HeapTupleHeader t,	/* the current instance of EMP */
 		   int32 limit)
 {
 	bool		isnull;
diff --git a/src/tutorial/funcs_new.c b/src/tutorial/funcs_new.c
index c9413096bcf8da55da1bc331b8f002bf8d477a81..e548b85ee415a8d8919f79cc2f6f1b11378703f5 100644
--- a/src/tutorial/funcs_new.c
+++ b/src/tutorial/funcs_new.c
@@ -115,7 +115,7 @@ PG_FUNCTION_INFO_V1(c_overpaid);
 Datum
 c_overpaid(PG_FUNCTION_ARGS)
 {
-	TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
+	HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
 	int32		limit = PG_GETARG_INT32(1);
 	bool		isnull;
 	int32		salary;