diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 9d4e1c6a5c48773571b2d948d3769dbccee4ada0..59c3151cdf2f3f1b621e975f337d3f0a92a667ba 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.36 2004/05/26 04:41:06 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.37 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -970,10 +970,15 @@ find_expr_references_walker(Node *node,
 		if (var->varno <= 0 || var->varno > list_length(rtable))
 			elog(ERROR, "invalid varno %d", var->varno);
 		rte = rt_fetch(var->varno, rtable);
+		/*
+		 * A whole-row Var references no specific columns, so adds no new
+		 * dependency.
+		 */
+		if (var->varattno == InvalidAttrNumber)
+			return false;
 		if (rte->rtekind == RTE_RELATION)
 		{
 			/* If it's a plain relation, reference this column */
-			/* NB: this code works for whole-row Var with attno 0, too */
 			add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
 							   &context->addrs);
 		}
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 24a51149e45db497082592979efd84d173e0aeac..d770c9175bc644f18fc6fae0ffa3f7681ce9ecc1 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.118 2004/06/05 01:55:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.119 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,10 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes,
 						bool *differentTypes);
 static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
 					  bool *differentTypes);
-static void subquery_push_qual(Query *subquery,
-							   RangeTblEntry *rte, Index rti, Node *qual);
+static void subquery_push_qual(Query *subquery, List *rtable,
+							   Index rti, Node *qual);
 static void recurse_push_qual(Node *setOp, Query *topquery,
-							  RangeTblEntry *rte, Index rti, Node *qual);
+							  List *rtable, Index rti, Node *qual);
 
 
 /*
@@ -376,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
 			if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
 			{
 				/* Push it down */
-				subquery_push_qual(subquery, rte, rti, clause);
+				subquery_push_qual(subquery, root->rtable, rti, clause);
 			}
 			else
 			{
@@ -780,12 +780,13 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
  * subquery_push_qual - push down a qual that we have determined is safe
  */
 static void
-subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
+subquery_push_qual(Query *subquery, List *rtable, Index rti, Node *qual)
 {
 	if (subquery->setOperations != NULL)
 	{
 		/* Recurse to push it separately to each component query */
-		recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual);
+		recurse_push_qual(subquery->setOperations, subquery,
+						  rtable, rti, qual);
 	}
 	else
 	{
@@ -799,7 +800,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
 		 * This step also ensures that when we are pushing into a setop tree,
 		 * each component query gets its own copy of the qual.
 		 */
-		qual = ResolveNew(qual, rti, 0, rte,
+		qual = ResolveNew(qual, rti, 0, rtable,
 						  subquery->targetList,
 						  CMD_SELECT, 0);
 		subquery->havingQual = make_and_qual(subquery->havingQual,
@@ -818,7 +819,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
  */
 static void
 recurse_push_qual(Node *setOp, Query *topquery,
-				  RangeTblEntry *rte, Index rti, Node *qual)
+				  List *rtable, Index rti, Node *qual)
 {
 	if (IsA(setOp, RangeTblRef))
 	{
@@ -827,14 +828,14 @@ recurse_push_qual(Node *setOp, Query *topquery,
 		Query	   *subquery = subrte->subquery;
 
 		Assert(subquery != NULL);
-		subquery_push_qual(subquery, rte, rti, qual);
+		subquery_push_qual(subquery, rtable, rti, qual);
 	}
 	else if (IsA(setOp, SetOperationStmt))
 	{
 		SetOperationStmt *op = (SetOperationStmt *) setOp;
 
-		recurse_push_qual(op->larg, topquery, rte, rti, qual);
-		recurse_push_qual(op->rarg, topquery, rte, rti, qual);
+		recurse_push_qual(op->larg, topquery, rtable, rti, qual);
+		recurse_push_qual(op->rarg, topquery, rtable, rti, qual);
 	}
 	else
 	{
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1eb4d5504a1981c34b9a292ec776f74246baed1e..430ec9e5db725205f43a0aea716929dc8b63b63f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.20 2004/05/30 23:40:29 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.21 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,7 +46,7 @@ typedef struct reduce_outer_joins_state
 static bool is_simple_subquery(Query *subquery);
 static bool has_nullable_targetlist(Query *subquery);
 static void resolvenew_in_jointree(Node *jtnode, int varno,
-								   RangeTblEntry *rte, List *subtlist);
+								   List *rtable, List *subtlist);
 static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
 static void reduce_outer_joins_pass2(Node *jtnode,
 						 reduce_outer_joins_state *state,
@@ -243,16 +243,19 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
 			subtlist = subquery->targetList;
 			parse->targetList = (List *)
 				ResolveNew((Node *) parse->targetList,
-						   varno, 0, rte, subtlist, CMD_SELECT, 0);
+						   varno, 0, parse->rtable,
+						   subtlist, CMD_SELECT, 0);
 			resolvenew_in_jointree((Node *) parse->jointree, varno,
-								   rte, subtlist);
+								   parse->rtable, subtlist);
 			Assert(parse->setOperations == NULL);
 			parse->havingQual =
 				ResolveNew(parse->havingQual,
-						   varno, 0, rte, subtlist, CMD_SELECT, 0);
+						   varno, 0, parse->rtable,
+						   subtlist, CMD_SELECT, 0);
 			parse->in_info_list = (List *)
 				ResolveNew((Node *) parse->in_info_list,
-						   varno, 0, rte, subtlist, CMD_SELECT, 0);
+						   varno, 0, parse->rtable,
+						   subtlist, CMD_SELECT, 0);
 
 			foreach(rt, parse->rtable)
 			{
@@ -261,7 +264,8 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
 				if (otherrte->rtekind == RTE_JOIN)
 					otherrte->joinaliasvars = (List *)
 						ResolveNew((Node *) otherrte->joinaliasvars,
-								   varno, 0, rte, subtlist, CMD_SELECT, 0);
+								   varno, 0, parse->rtable,
+								   subtlist, CMD_SELECT, 0);
 			}
 
 			/*
@@ -477,7 +481,7 @@ has_nullable_targetlist(Query *subquery)
  */
 static void
 resolvenew_in_jointree(Node *jtnode, int varno,
-					   RangeTblEntry *rte, List *subtlist)
+					   List *rtable, List *subtlist)
 {
 	if (jtnode == NULL)
 		return;
@@ -491,18 +495,20 @@ resolvenew_in_jointree(Node *jtnode, int varno,
 		ListCell   *l;
 
 		foreach(l, f->fromlist)
-			resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);
+			resolvenew_in_jointree(lfirst(l), varno, rtable, subtlist);
 		f->quals = ResolveNew(f->quals,
-							  varno, 0, rte, subtlist, CMD_SELECT, 0);
+							  varno, 0, rtable,
+							  subtlist, CMD_SELECT, 0);
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{
 		JoinExpr   *j = (JoinExpr *) jtnode;
 
-		resolvenew_in_jointree(j->larg, varno, rte, subtlist);
-		resolvenew_in_jointree(j->rarg, varno, rte, subtlist);
+		resolvenew_in_jointree(j->larg, varno, rtable, subtlist);
+		resolvenew_in_jointree(j->rarg, varno, rtable, subtlist);
 		j->quals = ResolveNew(j->quals,
-							  varno, 0, rte, subtlist, CMD_SELECT, 0);
+							  varno, 0, rtable,
+							  subtlist, CMD_SELECT, 0);
 
 		/*
 		 * We don't bother to update the colvars list, since it won't be
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index e46655e17dbd1868d1c941728782ec7959f9a2d9..94e0f15e289fd3c345797308a887c8e9df8ef169 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.59 2004/06/01 04:47:46 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.60 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -515,11 +515,19 @@ flatten_join_alias_vars_mutator(Node *node,
 			/* Must expand whole-row reference */
 			RowExpr		*rowexpr;
 			List		*fields = NIL;
+			AttrNumber	attnum;
 			ListCell	*l;
 
+			attnum = 0;
 			foreach(l, rte->joinaliasvars)
 			{
 				newvar = (Node *) lfirst(l);
+				attnum++;
+				/* Ignore dropped columns */
+				if (get_rte_attribute_is_dropped(context->root->rtable,
+												 var->varno,
+												 attnum))
+					continue;
 				/*
 				 * If we are expanding an alias carried down from an upper
 				 * query, must adjust its varlevelsup fields.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index b3a6b67f5c399a0c100744b1bfea15d1aa4f1e4a..156dbac5aad69181197ff5e2a88461caddd44c15 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.133 2004/06/16 01:26:44 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.134 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -655,8 +655,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg));
 			leftrti = 0;		/* keep compiler quiet */
 		}
-		rte = rt_fetch(leftrti, pstate->p_rtable);
-		expandRTE(pstate, rte, &l_colnames, &l_colvars);
+		expandRTE(pstate->p_rtable, leftrti, 0, false,
+				  &l_colnames, &l_colvars);
 
 		if (IsA(j->rarg, RangeTblRef))
 			rightrti = ((RangeTblRef *) j->rarg)->rtindex;
@@ -667,8 +667,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg));
 			rightrti = 0;		/* keep compiler quiet */
 		}
-		rte = rt_fetch(rightrti, pstate->p_rtable);
-		expandRTE(pstate, rte, &r_colnames, &r_colvars);
+		expandRTE(pstate->p_rtable, rightrti, 0, false,
+				  &r_colnames, &r_colvars);
 
 		/*
 		 * Natural join does not explicitly specify columns; must generate
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 8065b261beb1935c7592d81c6c40843e31470b3c..8d892fcffe12272a01161576c3528638a8168f6f 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.120 2004/08/17 18:47:08 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.121 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -662,29 +662,12 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
 	else if (node && IsA(node, Var) &&
 			 ((Var *) node)->varattno == InvalidAttrNumber)
 	{
-		RangeTblEntry *rte;
-		AttrNumber nfields;
-		AttrNumber nf;
-
-		rte = GetRTEByRangeTablePosn(pstate,
-									 ((Var *) node)->varno,
-									 ((Var *) node)->varlevelsup);
-		nfields = list_length(rte->eref->colnames);
-		for (nf = 1; nf <= nfields; nf++)
-		{
-			Oid		vartype;
-			int32	vartypmod;
+		int		rtindex = ((Var *) node)->varno;
+		int		sublevels_up = ((Var *) node)->varlevelsup;
+		List *rtable;
 
-			if (get_rte_attribute_is_dropped(rte, nf))
-				continue;
-			get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
-			args = lappend(args,
-						   makeVar(((Var *) node)->varno,
-								   nf,
-								   vartype,
-								   vartypmod,
-								   ((Var *) node)->varlevelsup));
-		}
+		rtable = GetLevelNRangeTable(pstate, sublevels_up);
+		expandRTE(rtable, rtindex, sublevels_up, false, NULL, &args);
 	}
 	else
 		ereport(ERROR,
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 3f32f8c80f5340f7aac15270e9b05e96a142f560..1a9aa2cd73fa76cb21413c5910b81a986e485e45 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.97 2004/08/17 18:47:08 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.98 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,10 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
 static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
 						 RangeTblEntry *rte1, const char *aliasname1);
 static bool isForUpdate(ParseState *pstate, char *refname);
+static void expandRelation(Oid relid, Alias *eref,
+						   int rtindex, int sublevels_up,
+						   bool include_dropped,
+						   List **colnames, List **colvars);
 static int	specialAttNum(const char *attname);
 static void warnAutoRange(ParseState *pstate, RangeVar *relation);
 
@@ -438,6 +442,27 @@ GetRTEByRangeTablePosn(ParseState *pstate,
 	return rt_fetch(varno, pstate->p_rtable);
 }
 
+/*
+ * GetLevelNRangeTable
+ *	  Get the rangetable list for the N'th query level up from current.
+ */
+List *
+GetLevelNRangeTable(ParseState *pstate, int sublevels_up)
+{
+	int			index = 0;
+
+	while (pstate != NULL)
+	{
+		if (index == sublevels_up)
+			return pstate->p_rtable;
+		index++;
+		pstate = pstate->parentParseState;
+	}
+
+	elog(ERROR, "rangetable not found (internal error)");
+	return NIL;					/* keep compiler quiet */
+}
+
 /*
  * scanRTEForColumn
  *	  Search the column names of a single RTE for the given name.
@@ -464,27 +489,20 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
 	 * Scan the user column names (or aliases) for a match. Complain if
 	 * multiple matches.
 	 *
-	 * Note: because eref->colnames may include names of dropped columns, we
-	 * need to check for non-droppedness before accepting a match. This
-	 * takes an extra cache lookup, but we can skip the lookup most of the
-	 * time by exploiting the knowledge that dropped columns are assigned
-	 * dummy names starting with '.', which is an unusual choice for
-	 * actual column names.
+	 * Note: eref->colnames may include entries for dropped columns,
+	 * but those will be empty strings that cannot match any legal SQL
+	 * identifier, so we don't bother to test for that case here.
 	 *
-	 * Should the user try to fool us by altering pg_attribute.attname for a
-	 * dropped column, we'll still catch it by virtue of the checks in
-	 * get_rte_attribute_type(), which is called by make_var().  That
-	 * routine has to do a cache lookup anyway, so the check there is
-	 * cheap.
+	 * Should this somehow go wrong and we try to access a dropped column,
+	 * we'll still catch it by virtue of the checks in
+	 * get_rte_attribute_type(), which is called by make_var().  That routine
+	 * has to do a cache lookup anyway, so the check there is cheap.
 	 */
 	foreach(c, rte->eref->colnames)
 	{
 		attnum++;
 		if (strcmp(strVal(lfirst(c)), colname) == 0)
 		{
-			if (colname[0] == '.' &&	/* see note above */
-				get_rte_attribute_is_dropped(rte, attnum))
-				continue;
 			if (result)
 				ereport(ERROR,
 						(errcode(ERRCODE_AMBIGUOUS_COLUMN),
@@ -633,6 +651,81 @@ qualifiedNameToVar(ParseState *pstate,
 	return scanRTEForColumn(pstate, rte, colname);
 }
 
+/*
+ * buildRelationAliases
+ *		Construct the eref column name list for a relation RTE.
+ *		This code is also used for the case of a function RTE returning
+ *		a named composite type.
+ *
+ * tupdesc: the physical column information
+ * alias: the user-supplied alias, or NULL if none
+ * eref: the eref Alias to store column names in
+ *
+ * eref->colnames is filled in.  Also, alias->colnames is rebuilt to insert
+ * empty strings for any dropped columns, so that it will be one-to-one with
+ * physical column numbers.
+ */
+static void
+buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
+{
+	int			maxattrs = tupdesc->natts;
+	ListCell   *aliaslc;
+	int			numaliases;
+	int			varattno;
+	int			numdropped = 0;
+
+	Assert(eref->colnames == NIL);
+
+	if (alias)
+	{
+		aliaslc = list_head(alias->colnames);
+		numaliases = list_length(alias->colnames);
+		/* We'll rebuild the alias colname list */
+		alias->colnames = NIL;
+	}
+	else
+	{
+		aliaslc = NULL;
+		numaliases = 0;
+	}
+
+	for (varattno = 0; varattno < maxattrs; varattno++)
+	{
+		Form_pg_attribute attr = tupdesc->attrs[varattno];
+		Value	   *attrname;
+
+		if (attr->attisdropped)
+		{
+			/* Always insert an empty string for a dropped column */
+			attrname = makeString(pstrdup(""));
+			if (aliaslc)
+				alias->colnames = lappend(alias->colnames, attrname);
+			numdropped++;
+		}
+		else if (aliaslc)
+		{
+			/* Use the next user-supplied alias */
+			attrname = (Value *) lfirst(aliaslc);
+			aliaslc = lnext(aliaslc);
+			alias->colnames = lappend(alias->colnames, attrname);
+		}
+		else
+		{
+			attrname = makeString(pstrdup(NameStr(attr->attname)));
+			/* we're done with the alias if any */
+		}
+
+		eref->colnames = lappend(eref->colnames, attrname);
+	}
+
+	/* Too many user-supplied aliases? */
+	if (aliaslc)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("table \"%s\" has %d columns available but %d columns specified",
+						eref->aliasname, maxattrs - numdropped, numaliases)));
+}
+
 /*
  * Add an entry for a relation to the pstate's range table (p_rtable).
  *
@@ -653,10 +746,6 @@ addRangeTableEntry(ParseState *pstate,
 	char	   *refname = alias ? alias->aliasname : relation->relname;
 	LOCKMODE	lockmode;
 	Relation	rel;
-	Alias	   *eref;
-	int			maxattrs;
-	int			numaliases;
-	int			varattno;
 
 	rte->rtekind = RTE_RELATION;
 	rte->alias = alias;
@@ -671,29 +760,12 @@ addRangeTableEntry(ParseState *pstate,
 	rel = heap_openrv(relation, lockmode);
 	rte->relid = RelationGetRelid(rel);
 
-	eref = alias ? (Alias *) copyObject(alias) : makeAlias(refname, NIL);
-	numaliases = list_length(eref->colnames);
-
 	/*
-	 * Since the rel is open anyway, let's check that the number of column
-	 * aliases is reasonable. - Thomas 2000-02-04
+	 * Build the list of effective column names using user-supplied aliases
+	 * and/or actual column names.
 	 */
-	maxattrs = RelationGetNumberOfAttributes(rel);
-	if (maxattrs < numaliases)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-				 errmsg("table \"%s\" has %d columns available but %d columns specified",
-				   RelationGetRelationName(rel), maxattrs, numaliases)));
-
-	/* fill in any unspecified alias columns using actual column names */
-	for (varattno = numaliases; varattno < maxattrs; varattno++)
-	{
-		char	   *attrname;
-
-		attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-		eref->colnames = lappend(eref->colnames, makeString(attrname));
-	}
-	rte->eref = eref;
+	rte->eref = makeAlias(refname, NIL);
+	buildRelationAliases(rel->rd_att, alias, rte->eref);
 
 	/*
 	 * Drop the rel refcount, but keep the access lock till end of
@@ -747,10 +819,6 @@ addRangeTableEntryForRelation(ParseState *pstate,
 	char	   *refname = alias->aliasname;
 	LOCKMODE	lockmode;
 	Relation	rel;
-	Alias	   *eref;
-	int			maxattrs;
-	int			numaliases;
-	int			varattno;
 
 	rte->rtekind = RTE_RELATION;
 	rte->alias = alias;
@@ -765,29 +833,12 @@ addRangeTableEntryForRelation(ParseState *pstate,
 	rel = heap_open(relid, lockmode);
 	rte->relid = relid;
 
-	eref = (Alias *) copyObject(alias);
-	numaliases = list_length(eref->colnames);
-
 	/*
-	 * Since the rel is open anyway, let's check that the number of column
-	 * aliases is reasonable. - Thomas 2000-02-04
+	 * Build the list of effective column names using user-supplied aliases
+	 * and/or actual column names.
 	 */
-	maxattrs = RelationGetNumberOfAttributes(rel);
-	if (maxattrs < numaliases)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-				 errmsg("table \"%s\" has %d columns available but %d columns specified",
-				   RelationGetRelationName(rel), maxattrs, numaliases)));
-
-	/* fill in any unspecified alias columns using actual column names */
-	for (varattno = numaliases; varattno < maxattrs; varattno++)
-	{
-		char	   *attrname;
-
-		attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-		eref->colnames = lappend(eref->colnames, makeString(attrname));
-	}
-	rte->eref = eref;
+	rte->eref = makeAlias(refname, NIL);
+	buildRelationAliases(rel->rd_att, alias, rte->eref);
 
 	/*
 	 * Drop the rel refcount, but keep the access lock till end of
@@ -918,8 +969,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	Alias	   *alias = rangefunc->alias;
 	List	   *coldeflist = rangefunc->coldeflist;
 	Alias	   *eref;
-	int			numaliases;
-	int			varattno;
 
 	rte->rtekind = RTE_FUNCTION;
 	rte->relid = InvalidOid;
@@ -928,11 +977,9 @@ addRangeTableEntryForFunction(ParseState *pstate,
 	rte->coldeflist = coldeflist;
 	rte->alias = alias;
 
-	eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
+	eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
 	rte->eref = eref;
 
-	numaliases = list_length(eref->colnames);
-
 	/*
 	 * Now determine if the function returns a simple or composite type,
 	 * and check/add column aliases.
@@ -969,7 +1016,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
 		 */
 		Oid			funcrelid = typeidTypeRelid(funcrettype);
 		Relation	rel;
-		int			maxattrs;
 
 		if (!OidIsValid(funcrelid))		/* shouldn't happen if typtype is
 										 * 'c' */
@@ -981,26 +1027,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
 		 */
 		rel = relation_open(funcrelid, AccessShareLock);
 
-		/*
-		 * Since the rel is open anyway, let's check that the number of
-		 * column aliases is reasonable.
-		 */
-		maxattrs = RelationGetNumberOfAttributes(rel);
-		if (maxattrs < numaliases)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-					 errmsg("table \"%s\" has %d columns available but %d columns specified",
-							RelationGetRelationName(rel),
-							maxattrs, numaliases)));
-
-		/* fill in alias columns using actual column names */
-		for (varattno = numaliases; varattno < maxattrs; varattno++)
-		{
-			char	   *attrname;
-
-			attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
-			eref->colnames = lappend(eref->colnames, makeString(attrname));
-		}
+		/* Build the column alias list */
+		buildRelationAliases(rel->rd_att, alias, eref);
 
 		/*
 		 * Drop the rel refcount, but keep the access lock till end of
@@ -1015,12 +1043,16 @@ addRangeTableEntryForFunction(ParseState *pstate,
 		 * Must be a base data type, i.e. scalar. Just add one alias
 		 * column named for the function.
 		 */
-		if (numaliases > 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-			  errmsg("too many column aliases specified for function %s",
-					 funcname)));
-		if (numaliases == 0)
+		if (alias && alias->colnames != NIL)
+		{
+			if (list_length(alias->colnames) != 1)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+						 errmsg("too many column aliases specified for function %s",
+								funcname)));
+			eref->colnames = copyObject(alias->colnames);
+		}
+		else
 			eref->colnames = list_make1(makeString(eref->aliasname));
 	}
 	else if (functyptype == 'p' && funcrettype == RECORDOID)
@@ -1028,7 +1060,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
 		ListCell   *col;
 
 		/* Use the column definition list to form the alias list */
-		eref->colnames = NIL;
 		foreach(col, coldeflist)
 		{
 			ColumnDef  *n = lfirst(col);
@@ -1202,77 +1233,42 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
 	return rte;
 }
 
-/* expandRTE()
+/*
+ * expandRTE -- expand the columns of a rangetable entry
  *
- * Given a rangetable entry, create lists of its column names (aliases if
- * provided, else real names) and Vars for each column.  Only user columns
- * are considered, since this is primarily used to expand '*' and determine
- * the contents of JOIN tables.
+ * This creates lists of an RTE's column names (aliases if provided, else
+ * real names) and Vars for each column.  Only user columns are considered.
+ * If include_dropped is FALSE then dropped columns are omitted from the
+ * results.  If include_dropped is TRUE then empty strings and NULL constants
+ * (not Vars!) are returned for dropped columns.
  *
+ * The target RTE is the rtindex'th entry of rtable.  (The whole rangetable
+ * must be passed since we need it to determine dropped-ness for JOIN columns.)
+ * sublevels_up is the varlevelsup value to use in the created Vars.
+ *
+ * The output lists go into *colnames and *colvars.
  * If only one of the two kinds of output list is needed, pass NULL for the
  * output pointer for the unwanted one.
  */
 void
-expandRTE(ParseState *pstate, RangeTblEntry *rte,
+expandRTE(List *rtable, int rtindex, int sublevels_up,
+		  bool include_dropped,
 		  List **colnames, List **colvars)
 {
-	int			rtindex,
-				sublevels_up,
-				varattno;
+	RangeTblEntry *rte = rt_fetch(rtindex, rtable);
+	int			varattno;
 
 	if (colnames)
 		*colnames = NIL;
 	if (colvars)
 		*colvars = NIL;
 
-	/* Need the RT index of the entry for creating Vars */
-	rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
 	switch (rte->rtekind)
 	{
 		case RTE_RELATION:
-			{
-				/* Ordinary relation RTE */
-				Relation	rel;
-				int			maxattrs;
-				int			numaliases;
-
-				rel = heap_open(rte->relid, AccessShareLock);
-				maxattrs = RelationGetNumberOfAttributes(rel);
-				numaliases = list_length(rte->eref->colnames);
-
-				for (varattno = 0; varattno < maxattrs; varattno++)
-				{
-					Form_pg_attribute attr = rel->rd_att->attrs[varattno];
-
-					if (attr->attisdropped)
-						continue;
-
-					if (colnames)
-					{
-						char	   *label;
-
-						if (varattno < numaliases)
-							label = strVal(list_nth(rte->eref->colnames, varattno));
-						else
-							label = NameStr(attr->attname);
-						*colnames = lappend(*colnames, makeString(pstrdup(label)));
-					}
-
-					if (colvars)
-					{
-						Var		   *varnode;
-
-						varnode = makeVar(rtindex, attr->attnum,
-										  attr->atttypid, attr->atttypmod,
-										  sublevels_up);
-
-						*colvars = lappend(*colvars, varnode);
-					}
-				}
-
-				heap_close(rel, AccessShareLock);
-			}
+			/* Ordinary relation RTE */
+			expandRelation(rte->relid, rte->eref, rtindex, sublevels_up,
+						   include_dropped, colnames, colvars);
 			break;
 		case RTE_SUBQUERY:
 			{
@@ -1318,60 +1314,22 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 				/* Function RTE */
 				Oid			funcrettype = exprType(rte->funcexpr);
 				char		functyptype = get_typtype(funcrettype);
-				List	   *coldeflist = rte->coldeflist;
 
 				if (functyptype == 'c')
 				{
 					/*
-					 * Composite data type, i.e. a table's row type Same
-					 * as ordinary relation RTE
+					 * Composite data type, i.e. a table's row type
+					 *
+					 * Same as ordinary relation RTE
 					 */
 					Oid			funcrelid = typeidTypeRelid(funcrettype);
-					Relation	rel;
-					int			maxattrs;
-					int			numaliases;
 
 					if (!OidIsValid(funcrelid)) /* shouldn't happen */
 						elog(ERROR, "invalid typrelid for complex type %u",
 							 funcrettype);
 
-					rel = relation_open(funcrelid, AccessShareLock);
-					maxattrs = RelationGetNumberOfAttributes(rel);
-					numaliases = list_length(rte->eref->colnames);
-
-					for (varattno = 0; varattno < maxattrs; varattno++)
-					{
-						Form_pg_attribute attr = rel->rd_att->attrs[varattno];
-
-						if (attr->attisdropped)
-							continue;
-
-						if (colnames)
-						{
-							char	   *label;
-
-							if (varattno < numaliases)
-								label = strVal(list_nth(rte->eref->colnames, varattno));
-							else
-								label = NameStr(attr->attname);
-							*colnames = lappend(*colnames, makeString(pstrdup(label)));
-						}
-
-						if (colvars)
-						{
-							Var		   *varnode;
-
-							varnode = makeVar(rtindex,
-											  attr->attnum,
-											  attr->atttypid,
-											  attr->atttypmod,
-											  sublevels_up);
-
-							*colvars = lappend(*colvars, varnode);
-						}
-					}
-
-					relation_close(rel, AccessShareLock);
+					expandRelation(funcrelid, rte->eref, rtindex, sublevels_up,
+								   include_dropped, colnames, colvars);
 				}
 				else if (functyptype == 'b' || functyptype == 'd')
 				{
@@ -1395,6 +1353,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 				}
 				else if (functyptype == 'p' && funcrettype == RECORDOID)
 				{
+					List	   *coldeflist = rte->coldeflist;
 					ListCell   *col;
 					int			attnum = 0;
 
@@ -1447,11 +1406,41 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 				{
 					varattno++;
 
+					/*
+					 * During ordinary parsing, there will never be any
+					 * deleted columns in the join; but we have to check
+					 * since this routine is also used by the rewriter,
+					 * and joins found in stored rules might have join
+					 * columns for since-deleted columns.
+					 */
+					if (get_rte_attribute_is_dropped(rtable, rtindex,
+													 varattno))
+					{
+						if (include_dropped)
+						{
+							if (colnames)
+								*colnames = lappend(*colnames,
+													makeString(pstrdup("")));
+							if (colvars)
+							{
+								/*
+								 * can't use atttypid here, but it doesn't
+								 * really matter what type the Const claims to
+								 * be.
+								 */
+								*colvars = lappend(*colvars,
+												   makeNullConst(INT4OID));
+							}
+						}
+						continue;
+					}
+
 					if (colnames)
 					{
 						char	   *label = strVal(lfirst(colname));
 
-						*colnames = lappend(*colnames, makeString(pstrdup(label)));
+						*colnames = lappend(*colnames,
+											makeString(pstrdup(label)));
 					}
 
 					if (colvars)
@@ -1474,13 +1463,78 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
 	}
 }
 
+/*
+ * expandRelation -- expandRTE subroutine
+ */
+static void
+expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up,
+			   bool include_dropped,
+			   List **colnames, List **colvars)
+{
+	Relation	rel;
+	int			varattno;
+	int			maxattrs;
+	int			numaliases;
+
+	rel = relation_open(relid, AccessShareLock);
+	maxattrs = RelationGetNumberOfAttributes(rel);
+	numaliases = list_length(eref->colnames);
+
+	for (varattno = 0; varattno < maxattrs; varattno++)
+	{
+		Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+
+		if (attr->attisdropped)
+		{
+			if (include_dropped)
+			{
+				if (colnames)
+					*colnames = lappend(*colnames, makeString(pstrdup("")));
+				if (colvars)
+				{
+					/*
+					 * can't use atttypid here, but it doesn't really matter
+					 * what type the Const claims to be.
+					 */
+					*colvars = lappend(*colvars, makeNullConst(INT4OID));
+				}
+			}
+			continue;
+		}
+
+		if (colnames)
+		{
+			char	   *label;
+
+			if (varattno < numaliases)
+				label = strVal(list_nth(eref->colnames, varattno));
+			else
+				label = NameStr(attr->attname);
+			*colnames = lappend(*colnames, makeString(pstrdup(label)));
+		}
+
+		if (colvars)
+		{
+			Var		   *varnode;
+
+			varnode = makeVar(rtindex, attr->attnum,
+							  attr->atttypid, attr->atttypmod,
+							  sublevels_up);
+
+			*colvars = lappend(*colvars, varnode);
+		}
+	}
+
+	relation_close(rel, AccessShareLock);
+}
+
 /*
  * expandRelAttrs -
  *	  Workhorse for "*" expansion: produce a list of targetentries
  *	  for the attributes of the rte
  */
 List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
+expandRelAttrs(ParseState *pstate, List *rtable, int rtindex, int sublevels_up)
 {
 	List	   *names,
 			   *vars;
@@ -1488,9 +1542,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
 			   *var;
 	List	   *te_list = NIL;
 
-	expandRTE(pstate, rte, &names, &vars);
+	expandRTE(rtable, rtindex, sublevels_up, false, &names, &vars);
 
-	forboth (name, names, var, vars)
+	forboth(name, names, var, vars)
 	{
 		char	   *label = strVal(lfirst(name));
 		Node	   *varnode = (Node *) lfirst(var);
@@ -1698,15 +1752,16 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
  *		Check whether attempted attribute ref is to a dropped column
  */
 bool
-get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
+get_rte_attribute_is_dropped(List *rtable, int rtindex, AttrNumber attnum)
 {
+	RangeTblEntry *rte = rt_fetch(rtindex, rtable);
 	bool		result;
 
 	switch (rte->rtekind)
 	{
 		case RTE_RELATION:
 			{
-				/* Plain relation RTE --- get the attribute's type info */
+				/* Plain relation RTE --- get the attribute's catalog entry */
 				HeapTuple	tp;
 				Form_pg_attribute att_tup;
 
@@ -1723,10 +1778,38 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
 			}
 			break;
 		case RTE_SUBQUERY:
-		case RTE_JOIN:
-			/* Subselect and join RTEs never have dropped columns */
+			/* Subselect RTEs never have dropped columns */
 			result = false;
 			break;
+		case RTE_JOIN:
+			{
+				/*
+				 * A join RTE would not have dropped columns when constructed,
+				 * but one in a stored rule might contain columns that were
+				 * dropped from the underlying tables, if said columns are
+				 * nowhere explicitly referenced in the rule.  So we have to
+				 * recursively look at the referenced column.
+				 */
+				Var		*aliasvar;
+
+				if (attnum <= 0 ||
+					attnum > list_length(rte->joinaliasvars))
+					elog(ERROR, "invalid varattno %d", attnum);
+				aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
+				/*
+				 * If the list item isn't a simple Var, then it must
+				 * represent a merged column, ie a USING column, and so it
+				 * couldn't possibly be dropped (since it's referenced in
+				 * the join clause).
+				 */
+				if (!IsA(aliasvar, Var))
+					result = false;
+				else
+					result = get_rte_attribute_is_dropped(rtable,
+														  aliasvar->varno,
+														  aliasvar->varattno);
+			}
+			break;
 		case RTE_FUNCTION:
 			{
 				/* Function RTE */
@@ -1736,8 +1819,9 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
 				if (OidIsValid(funcrelid))
 				{
 					/*
-					 * Composite data type, i.e. a table's row type Same
-					 * as ordinary relation RTE
+					 * Composite data type, i.e. a table's row type
+					 *
+					 * Same as ordinary relation RTE
 					 */
 					HeapTuple	tp;
 					Form_pg_attribute att_tup;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 7dcbc7b7b741d0e9a04d54ff1e3824730cd37b36..1ca2b457936ac606e164b6a61a88205260c4f8bd 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.122 2004/06/19 18:19:55 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.123 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -699,6 +699,8 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
 		char	   *relname;
 		RangeTblEntry *rte;
 		int			sublevels_up;
+		int			rtindex;
+		List	   *rtable;
 
 		switch (numnames)
 		{
@@ -743,7 +745,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
 			rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
 													  relname));
 
-		return expandRelAttrs(pstate, rte);
+		rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
+		rtable = GetLevelNRangeTable(pstate, sublevels_up);
+
+		return expandRelAttrs(pstate, rtable, rtindex, sublevels_up);
 	}
 }
 
@@ -765,29 +770,31 @@ ExpandAllTables(ParseState *pstate)
 	foreach(ns, pstate->p_namespace)
 	{
 		Node	   *n = (Node *) lfirst(ns);
+		int			rtindex;
 		RangeTblEntry *rte;
 
 		if (IsA(n, RangeTblRef))
-			rte = rt_fetch(((RangeTblRef *) n)->rtindex,
-						   pstate->p_rtable);
+			rtindex = ((RangeTblRef *) n)->rtindex;
 		else if (IsA(n, JoinExpr))
-			rte = rt_fetch(((JoinExpr *) n)->rtindex,
-						   pstate->p_rtable);
+			rtindex = ((JoinExpr *) n)->rtindex;
 		else
 		{
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
-			rte = NULL;			/* keep compiler quiet */
+			rtindex = 0;			/* keep compiler quiet */
 		}
 
 		/*
 		 * Ignore added-on relations that were not listed in the FROM
 		 * clause.
 		 */
+		rte = rt_fetch(rtindex, pstate->p_rtable);
 		if (!rte->inFromCl)
 			continue;
 
 		found_table = true;
-		target = list_concat(target, expandRelAttrs(pstate, rte));
+		target = list_concat(target,
+							 expandRelAttrs(pstate, pstate->p_rtable,
+											rtindex, 0));
 	}
 
 	/* Check for SELECT *; */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index fadc7416ad958d32d0580450db20e81b4a69a86e..7358d745f78604a1d16e8192890f2bd0ca887ef2 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.141 2004/08/07 17:40:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.142 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -214,8 +214,7 @@ rewriteRuleAction(Query *parsetree,
 		sub_action = (Query *) ResolveNew((Node *) sub_action,
 										  new_varno,
 										  0,
-										  rt_fetch(new_varno,
-												   sub_action->rtable),
+										  sub_action->rtable,
 										  parsetree->targetList,
 										  event,
 										  current_varno);
@@ -1040,7 +1039,7 @@ CopyAndAddInvertedQual(Query *parsetree,
 		new_qual = ResolveNew(new_qual,
 							  PRS2_NEW_VARNO,
 							  0,
-							  rt_fetch(rt_index, parsetree->rtable),
+							  parsetree->rtable,
 							  parsetree->targetList,
 							  event,
 							  rt_index);
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 86412e90634adf34a0c47e82c09c23d0d1d39320..6480d8853b5d95f518abc3efe565b6cad61bee3d 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.85 2004/08/17 18:47:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.86 2004/08/19 20:57:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
 #include "parser/parsetree.h"
-#include "parser/parse_clause.h"
+#include "parser/parse_relation.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
@@ -853,9 +853,10 @@ AddInvertedQual(Query *parsetree, Node *qual)
  * If not, we either change the unmatched Var's varno to update_varno
  * (when event == CMD_UPDATE) or replace it with a constant NULL.
  *
- * The caller must also provide target_rte, the RTE describing the target
- * relation.  This is needed to handle whole-row Vars referencing the target.
- * We expand such Vars into RowExpr constructs.
+ * The caller must also provide target_rtable, the rangetable containing
+ * the target relation (which must be described by the target_varno'th
+ * RTE in that list).  This is needed to handle whole-row Vars referencing
+ * the target.  We expand such Vars into RowExpr constructs.
  *
  * Note: the business with inserted_sublink is needed to update hasSubLinks
  * in subqueries when the replacement adds a subquery inside a subquery.
@@ -868,7 +869,7 @@ typedef struct
 {
 	int			target_varno;
 	int			sublevels_up;
-	RangeTblEntry *target_rte;
+	List	   *target_rtable;
 	List	   *targetlist;
 	int			event;
 	int			update_varno;
@@ -931,40 +932,21 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
 			if (var->varattno == InvalidAttrNumber)
 			{
 				/* Must expand whole-tuple reference into RowExpr */
-				RangeTblEntry *rte = context->target_rte;
 				RowExpr *rowexpr;
-				List	*fields = NIL;
-				AttrNumber nfields = list_length(rte->eref->colnames);
-				AttrNumber nf;
-
-				for (nf = 1; nf <= nfields; nf++)
-				{
-					if (get_rte_attribute_is_dropped(rte, nf))
-					{
-						/*
-						 * can't determine att type here, but it doesn't
-						 * really matter what type the Const claims to be.
-						 */
-						fields = lappend(fields,
-										 makeNullConst(INT4OID));
-					}
-					else
-					{
-						Oid		vartype;
-						int32	vartypmod;
-						Var	   *newvar;
-
-						get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
-						newvar = makeVar(this_varno,
-										 nf,
-										 vartype,
-										 vartypmod,
-										 this_varlevelsup);
-						fields = lappend(fields,
-										 resolve_one_var(newvar, context));
-					}
-				}
-
+				List	*fields;
+
+				/*
+				 * If generating an expansion for a var of a named rowtype
+				 * (ie, this is a plain relation RTE), then we must include
+				 * dummy items for dropped columns.  If the var is RECORD
+				 * (ie, this is a JOIN), then omit dropped columns.
+				 */
+				expandRTE(context->target_rtable, this_varno, this_varlevelsup,
+						  (var->vartype != RECORDOID),
+						  NULL, &fields);
+				/* Adjust the generated per-field Vars... */
+				fields = (List *) ResolveNew_mutator((Node *) fields,
+													 context);
 				rowexpr = makeNode(RowExpr);
 				rowexpr->args = fields;
 				rowexpr->row_typeid = var->vartype;
@@ -1003,14 +985,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
 
 Node *
 ResolveNew(Node *node, int target_varno, int sublevels_up,
-		   RangeTblEntry *target_rte,
+		   List *target_rtable,
 		   List *targetlist, int event, int update_varno)
 {
 	ResolveNew_context context;
 
 	context.target_varno = target_varno;
 	context.sublevels_up = sublevels_up;
-	context.target_rte = target_rte;
+	context.target_rtable = target_rtable;
 	context.targetlist = targetlist;
 	context.event = event;
 	context.update_varno = update_varno;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8e1420d9267b037212034c5d83a08a96f32be277..52089d221185137c5d1cccc5266534fbdb9edcbf 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.177 2004/08/17 18:47:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.178 2004/08/19 20:57:41 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -203,6 +203,8 @@ static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_from_clause(Query *query, deparse_context *context);
 static void get_from_clause_item(Node *jtnode, Query *query,
 					 deparse_context *context);
+static void get_from_clause_alias(Alias *alias, int varno,
+								  Query *query, deparse_context *context);
 static void get_from_clause_coldeflist(List *coldeflist,
 						   deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
@@ -3962,20 +3964,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			appendStringInfo(buf, " %s",
 							 quote_identifier(rte->alias->aliasname));
 			gavealias = true;
-			if (rte->alias->colnames != NIL && coldeflist == NIL)
-			{
-				ListCell   *col;
-
-				appendStringInfoChar(buf, '(');
-				foreach(col, rte->alias->colnames)
-				{
-					if (col != list_head(rte->alias->colnames))
-						appendStringInfo(buf, ", ");
-					appendStringInfoString(buf,
-										   quote_identifier(strVal(lfirst(col))));
-				}
-				appendStringInfoChar(buf, ')');
-			}
+			if (coldeflist == NIL)
+				get_from_clause_alias(rte->alias, varno, query, context);
 		}
 		else if (rte->rtekind == RTE_RELATION &&
 			 strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0)
@@ -4128,20 +4118,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 		{
 			appendStringInfo(buf, " %s",
 							 quote_identifier(j->alias->aliasname));
-			if (j->alias->colnames != NIL)
-			{
-				ListCell   *col;
-
-				appendStringInfoChar(buf, '(');
-				foreach(col, j->alias->colnames)
-				{
-					if (col != list_head(j->alias->colnames))
-						appendStringInfo(buf, ", ");
-					appendStringInfoString(buf,
-								  quote_identifier(strVal(lfirst(col))));
-				}
-				appendStringInfoChar(buf, ')');
-			}
+			get_from_clause_alias(j->alias, j->rtindex, query, context);
 		}
 	}
 	else
@@ -4149,6 +4126,43 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			 (int) nodeTag(jtnode));
 }
 
+/*
+ * get_from_clause_alias - reproduce column alias list
+ *
+ * This is tricky because we must ignore dropped columns.
+ */
+static void
+get_from_clause_alias(Alias *alias, int varno,
+					  Query *query, deparse_context *context)
+{
+	StringInfo	buf = context->buf;
+	ListCell   *col;
+	AttrNumber attnum;
+	bool		first = true;
+
+	if (alias == NULL || alias->colnames == NIL)
+		return;					/* definitely nothing to do */
+
+	attnum = 0;
+	foreach(col, alias->colnames)
+	{
+		attnum++;
+		if (get_rte_attribute_is_dropped(query->rtable, varno, attnum))
+			continue;
+		if (first)
+		{
+			appendStringInfoChar(buf, '(');
+			first = false;
+		}
+		else
+			appendStringInfo(buf, ", ");
+		appendStringInfoString(buf,
+							   quote_identifier(strVal(lfirst(col))));
+	}
+	if (!first)
+		appendStringInfoChar(buf, ')');
+}
+
 /*
  * get_from_clause_coldeflist - reproduce FROM clause coldeflist
  *
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 80230e344466fd6d5033ee8178042acb21348bc4..aff28bea08fb67104ee91235c918bb5869f56149 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.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/parsenodes.h,v 1.265 2004/08/04 21:34:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.266 2004/08/19 20:57:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -447,6 +447,22 @@ typedef struct DefElem
  *	  eref->aliasname is required to be present, and should generally be used
  *	  to identify the RTE for error messages etc.
  *
+ *	  In RELATION RTEs, the colnames in both alias and eref are indexed by
+ *	  physical attribute number; this means there must be colname entries for
+ *	  dropped columns.  When building an RTE we insert empty strings ("") for
+ *	  dropped columns.  Note however that a stored rule may have nonempty
+ *	  colnames for columns dropped since the rule was created (and for that
+ *	  matter the colnames might be out of date due to column renamings).
+ *	  The same comments apply to FUNCTION RTEs when the function's return type
+ *	  is a named composite type.
+ *
+ *	  In JOIN RTEs, the colnames in both alias and eref are one-to-one with
+ *	  joinaliasvars entries.  A JOIN RTE will omit columns of its inputs when
+ *	  those columns are known to be dropped at parse time.  Again, however,
+ *	  a stored rule might contain entries for columns dropped since the rule
+ *	  was created.  (This is only possible for columns not actually referenced
+ *	  in the rule.)
+ *
  *	  inh is TRUE for relation references that should be expanded to include
  *	  inheritance children, if the rel has any.  This *must* be FALSE for
  *	  RTEs other than RTE_RELATION entries.
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 5abdbd548fd3fc40e80e8e06fdbe6e5e573e2ccf..56f359978d4a1d60efd2912793b3ab68e883c92c 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.101 2004/08/17 18:47:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.102 2004/08/19 20:57:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -98,9 +98,9 @@ typedef struct Resdom
  *	  specifies an alias for a range variable; the alias might also
  *	  specify renaming of columns within the table.
  *
- * Note: colnames is a list of Value nodes (always strings).  In an RTE's
- * eref Alias, the colnames list includes dropped columns, so that the
- * colname list position matches the physical attribute number.
+ * Note: colnames is a list of Value nodes (always strings).  In Alias structs
+ * associated with RTEs, there may be entries corresponding to dropped
+ * columns; these are normally empty strings ("").  See parsenodes.h for info.
  */
 typedef struct Alias
 {
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 724639dd96ad56d820b6fb891a39e96d4f12e83a..afb6c6303d6b13605f9eb99bfa9281495a9b3835 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.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/parser/parse_relation.h,v 1.44 2004/04/18 18:12:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.45 2004/08/19 20:57:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@ extern int RTERangeTablePosn(ParseState *pstate,
 extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
 											 int varno,
 											 int sublevels_up);
+extern List *GetLevelNRangeTable(ParseState *pstate, int sublevels_up);
 extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
 							  char *colname);
 extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly);
@@ -66,9 +67,11 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
 extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
 			  bool addToJoinList, bool addToNameSpace);
 extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation);
-extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
-		  List **colnames, List **colvars);
-extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
+extern void expandRTE(List *rtable, int rtindex, int sublevels_up,
+					  bool include_dropped,
+					  List **colnames, List **colvars);
+extern List *expandRelAttrs(ParseState *pstate, List *rtable,
+							int rtindex, int sublevels_up);
 extern int	attnameAttNum(Relation rd, const char *attname, bool sysColOK);
 extern Name attnumAttName(Relation rd, int attid);
 extern Oid	attnumTypeId(Relation rd, int attid);
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index e7f401ac315c896d4a12d4b17b31c019898c442b..a972cd733122dd42e10bc8d203c463962ce7a532 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.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/parser/parsetree.h,v 1.25 2004/08/17 18:47:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.26 2004/08/19 20:57:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,7 +59,7 @@ extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
  * Check whether an attribute of an RTE has been dropped (note that
  * get_rte_attribute_type will fail on such an attr)
  */
-extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
+extern bool get_rte_attribute_is_dropped(List *rtable, int rtindex,
 										 AttrNumber attnum);
 
 
diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h
index ce4702de35ba49dd29fca8543617c850b186b985..a17fd918b22235723c11503f460def1bf56fb1a3 100644
--- a/src/include/rewrite/rewriteManip.h
+++ b/src/include/rewrite/rewriteManip.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/rewrite/rewriteManip.h,v 1.35 2004/05/10 22:44:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.36 2004/08/19 20:57:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,7 @@ extern bool checkExprHasAggs(Node *node);
 extern bool checkExprHasSubLink(Node *node);
 
 extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
-						RangeTblEntry *target_rte,
+						List *target_rtable,
 						List *targetlist, int event, int update_varno);
 
 #endif   /* REWRITEMANIP_H */