diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 8726797524b12aa25dbb3d193998d53d6356bc34..020011966f010a69f5002928c4661b87ddc0f6e0 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.115 2006/03/14 22:48:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.116 2006/03/16 00:31:54 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -554,3 +554,54 @@ BuildDescForRelation(List *schema)
 
 	return desc;
 }
+
+/*
+ * BuildDescFromLists
+ *
+ * Build a TupleDesc given lists of column names (as String nodes),
+ * column type OIDs, and column typmods.  No constraints are generated.
+ *
+ * This is essentially a cut-down version of BuildDescForRelation for use
+ * with functions returning RECORD.
+ */
+TupleDesc
+BuildDescFromLists(List *names, List *types, List *typmods)
+{
+	int			natts;
+	AttrNumber	attnum;
+	ListCell   *l1;
+	ListCell   *l2;
+	ListCell   *l3;
+	TupleDesc	desc;
+
+	natts = list_length(names);
+	Assert(natts == list_length(types));
+	Assert(natts == list_length(typmods));
+
+	/*
+	 * allocate a new tuple descriptor
+	 */
+	desc = CreateTemplateTupleDesc(natts, false);
+
+	attnum = 0;
+
+	l2 = list_head(types);
+	l3 = list_head(typmods);
+	foreach(l1, names)
+	{
+		char	   *attname = strVal(lfirst(l1));
+		Oid			atttypid;
+		int32		atttypmod;
+
+		atttypid = lfirst_oid(l2);
+		l2 = lnext(l2);
+		atttypmod = lfirst_int(l3);
+		l3 = lnext(l3);
+
+		attnum++;
+
+		TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
+	}
+
+	return desc;
+}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 4ed850f1a5bc580538d5e867b270946e9df26fdf..160647aed72311c8175adb2c20fb1feee31d153b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.50 2006/03/05 15:58:22 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.51 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -850,11 +850,6 @@ doDeletion(const ObjectAddress *object)
  *
  * rtable is the rangetable to be used to interpret Vars with varlevelsup=0.
  * It can be NIL if no such variables are expected.
- *
- * XXX is it important to create dependencies on the datatypes mentioned in
- * the expression?	In most cases this would be redundant (eg, a ref to an
- * operator indirectly references its input and output datatypes), but I'm
- * not quite convinced there are no cases where we need it.
  */
 void
 recordDependencyOnExpr(const ObjectAddress *depender,
@@ -975,6 +970,13 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
  * To do so, we do not scan the joinaliasvars list of a join RTE while
  * scanning the query rangetable, but instead scan each individual entry
  * of the alias list when we find a reference to it.
+ *
+ * Note: in many cases we do not need to create dependencies on the datatypes
+ * involved in an expression, because we'll have an indirect dependency via
+ * some other object.  For instance Var nodes depend on a column which depends
+ * on the datatype, and OpExpr nodes depend on the operator which depends on
+ * the datatype.  However we do need a type dependency if there is no such
+ * indirect dependency, as for example in Const and CoerceToDomain nodes.
  */
 static bool
 find_expr_references_walker(Node *node,
@@ -1033,6 +1035,10 @@ find_expr_references_walker(Node *node,
 		Const	   *con = (Const *) node;
 		Oid			objoid;
 
+		/* A constant must depend on the constant's datatype */
+		add_object_address(OCLASS_TYPE, con->consttype, 0,
+						   &context->addrs);
+
 		/*
 		 * If it's a regclass or similar literal referring to an existing
 		 * object, add a reference to that object.	(Currently, only the
@@ -1081,6 +1087,14 @@ find_expr_references_walker(Node *node,
 		}
 		return false;
 	}
+	if (IsA(node, Param))
+	{
+		Param	   *param = (Param *) node;
+
+		/* A parameter must depend on the parameter's datatype */
+		add_object_address(OCLASS_TYPE, param->paramtype, 0,
+						   &context->addrs);
+	}
 	if (IsA(node, FuncExpr))
 	{
 		FuncExpr   *funcexpr = (FuncExpr *) node;
@@ -1134,6 +1148,29 @@ find_expr_references_walker(Node *node,
 		/* Extra work needed here if we ever need this case */
 		elog(ERROR, "already-planned subqueries not supported");
 	}
+	if (IsA(node, RelabelType))
+	{
+		RelabelType	   *relab = (RelabelType *) node;
+
+		/* since there is no function dependency, need to depend on type */
+		add_object_address(OCLASS_TYPE, relab->resulttype, 0,
+						   &context->addrs);
+	}
+	if (IsA(node, ConvertRowtypeExpr))
+	{
+		ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
+
+		/* since there is no function dependency, need to depend on type */
+		add_object_address(OCLASS_TYPE, cvt->resulttype, 0,
+						   &context->addrs);
+	}
+	if (IsA(node, RowExpr))
+	{
+		RowExpr	   *rowexpr = (RowExpr *) node;
+
+		add_object_address(OCLASS_TYPE, rowexpr->row_typeid, 0,
+						   &context->addrs);
+	}
 	if (IsA(node, RowCompareExpr))
 	{
 		RowCompareExpr *rcexpr = (RowCompareExpr *) node;
@@ -1151,6 +1188,13 @@ find_expr_references_walker(Node *node,
 		}
 		/* fall through to examine arguments */
 	}
+	if (IsA(node, CoerceToDomain))
+	{
+		CoerceToDomain *cd = (CoerceToDomain *) node;
+
+		add_object_address(OCLASS_TYPE, cd->resulttype, 0,
+						   &context->addrs);
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
@@ -1160,17 +1204,32 @@ find_expr_references_walker(Node *node,
 
 		/*
 		 * Add whole-relation refs for each plain relation mentioned in the
-		 * subquery's rtable.  (Note: query_tree_walker takes care of
-		 * recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to do
-		 * that here.  But keep it from looking at join alias lists.)
+		 * subquery's rtable, as well as datatype refs for any datatypes used
+		 * as a RECORD function's output.  (Note: query_tree_walker takes care
+		 * of recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need to
+		 * do that here.  But keep it from looking at join alias lists.)
 		 */
 		foreach(rtable, query->rtable)
 		{
 			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
+			ListCell   *ct;
 
-			if (rte->rtekind == RTE_RELATION)
-				add_object_address(OCLASS_CLASS, rte->relid, 0,
-								   &context->addrs);
+			switch (rte->rtekind)
+			{
+				case RTE_RELATION:
+					add_object_address(OCLASS_CLASS, rte->relid, 0,
+									   &context->addrs);
+					break;
+				case RTE_FUNCTION:
+					foreach(ct, rte->funccoltypes)
+					{
+						add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0,
+										   &context->addrs);
+					}
+					break;
+				default:
+					break;
+			}
 		}
 
 		/* Examine substructure of query */
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 45cbc6c1fcb6fe2e9ef8d2cad44066597aa490c9..494ac209a926492dbdb202bfe7afa5da60b8b6dc 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.37 2006/03/05 15:58:26 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.38 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -202,7 +202,9 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	}
 	else if (functypclass == TYPEFUNC_RECORD)
 	{
-		tupdesc = BuildDescForRelation(rte->coldeflist);
+		tupdesc = BuildDescFromLists(rte->eref->colnames,
+									 rte->funccoltypes,
+									 rte->funccoltypmods);
 	}
 	else
 	{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b38efcdec8b581c7f60e3bd6a5ff7b8008d06061..0a0e5f40b0bb79ebe2a7b758a251d568e34f96bb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.330 2006/03/14 22:48:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.331 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1355,7 +1355,8 @@ _copyRangeTblEntry(RangeTblEntry *from)
 	COPY_SCALAR_FIELD(relid);
 	COPY_NODE_FIELD(subquery);
 	COPY_NODE_FIELD(funcexpr);
-	COPY_NODE_FIELD(coldeflist);
+	COPY_NODE_FIELD(funccoltypes);
+	COPY_NODE_FIELD(funccoltypmods);
 	COPY_SCALAR_FIELD(jointype);
 	COPY_NODE_FIELD(joinaliasvars);
 	COPY_NODE_FIELD(alias);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 0275b01c28e5f99a6f8903bddae9d2f676fae71f..9696925c2f32c6263370184aec8de603fbc70e14 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.266 2006/03/14 22:48:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.267 2006/03/16 00:31:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1730,7 +1730,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 	COMPARE_SCALAR_FIELD(relid);
 	COMPARE_NODE_FIELD(subquery);
 	COMPARE_NODE_FIELD(funcexpr);
-	COMPARE_NODE_FIELD(coldeflist);
+	COMPARE_NODE_FIELD(funccoltypes);
+	COMPARE_NODE_FIELD(funccoltypmods);
 	COMPARE_SCALAR_FIELD(jointype);
 	COMPARE_NODE_FIELD(joinaliasvars);
 	COMPARE_NODE_FIELD(alias);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ef87fedbc216ab9c376693ba0769d029635d3ff3..70e48c071b31c13489080e49260af38bfc0bc1ad 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.270 2006/03/14 22:48:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.271 2006/03/16 00:31:54 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1450,7 +1450,7 @@ _outTypeName(StringInfo str, TypeName *node)
 	WRITE_BOOL_FIELD(pct_type);
 	WRITE_INT_FIELD(typmod);
 	WRITE_NODE_FIELD(arrayBounds);
-	/* location is deliberately not stored */
+	WRITE_INT_FIELD(location);
 }
 
 static void
@@ -1580,7 +1580,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 			break;
 		case RTE_FUNCTION:
 			WRITE_NODE_FIELD(funcexpr);
-			WRITE_NODE_FIELD(coldeflist);
+			WRITE_NODE_FIELD(funccoltypes);
+			WRITE_NODE_FIELD(funccoltypmods);
 			break;
 		case RTE_JOIN:
 			WRITE_ENUM_FIELD(jointype, JoinType);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index c7490a9567ccc59e8df2f8be6f6713a3c03052fb..4e762d3a2ecbcff27208ccc75d3dd44e9a6b54ac 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.186 2006/03/14 22:48:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.187 2006/03/16 00:31:55 tgl Exp $
  *
  * NOTES
  *	  Path and Plan nodes do not have any readfuncs support, because we
@@ -863,42 +863,6 @@ _readFromExpr(void)
  *	Stuff from parsenodes.h.
  */
 
-static ColumnDef *
-_readColumnDef(void)
-{
-	READ_LOCALS(ColumnDef);
-
-	READ_STRING_FIELD(colname);
-	READ_NODE_FIELD(typename);
-	READ_INT_FIELD(inhcount);
-	READ_BOOL_FIELD(is_local);
-	READ_BOOL_FIELD(is_not_null);
-	READ_NODE_FIELD(raw_default);
-	READ_STRING_FIELD(cooked_default);
-	READ_NODE_FIELD(constraints);
-	READ_NODE_FIELD(support);
-
-	READ_DONE();
-}
-
-static TypeName *
-_readTypeName(void)
-{
-	READ_LOCALS(TypeName);
-
-	READ_NODE_FIELD(names);
-	READ_OID_FIELD(typeid);
-	READ_BOOL_FIELD(timezone);
-	READ_BOOL_FIELD(setof);
-	READ_BOOL_FIELD(pct_type);
-	READ_INT_FIELD(typmod);
-	READ_NODE_FIELD(arrayBounds);
-	/* location is deliberately not stored */
-	local_node->location = -1;
-
-	READ_DONE();
-}
-
 /*
  * _readRangeTblEntry
  */
@@ -923,7 +887,8 @@ _readRangeTblEntry(void)
 			break;
 		case RTE_FUNCTION:
 			READ_NODE_FIELD(funcexpr);
-			READ_NODE_FIELD(coldeflist);
+			READ_NODE_FIELD(funccoltypes);
+			READ_NODE_FIELD(funccoltypmods);
 			break;
 		case RTE_JOIN:
 			READ_ENUM_FIELD(jointype, JoinType);
@@ -1042,10 +1007,6 @@ parseNodeString(void)
 		return_value = _readJoinExpr();
 	else if (MATCH("FROMEXPR", 8))
 		return_value = _readFromExpr();
-	else if (MATCH("COLUMNDEF", 9))
-		return_value = _readColumnDef();
-	else if (MATCH("TYPENAME", 8))
-		return_value = _readTypeName();
 	else if (MATCH("RTE", 3))
 		return_value = _readRangeTblEntry();
 	else if (MATCH("NOTIFY", 6))
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 4384a4eaab83fce99cd21736fc8e62f293a4cf0d..9bdb91b47445b9972be2433f50e11e81cdeeb8a2 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.148 2006/03/14 22:48:20 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.149 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,23 +537,27 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	}
 
 	/*
-	 * If a coldeflist is supplied, ensure it defines a legal set of names (no
-	 * duplicates) and datatypes (no pseudo-types, for instance).
+	 * OK, build an RTE for the function.
+	 */
+	rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
+										r, true);
+
+	/*
+	 * If a coldeflist was supplied, ensure it defines a legal set of names
+	 * (no duplicates) and datatypes (no pseudo-types, for instance).
+	 * addRangeTableEntryForFunction looked up the type names but didn't
+	 * check them further than that.
 	 */
 	if (r->coldeflist)
 	{
 		TupleDesc	tupdesc;
 
-		tupdesc = BuildDescForRelation(r->coldeflist);
+		tupdesc = BuildDescFromLists(rte->eref->colnames,
+									 rte->funccoltypes,
+									 rte->funccoltypmods);
 		CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE);
 	}
 
-	/*
-	 * OK, build an RTE for the function.
-	 */
-	rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
-										r, true);
-
 	return rte;
 }
 
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index d0f78b119cdf0e65b88b5b494a3f024e854233ae..e8e11ad167ad758df685e94ea61dd14496b715a7 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.120 2006/03/14 22:48:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.121 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -835,7 +835,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	rte->relid = InvalidOid;
 	rte->subquery = NULL;
 	rte->funcexpr = funcexpr;
-	rte->coldeflist = coldeflist;
+	rte->funccoltypes = NIL;
+	rte->funccoltypmods = NIL;
 	rte->alias = alias;
 
 	eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
@@ -883,14 +884,28 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	{
 		ListCell   *col;
 
-		/* Use the column definition list to form the alias list */
+		/*
+		 * Use the column definition list to form the alias list and
+		 * funccoltypes/funccoltypmods lists.
+		 */
 		foreach(col, coldeflist)
 		{
-			ColumnDef  *n = lfirst(col);
+			ColumnDef  *n = (ColumnDef *) lfirst(col);
 			char	   *attrname;
+			Oid			attrtype;
+			int32		attrtypmod;
 
 			attrname = pstrdup(n->colname);
+			if (n->typename->setof)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("column \"%s\" cannot be declared SETOF",
+								attrname)));
 			eref->colnames = lappend(eref->colnames, makeString(attrname));
+			attrtype = typenameTypeId(pstate, n->typename);
+			attrtypmod = n->typename->typmod;
+			rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
+			rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
 		}
 	}
 	else
@@ -1181,36 +1196,26 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
 				}
 				else if (functypclass == TYPEFUNC_RECORD)
 				{
-					List	   *coldeflist = rte->coldeflist;
-					ListCell   *col;
-					int			attnum = 0;
-
-					foreach(col, coldeflist)
+					if (colnames)
+						*colnames = copyObject(rte->eref->colnames);
+					if (colvars)
 					{
-						ColumnDef  *colDef = lfirst(col);
+						ListCell   *l1;
+						ListCell   *l2;
+						int			attnum = 0;
 
-						attnum++;
-						if (colnames)
-						{
-							char	   *attrname;
-
-							attrname = pstrdup(colDef->colname);
-							*colnames = lappend(*colnames, makeString(attrname));
-						}
-
-						if (colvars)
+						forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
 						{
+							Oid			attrtype = lfirst_oid(l1);
+							int32		attrtypmod = lfirst_int(l2);
 							Var		   *varnode;
-							Oid			atttypid;
-
-							atttypid = typenameTypeId(NULL, colDef->typename);
 
+							attnum++;
 							varnode = makeVar(rtindex,
 											  attnum,
-											  atttypid,
-											  colDef->typename->typmod,
+											  attrtype,
+											  attrtypmod,
 											  sublevels_up);
-
 							*colvars = lappend(*colvars, varnode);
 						}
 					}
@@ -1548,10 +1553,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 				}
 				else if (functypclass == TYPEFUNC_RECORD)
 				{
-					ColumnDef  *colDef = list_nth(rte->coldeflist, attnum - 1);
-
-					*vartype = typenameTypeId(NULL, colDef->typename);
-					*vartypmod = colDef->typename->typmod;
+					*vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
+					*vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
 				}
 				else
 				{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9942bcc291b86989f658e4d74d641d7af9d96671..714140e06179d315ad64b36802612a90d46418bf 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
  * ruleutils.c	- Functions to convert stored expressions/querytrees
  *				back to source text
  *
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.216 2006/03/14 22:48:22 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.217 2006/03/16 00:31:55 tgl Exp $
  **********************************************************************/
 
 #include "postgres.h"
@@ -176,7 +176,7 @@ static void get_from_clause_item(Node *jtnode, Query *query,
 					 deparse_context *context);
 static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
 					  deparse_context *context);
-static void get_from_clause_coldeflist(List *coldeflist,
+static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
 						   deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf);
@@ -1497,13 +1497,16 @@ deparse_context_for_subplan(const char *name, List *tlist,
 
 	/*
 	 * We create an RTE_SPECIAL RangeTblEntry, and store the given tlist
-	 * in its coldeflist field.  This is a hack to make the tlist available
+	 * in its funccoltypes field.  This is a hack to make the tlist available
 	 * to get_name_for_var_field().  RTE_SPECIAL nodes shouldn't appear in
 	 * deparse contexts otherwise.
+	 *
+	 * XXX this desperately needs redesign, as it fails to handle cases where
+	 * we need to drill down through multiple tlists.
 	 */
 	rte->rtekind = RTE_SPECIAL;
 	rte->relid = InvalidOid;
-	rte->coldeflist = tlist;
+	rte->funccoltypes = tlist;
 	rte->eref = makeAlias(name, attrs);
 	rte->inh = false;
 	rte->inFromCl = true;
@@ -2678,9 +2681,9 @@ get_name_for_var_field(Var *var, int fieldno,
 			 * Look into the subplan's target list to get the referenced
 			 * expression, and then pass it to get_expr_result_type().
 			 */
-			if (rte->coldeflist)
+			if (rte->funccoltypes)
 			{
-				TargetEntry *ste = get_tle_by_resno(rte->coldeflist, attnum);
+				TargetEntry *ste = get_tle_by_resno(rte->funccoltypes, attnum);
 
 				if (ste != NULL)
 					expr = (Node *) ste->expr;
@@ -4205,7 +4208,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 	{
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 		RangeTblEntry *rte = rt_fetch(varno, query->rtable);
-		List	   *coldeflist = NIL;
 		bool		gavealias = false;
 
 		switch (rte->rtekind)
@@ -4226,8 +4228,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			case RTE_FUNCTION:
 				/* Function RTE */
 				get_rule_expr(rte->funcexpr, context, true);
-				/* might need to emit column list for RECORD function */
-				coldeflist = rte->coldeflist;
 				break;
 			default:
 				elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
@@ -4265,27 +4265,37 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			gavealias = true;
 		}
 
-		if (coldeflist != NIL)
+		if (rte->rtekind == RTE_FUNCTION)
 		{
-			if (!gavealias)
-				appendStringInfo(buf, " AS ");
-			get_from_clause_coldeflist(coldeflist, context);
+			if (rte->funccoltypes != NIL)
+			{
+				/* Function returning RECORD, reconstruct the columndefs */
+				if (!gavealias)
+					appendStringInfo(buf, " AS ");
+				get_from_clause_coldeflist(rte->eref->colnames,
+										   rte->funccoltypes,
+										   rte->funccoltypmods,
+										   context);
+			}
+			else
+			{
+				/*
+				 * For a function RTE, always emit a complete column alias
+				 * list; this is to protect against possible instability of
+				 * the default column names (eg, from altering parameter
+				 * names).
+				 */
+				get_from_clause_alias(rte->eref, rte, context);
+			}
 		}
 		else
 		{
 			/*
-			 * For a function RTE, always emit a complete column alias list;
-			 * this is to protect against possible instability of the default
-			 * column names (eg, from altering parameter names).  Otherwise
-			 * just report whatever the user originally gave as column
-			 * aliases.
+			 * For non-function RTEs, just report whatever the user originally
+			 * gave as column aliases.
 			 */
-			if (rte->rtekind == RTE_FUNCTION)
-				get_from_clause_alias(rte->eref, rte, context);
-			else
-				get_from_clause_alias(rte->alias, rte, context);
+			get_from_clause_alias(rte->alias, rte, context);
 		}
-
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{
@@ -4463,30 +4473,35 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
  * responsible for ensuring that an alias or AS is present before it.
  */
 static void
-get_from_clause_coldeflist(List *coldeflist, deparse_context *context)
+get_from_clause_coldeflist(List *names, List *types, List *typmods,
+						   deparse_context *context)
 {
 	StringInfo	buf = context->buf;
-	ListCell   *col;
+	ListCell   *l1;
+	ListCell   *l2;
+	ListCell   *l3;
 	int			i = 0;
 
 	appendStringInfoChar(buf, '(');
 
-	foreach(col, coldeflist)
+	l2 = list_head(types);
+	l3 = list_head(typmods);
+	foreach(l1, names)
 	{
-		ColumnDef  *n = lfirst(col);
-		char	   *attname;
-		Oid			atttypeid;
+		char	   *attname = strVal(lfirst(l1));
+		Oid			atttypid;
 		int32		atttypmod;
 
-		attname = n->colname;
-		atttypeid = typenameTypeId(NULL, n->typename);
-		atttypmod = n->typename->typmod;
+		atttypid = lfirst_oid(l2);
+		l2 = lnext(l2);
+		atttypmod = lfirst_int(l3);
+		l3 = lnext(l3);
 
 		if (i > 0)
 			appendStringInfo(buf, ", ");
 		appendStringInfo(buf, "%s %s",
 						 quote_identifier(attname),
-						 format_type_with_typemod(atttypeid, atttypmod));
+						 format_type_with_typemod(atttypid, atttypmod));
 		i++;
 	}
 
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 1a5e23e1d1be110a7c347ace0aecf8b587bcf804..670ef5c92e0e7434d522816f401ea386d5f01416 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.48 2006/03/05 15:58:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupdesc.h,v 1.49 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,4 +92,6 @@ extern void TupleDescInitEntry(TupleDesc desc,
 
 extern TupleDesc BuildDescForRelation(List *schema);
 
+extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods);
+
 #endif   /* TUPDESC_H */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4b541531f8404cd96a4dbdada472aa90952e9280..2ae52c723fce01494ecff641dde23f09b03c33f2 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.320 2006/03/10 20:15:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.321 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200603101
+#define CATALOG_VERSION_NO	200603151
 
 #endif
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0e7d5bf9f51859e87503dc8102dd5f0e6cfa2621..aea32d236d9d7b2bfada58cdd69c5156e603ad2a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.304 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.305 2006/03/16 00:31:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -362,8 +362,8 @@ typedef struct RangeFunction
 	NodeTag		type;
 	Node	   *funccallnode;	/* untransformed function call tree */
 	Alias	   *alias;			/* table alias & optional column aliases */
-	List	   *coldeflist;		/* list of ColumnDef nodes for runtime
-								 * assignment of RECORD TupleDesc */
+	List	   *coldeflist;		/* list of ColumnDef nodes to describe
+								 * result of function returning RECORD */
 } RangeFunction;
 
 /*
@@ -547,10 +547,14 @@ typedef struct RangeTblEntry
 
 	/*
 	 * Fields valid for a function RTE (else NULL):
+	 *
+	 * If the function returns RECORD, funccoltypes lists the column types
+	 * declared in the RTE's column type specification, and funccoltypmods
+	 * lists their declared typmods.  Otherwise, both fields are NIL.
 	 */
 	Node	   *funcexpr;		/* expression tree for func call */
-	List	   *coldeflist;		/* list of ColumnDef nodes for runtime
-								 * assignment of RECORD TupleDesc */
+	List	   *funccoltypes;	/* OID list of column type OIDs */
+	List	   *funccoltypmods;	/* integer list of column typmods */
 
 	/*
 	 * Fields valid for a join RTE (else NULL/zero):