From 375369acd1c621bdc683c58bc9c31d4e79d14849 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 1 Apr 2004 21:28:47 +0000
Subject: [PATCH] Replace TupleTableSlot convention for whole-row variables and
 function results with tuples as ordinary varlena Datums.  This commit does
 not in itself do much for us, except eliminate the horrid memory leak
 associated with evaluation of whole-row variables.  However, it lays the
 groundwork for allowing composite types as table columns, and perhaps some
 other useful features as well.  Per my proposal of a few days ago.

---
 contrib/dblink/dblink.c                 |  45 +--
 contrib/intagg/int_aggregate.c          |   1 -
 contrib/pgstattuple/pgstattuple.c       |  10 +-
 contrib/tablefunc/tablefunc.c           |  20 +-
 contrib/tsearch2/ts_stat.c              |   3 +-
 contrib/tsearch2/wparser.c              |   6 +-
 doc/src/sgml/catalogs.sgml              |  20 +-
 doc/src/sgml/spi.sgml                   |  91 ++---
 doc/src/sgml/xfunc.sgml                 | 155 ++++----
 src/backend/access/common/heaptuple.c   |  74 +++-
 src/backend/access/common/tupdesc.c     | 198 +++++-----
 src/backend/access/heap/heapam.c        |   4 +-
 src/backend/bootstrap/bootstrap.c       | 126 +++---
 src/backend/catalog/heap.c              |  43 +--
 src/backend/catalog/pg_proc.c           |  41 +-
 src/backend/commands/explain.c          |   4 +-
 src/backend/commands/tablecmds.c        |   9 +-
 src/backend/executor/execQual.c         | 276 +++++++------
 src/backend/executor/execTuples.c       |  61 ++-
 src/backend/executor/functions.c        | 124 ++----
 src/backend/executor/nodeFunctionscan.c |  39 +-
 src/backend/executor/spi.c              |  55 +--
 src/backend/parser/parse_func.c         |  12 +-
 src/backend/rewrite/rewriteHandler.c    |  13 +-
 src/backend/utils/adt/Makefile          |   6 +-
 src/backend/utils/adt/lockfuncs.c       |  23 +-
 src/backend/utils/adt/pseudotypes.c     |  55 +--
 src/backend/utils/adt/rowtypes.c        |  75 ++++
 src/backend/utils/adt/sets.c            | 213 ----------
 src/backend/utils/cache/relcache.c      |  30 +-
 src/backend/utils/cache/typcache.c      | 268 ++++++++++++-
 src/backend/utils/fmgr/funcapi.c        |  15 +-
 src/backend/utils/misc/guc.c            |  44 +--
 src/include/access/heapam.h             |   7 +-
 src/include/access/htup.h               | 291 ++++++++------
 src/include/access/tupdesc.h            |  16 +-
 src/include/catalog/catversion.h        |   4 +-
 src/include/catalog/pg_attribute.h      | 490 ++++++++++++------------
 src/include/catalog/pg_class.h          |   4 +-
 src/include/catalog/pg_proc.h           |  12 +-
 src/include/catalog/pg_type.h           |  22 +-
 src/include/executor/executor.h         |   6 +-
 src/include/executor/spi.h              |   6 +-
 src/include/fmgr.h                      |   7 +-
 src/include/funcapi.h                   |  71 ++--
 src/include/nodes/execnodes.h           |  13 +-
 src/include/nodes/nodes.h               |   3 +-
 src/include/nodes/primnodes.h           |   7 +-
 src/include/utils/builtins.h            |  12 +-
 src/include/utils/sets.h                |  27 --
 src/include/utils/typcache.h            |  19 +-
 src/pl/plperl/plperl.c                  |  69 ++--
 src/pl/plpgsql/src/pl_exec.c            |  36 +-
 src/pl/plpython/plpython.c              |  98 +++--
 src/pl/tcl/pltcl.c                      |  81 ++--
 src/test/regress/input/misc.source      |   4 +-
 src/test/regress/output/misc.source     |  18 +-
 src/test/regress/regress.c              |   6 +-
 src/tutorial/funcs.c                    |   4 +-
 src/tutorial/funcs_new.c                |   2 +-
 60 files changed, 1770 insertions(+), 1724 deletions(-)
 create mode 100644 src/backend/utils/adt/rowtypes.c
 delete mode 100644 src/backend/utils/adt/sets.c
 delete mode 100644 src/include/utils/sets.h

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ac941598be7..4a521a0fac7 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 2bb06ff73a4..bd03f5c0c31 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 abc2249aedf..ca082618568 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 622164b91b5..3eccebf476f 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 732f25b6bd1..a6518e34396 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 b7e45e51885..9c3c4430480 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 196fdc6efdd..0cdbdcfb7b5 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 c87a136bdd4..4018c2e3e1b 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 c8f71296857..4b06aefd362 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 f1b20ff2732..4355c0df4c7 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 614cb00f5c2..1a016d86f80 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 902f0621377..695ef36b509 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 f18fece3982..c7fcb400873 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 ed68f9124d5..087e2d7f23c 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 6fe64eadd0d..0640aacbe5a 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 3c134a5c166..e2f3d4aa813 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 1a6bd2fdfc0..a6e3a93d349 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 4c6f95a9a6f..b27e86122bc 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 0bb59d26b11..faf910b736f 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 8dec6131fb4..aa7652e07ef 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 f3fa17c8881..7847b24ffe2 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 1fb60fff02d..bcf7f8d52b6 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 24b2fefe16a..1677493abbc 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 e66eb905f56..d83e8ac4580 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 5223e33816e..427d6334cd4 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 b009e167a3f..bc4901aa546 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 71b99553bc1..0526f52e677 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 00000000000..b487dfc9047
--- /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 33f1ed1a4a8..00000000000
--- 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 5c302e18d39..85ad6ffe788 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 d6e560c34c5..7a8e67c83c8 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 e6ef3fcec3c..dac4a8916e1 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 bc2499ed3af..26f4210ad0e 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 c6579ea2462..9f91d81107d 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 a47e668f9b2..3d48b5f45a3 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 bf4e3aa1055..c7e560c0c65 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 5344d7afe86..82900e55f34 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 754878f0148..896a06ada86 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 eafe5ceb328..8f044b0459a 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 739e1b0da64..7da13b33c90 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 8cbc0aa8e67..9f68e22fdb8 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 1eb158440b1..a3088ca7f62 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 ac2b494b532..2e477e70f87 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 6ed389e8f95..acdcb4ff475 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 a8051fc27f0..fd1660423e1 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 440fcc4d576..8a0fbf7be0f 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 5c34b8d8559..d5d4f0832a7 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 d94196d7400..567310fa1c3 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 b7c1f5fea77..02e6cbca49d 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 82350f2806a..00000000000
--- 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 93f3f163c46..8b85f517000 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 f7b690f71ce..8c3680ae19c 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 b984f0932bb..b71a71bbaac 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 0da1399d836..010f1e1645e 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 37e515ff66b..d9382aff83b 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 c10f73c5c65..ebf13626c7f 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 c478dd71897..3173f718c6c 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 b26eed5b371..31210a8e0d9 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 d2c123a0094..5e8ad6ee707 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 c9413096bcf..e548b85ee41 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;
-- 
GitLab