diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 530cca8be86a20bd3d68a3047331529f3c0b35ed..8c6f6bec2557154f66da32d8829ad62c32a93b11 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.284 2005/04/14 20:03:23 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.285 2005/06/05 00:38:07 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1486,7 +1486,7 @@ AddRelationRawConstraints(Relation rel,
 										NULL,
 										false,
 										true);
-	addRTEtoQuery(pstate, rte, true, true);
+	addRTEtoQuery(pstate, rte, true, true, true);
 
 	/*
 	 * Process column default expressions.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4335792ee712dc82658ec48a160e1eb17a2a2437..c7669dfc58fe6afe43dc771bd14d2ba176222ca7 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.159 2005/05/30 07:20:58 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.160 2005/06/05 00:38:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4720,7 +4720,7 @@ ATPrepAlterColumnType(List **wqueue,
 											NULL,
 											false,
 											true);
-		addRTEtoQuery(pstate, rte, false, true);
+		addRTEtoQuery(pstate, rte, false, true, true);
 
 		transform = transformExpr(pstate, cmd->transform);
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index ee6bfe6ae92ebe512e7d94aa7ecd67425974ea99..1c8fe6cf773012e6f202455fff154afbeeded896 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.321 2005/04/28 21:47:14 tgl Exp $
+ *	$PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.322 2005/06/05 00:38:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -512,7 +512,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 	Query	   *qry = makeNode(Query);
 	Query	   *selectQuery = NULL;
 	List	   *sub_rtable;
-	List	   *sub_namespace;
+	List	   *sub_relnamespace;
+	List	   *sub_varnamespace;
 	List	   *icolumns;
 	List	   *attrnos;
 	ListCell   *icols;
@@ -528,20 +529,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 	 * SELECT.	This can only happen if we are inside a CREATE RULE, and
 	 * in that case we want the rule's OLD and NEW rtable entries to
 	 * appear as part of the SELECT's rtable, not as outer references for
-	 * it.	(Kluge!)  The SELECT's joinlist is not affected however. We
+	 * it.  (Kluge!)  The SELECT's joinlist is not affected however.  We
 	 * must do this before adding the target table to the INSERT's rtable.
 	 */
 	if (stmt->selectStmt)
 	{
 		sub_rtable = pstate->p_rtable;
 		pstate->p_rtable = NIL;
-		sub_namespace = pstate->p_namespace;
-		pstate->p_namespace = NIL;
+		sub_relnamespace = pstate->p_relnamespace;
+		pstate->p_relnamespace = NIL;
+		sub_varnamespace = pstate->p_varnamespace;
+		pstate->p_varnamespace = NIL;
 	}
 	else
 	{
 		sub_rtable = NIL;		/* not used, but keep compiler quiet */
-		sub_namespace = NIL;
+		sub_relnamespace = NIL;
+		sub_varnamespace = NIL;
 	}
 
 	/*
@@ -578,7 +582,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 		 * through 6.5 had bugs of just that nature...)
 		 */
 		sub_pstate->p_rtable = sub_rtable;
-		sub_pstate->p_namespace = sub_namespace;
+		sub_pstate->p_relnamespace = sub_relnamespace;
+		sub_pstate->p_varnamespace = sub_varnamespace;
 
 		/*
 		 * Note: we are not expecting that extras_before and extras_after
@@ -605,7 +610,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 		rte = addRangeTableEntryForSubquery(pstate,
 											selectQuery,
 											makeAlias("*SELECT*", NIL),
-											true);
+											false);
 		rtr = makeNode(RangeTblRef);
 		/* assume new rte is at end */
 		rtr->rtindex = list_length(pstate->p_rtable);
@@ -1481,8 +1486,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 		 */
 		rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
 
-		/* no to join list, yes to namespace */
-		addRTEtoQuery(pstate, rte, false, true);
+		/* no to join list, yes to namespaces */
+		addRTEtoQuery(pstate, rte, false, true, true);
 
 		stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
 												 "WHERE");
@@ -1500,8 +1505,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 			{
 				rte = addRangeTableEntry(pstate, stmt->relation, NULL,
 										 false, true);
-				/* no to join list, yes to namespace */
-				addRTEtoQuery(pstate, rte, false, true);
+				/* no to join list, yes to namespaces */
+				addRTEtoQuery(pstate, rte, false, true, true);
 			}
 			ielem->expr = transformExpr(pstate, ielem->expr);
 
@@ -1559,10 +1564,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 	Assert(pstate->p_rtable == NIL);
 	oldrte = addRangeTableEntryForRelation(pstate, rel,
 										   makeAlias("*OLD*", NIL),
-										   false, true);
+										   false, false);
 	newrte = addRangeTableEntryForRelation(pstate, rel,
 										   makeAlias("*NEW*", NIL),
-										   false, true);
+										   false, false);
 	/* Must override addRangeTableEntry's default access-check flags */
 	oldrte->requiredPerms = 0;
 	newrte->requiredPerms = 0;
@@ -1572,24 +1577,22 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 	 * the one(s) that are relevant for the current kind of rule.  In an
 	 * UPDATE rule, quals must refer to OLD.field or NEW.field to be
 	 * unambiguous, but there's no need to be so picky for INSERT &
-	 * DELETE. (Note we marked the RTEs "inFromCl = true" above to allow
-	 * unqualified references to their fields.)  We do not add them to the
-	 * joinlist.
+	 * DELETE.  We do not add them to the joinlist.
 	 */
 	switch (stmt->event)
 	{
 		case CMD_SELECT:
-			addRTEtoQuery(pstate, oldrte, false, true);
+			addRTEtoQuery(pstate, oldrte, false, true, true);
 			break;
 		case CMD_UPDATE:
-			addRTEtoQuery(pstate, oldrte, false, true);
-			addRTEtoQuery(pstate, newrte, false, true);
+			addRTEtoQuery(pstate, oldrte, false, true, true);
+			addRTEtoQuery(pstate, newrte, false, true, true);
 			break;
 		case CMD_INSERT:
-			addRTEtoQuery(pstate, newrte, false, true);
+			addRTEtoQuery(pstate, newrte, false, true, true);
 			break;
 		case CMD_DELETE:
-			addRTEtoQuery(pstate, oldrte, false, true);
+			addRTEtoQuery(pstate, oldrte, false, true, true);
 			break;
 		default:
 			elog(ERROR, "unrecognized event type: %d",
@@ -1651,10 +1654,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 
 			/*
 			 * Set up OLD/NEW in the rtable for this statement.  The
-			 * entries are marked not inFromCl because we don't want them
-			 * to be referred to by unqualified field names nor "*" in the
-			 * rule actions.  We must add them to the namespace, however,
-			 * or they won't be accessible at all.  We decide later
+			 * entries are added only to relnamespace, not varnamespace,
+			 * because we don't want them to be referred to by unqualified
+			 * field names nor "*" in the rule actions.  We decide later
 			 * whether to put them in the joinlist.
 			 */
 			oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
@@ -1665,8 +1667,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 												   false, false);
 			oldrte->requiredPerms = 0;
 			newrte->requiredPerms = 0;
-			addRTEtoQuery(sub_pstate, oldrte, false, true);
-			addRTEtoQuery(sub_pstate, newrte, false, true);
+			addRTEtoQuery(sub_pstate, oldrte, false, true, false);
+			addRTEtoQuery(sub_pstate, newrte, false, true, false);
 
 			/* Transform the rule action statement */
 			top_subqry = transformStmt(sub_pstate, action,
@@ -1776,7 +1778,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 				/* hack so we can use addRTEtoQuery() */
 				sub_pstate->p_rtable = sub_qry->rtable;
 				sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
-				addRTEtoQuery(sub_pstate, oldrte, true, false);
+				addRTEtoQuery(sub_pstate, oldrte, true, false, false);
 				sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
 			}
 
@@ -1906,10 +1908,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 			   *dtlist;
 	List	   *targetvars,
 			   *targetnames,
-			   *sv_namespace,
+			   *sv_relnamespace,
+			   *sv_varnamespace,
 			   *sv_rtable;
 	RangeTblEntry *jrte;
-	RangeTblRef *jrtr;
 	int			tllen;
 
 	qry->commandType = CMD_SELECT;
@@ -2027,7 +2029,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 
 	/*
 	 * As a first step towards supporting sort clauses that are
-	 * expressions using the output columns, generate a namespace entry
+	 * expressions using the output columns, generate a varnamespace entry
 	 * that makes the output columns visible.  A Join RTE node is handy
 	 * for this, since we can easily control the Vars generated upon
 	 * matches.
@@ -2041,15 +2043,16 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 									 JOIN_INNER,
 									 targetvars,
 									 NULL,
-									 true);
-	jrtr = makeNode(RangeTblRef);
-	jrtr->rtindex = 1;			/* only entry in dummy rtable */
+									 false);
 
 	sv_rtable = pstate->p_rtable;
 	pstate->p_rtable = list_make1(jrte);
 
-	sv_namespace = pstate->p_namespace;
-	pstate->p_namespace = list_make1(jrtr);
+	sv_relnamespace = pstate->p_relnamespace;
+	pstate->p_relnamespace = NIL;	/* no qualified names allowed */
+
+	sv_varnamespace = pstate->p_varnamespace;
+	pstate->p_varnamespace = list_make1(jrte);
 
 	/*
 	 * For now, we don't support resjunk sort clauses on the output of a
@@ -2064,8 +2067,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 										  &qry->targetList,
 									  false /* no unknowns expected */ );
 
-	pstate->p_namespace = sv_namespace;
 	pstate->p_rtable = sv_rtable;
+	pstate->p_relnamespace = sv_relnamespace;
+	pstate->p_varnamespace = sv_varnamespace;
 
 	if (tllen != list_length(qry->targetList))
 		ereport(ERROR,
@@ -2164,7 +2168,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
 		 * happen because the namespace will be empty, but it could happen
 		 * if we are inside a rule.
 		 */
-		if (pstate->p_namespace)
+		if (pstate->p_relnamespace || pstate->p_varnamespace)
 		{
 			if (contain_vars_of_level((Node *) selectQuery, 1))
 				ereport(ERROR,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 8d282d13e4f5198b259e87fa7ed556b60bcb0bf5..593f8f1f4b6352aa1ad80cae0b999ad856be33e5 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.141 2005/06/04 19:19:42 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.142 2005/06/05 00:38:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,14 +47,19 @@ static void extractRemainingColumns(List *common_colnames,
 static Node *transformJoinUsingClause(ParseState *pstate,
 						 List *leftVars, List *rightVars);
 static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
-					  List *containedRels);
-static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
-static RangeTblRef *transformRangeSubselect(ParseState *pstate,
+					  RangeTblEntry *l_rte,
+					  RangeTblEntry *r_rte,
+					  List *relnamespace,
+					  Relids containedRels);
+static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
+static RangeTblEntry *transformRangeSubselect(ParseState *pstate,
 						RangeSubselect *r);
-static RangeTblRef *transformRangeFunction(ParseState *pstate,
+static RangeTblEntry *transformRangeFunction(ParseState *pstate,
 					   RangeFunction *r);
 static Node *transformFromClauseItem(ParseState *pstate, Node *n,
-						List **containedRels);
+						RangeTblEntry **top_rte, int *top_rti,
+						List **relnamespace,
+						Relids *containedRels);
 static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 				   Var *l_colvar, Var *r_colvar);
 static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
@@ -64,12 +69,12 @@ static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
 /*
  * transformFromClause -
  *	  Process the FROM clause and add items to the query's range table,
- *	  joinlist, and namespace.
+ *	  joinlist, and namespaces.
  *
- * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists
- * were initialized to NIL when the pstate was created.  We will add onto
- * any entries already present --- this is needed for rule processing, as
- * well as for UPDATE and DELETE.
+ * Note: we assume that pstate's p_rtable, p_joinlist, p_relnamespace, and
+ * p_varnamespace lists were initialized to NIL when the pstate was created.
+ * We will add onto any entries already present --- this is needed for rule
+ * processing, as well as for UPDATE and DELETE.
  *
  * The range table may grow still further when we transform the expressions
  * in the query's quals and target list. (This is possible because in
@@ -85,17 +90,27 @@ transformFromClause(ParseState *pstate, List *frmList)
 	 * The grammar will have produced a list of RangeVars,
 	 * RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each
 	 * one (possibly adding entries to the rtable), check for duplicate
-	 * refnames, and then add it to the joinlist and namespace.
+	 * refnames, and then add it to the joinlist and namespaces.
 	 */
 	foreach(fl, frmList)
 	{
 		Node	   *n = lfirst(fl);
-		List	   *containedRels;
-
-		n = transformFromClauseItem(pstate, n, &containedRels);
-		checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n);
+		RangeTblEntry *rte;
+		int			rtindex;
+		List	   *relnamespace;
+		Relids		containedRels;
+
+		n = transformFromClauseItem(pstate, n,
+									&rte,
+									&rtindex,
+									&relnamespace,
+									&containedRels);
+		checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace);
 		pstate->p_joinlist = lappend(pstate->p_joinlist, n);
-		pstate->p_namespace = lappend(pstate->p_namespace, n);
+		pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
+											 relnamespace);
+		pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
+		bms_free(containedRels);
 	}
 }
 
@@ -165,10 +180,10 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 	rte->requiredPerms = requiredPerms;
 
 	/*
-	 * If UPDATE/DELETE, add table to joinlist and namespace.
+	 * If UPDATE/DELETE, add table to joinlist and namespaces.
 	 */
 	if (alsoSource)
-		addRTEtoQuery(pstate, rte, true, true);
+		addRTEtoQuery(pstate, rte, true, true, true);
 
 	return rtindex;
 }
@@ -322,10 +337,14 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
  */
 static Node *
 transformJoinOnClause(ParseState *pstate, JoinExpr *j,
-					  List *containedRels)
+					  RangeTblEntry *l_rte,
+					  RangeTblEntry *r_rte,
+					  List *relnamespace,
+					  Relids containedRels)
 {
 	Node	   *result;
-	List	   *save_namespace;
+	List	   *save_relnamespace;
+	List	   *save_varnamespace;
 	Relids		clause_varnos;
 	int			varno;
 
@@ -339,12 +358,16 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
 	 * can't legally alter the namespace by causing implicit relation refs
 	 * to be added.
 	 */
-	save_namespace = pstate->p_namespace;
-	pstate->p_namespace = list_make2(j->larg, j->rarg);
+	save_relnamespace = pstate->p_relnamespace;
+	save_varnamespace = pstate->p_varnamespace;
+
+	pstate->p_relnamespace = relnamespace;
+	pstate->p_varnamespace = list_make2(l_rte, r_rte);
 
 	result = transformWhereClause(pstate, j->quals, "JOIN/ON");
 
-	pstate->p_namespace = save_namespace;
+	pstate->p_relnamespace = save_relnamespace;
+	pstate->p_varnamespace = save_varnamespace;
 
 	/*
 	 * Second, we need to check that the ON condition doesn't refer to any
@@ -355,15 +378,13 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
 	 * here.)
 	 */
 	clause_varnos = pull_varnos(result);
-	while ((varno = bms_first_member(clause_varnos)) >= 0)
+	clause_varnos = bms_del_members(clause_varnos, containedRels);
+	if ((varno = bms_first_member(clause_varnos)) >= 0)
 	{
-		if (!list_member_int(containedRels, varno))
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-					 errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN",
-				   rt_fetch(varno, pstate->p_rtable)->eref->aliasname)));
-		}
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+				 errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN",
+						rt_fetch(varno, pstate->p_rtable)->eref->aliasname)));
 	}
 	bms_free(clause_varnos);
 
@@ -373,11 +394,10 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
 /*
  * transformTableEntry --- transform a RangeVar (simple relation reference)
  */
-static RangeTblRef *
+static RangeTblEntry *
 transformTableEntry(ParseState *pstate, RangeVar *r)
 {
 	RangeTblEntry *rte;
-	RangeTblRef *rtr;
 
 	/*
 	 * mark this entry to indicate it comes from the FROM clause. In SQL,
@@ -389,29 +409,19 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
 	rte = addRangeTableEntry(pstate, r, r->alias,
 							 interpretInhOption(r->inhOpt), true);
 
-	/*
-	 * We create a RangeTblRef, but we do not add it to the joinlist or
-	 * namespace; our caller must do that if appropriate.
-	 */
-	rtr = makeNode(RangeTblRef);
-	/* assume new rte is at end */
-	rtr->rtindex = list_length(pstate->p_rtable);
-	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
-
-	return rtr;
+	return rte;
 }
 
 
 /*
  * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
  */
-static RangeTblRef *
+static RangeTblEntry *
 transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 {
 	List	   *parsetrees;
 	Query	   *query;
 	RangeTblEntry *rte;
-	RangeTblRef *rtr;
 
 	/*
 	 * We require user to supply an alias for a subselect, per SQL92. To
@@ -461,7 +471,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	 * visible in the current query.  Also note that outer references are
 	 * OK.
 	 */
-	if (pstate->p_namespace)
+	if (pstate->p_relnamespace || pstate->p_varnamespace)
 	{
 		if (contain_vars_of_level((Node *) query, 1))
 			ereport(ERROR,
@@ -474,29 +484,19 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	 */
 	rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true);
 
-	/*
-	 * We create a RangeTblRef, but we do not add it to the joinlist or
-	 * namespace; our caller must do that if appropriate.
-	 */
-	rtr = makeNode(RangeTblRef);
-	/* assume new rte is at end */
-	rtr->rtindex = list_length(pstate->p_rtable);
-	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
-
-	return rtr;
+	return rte;
 }
 
 
 /*
  * transformRangeFunction --- transform a function call appearing in FROM
  */
-static RangeTblRef *
+static RangeTblEntry *
 transformRangeFunction(ParseState *pstate, RangeFunction *r)
 {
 	Node	   *funcexpr;
 	char	   *funcname;
 	RangeTblEntry *rte;
-	RangeTblRef *rtr;
 
 	/*
 	 * Get function name for possible use as alias.  We use the same
@@ -520,7 +520,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	 * XXX this will need further work to support SQL99's LATERAL() feature,
 	 * wherein such references would indeed be legal.
 	 */
-	if (pstate->p_namespace)
+	if (pstate->p_relnamespace || pstate->p_varnamespace)
 	{
 		if (contain_vars_of_level(funcexpr, 0))
 			ereport(ERROR,
@@ -558,16 +558,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
 										r, true);
 
-	/*
-	 * We create a RangeTblRef, but we do not add it to the joinlist or
-	 * namespace; our caller must do that if appropriate.
-	 */
-	rtr = makeNode(RangeTblRef);
-	/* assume new rte is at end */
-	rtr->rtindex = list_length(pstate->p_rtable);
-	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
-
-	return rtr;
+	return rte;
 }
 
 
@@ -575,109 +566,151 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
  * transformFromClauseItem -
  *	  Transform a FROM-clause item, adding any required entries to the
  *	  range table list being built in the ParseState, and return the
- *	  transformed item ready to include in the joinlist and namespace.
+ *	  transformed item ready to include in the joinlist and namespaces.
  *	  This routine can recurse to handle SQL92 JOIN expressions.
  *
- *	  Aside from the primary return value (the transformed joinlist item)
- *	  this routine also returns an integer list of the rangetable indexes
- *	  of all the base and join relations represented in the joinlist item.
- *	  This list is needed for checking JOIN/ON conditions in higher levels.
+ * The function return value is the node to add to the jointree (a
+ * RangeTblRef or JoinExpr).  Additional output parameters are:
+ *
+ * *top_rte: receives the RTE corresponding to the jointree item.
+ * (We could extract this from the function return node, but it saves cycles
+ * to pass it back separately.)
+ *
+ * *top_rti: receives the rangetable index of top_rte.  (Ditto.)
+ *
+ * *relnamespace: receives a List of the RTEs exposed as relation names
+ * by this item.
+ *
+ * *containedRels: receives a bitmap set of the rangetable indexes
+ * of all the base and join relations represented in this jointree item.
+ * This is needed for checking JOIN/ON conditions in higher levels.
+ *
+ * We do not need to pass back an explicit varnamespace value, because
+ * in all cases the varnamespace contribution is exactly top_rte.
  */
 static Node *
-transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
+transformFromClauseItem(ParseState *pstate, Node *n,
+						RangeTblEntry **top_rte, int *top_rti,
+						List **relnamespace,
+						Relids *containedRels)
 {
 	if (IsA(n, RangeVar))
 	{
 		/* Plain relation reference */
 		RangeTblRef *rtr;
+		RangeTblEntry *rte;
+		int		rtindex;
 
-		rtr = transformTableEntry(pstate, (RangeVar *) n);
-		*containedRels = list_make1_int(rtr->rtindex);
+		rte = transformTableEntry(pstate, (RangeVar *) n);
+		/* assume new rte is at end */
+		rtindex = list_length(pstate->p_rtable);
+		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
+		*top_rte = rte;
+		*top_rti = rtindex;
+		*relnamespace = list_make1(rte);
+		*containedRels = bms_make_singleton(rtindex);
+		rtr = makeNode(RangeTblRef);
+		rtr->rtindex = rtindex;
 		return (Node *) rtr;
 	}
 	else if (IsA(n, RangeSubselect))
 	{
 		/* sub-SELECT is like a plain relation */
 		RangeTblRef *rtr;
+		RangeTblEntry *rte;
+		int		rtindex;
 
-		rtr = transformRangeSubselect(pstate, (RangeSubselect *) n);
-		*containedRels = list_make1_int(rtr->rtindex);
+		rte = transformRangeSubselect(pstate, (RangeSubselect *) n);
+		/* assume new rte is at end */
+		rtindex = list_length(pstate->p_rtable);
+		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
+		*top_rte = rte;
+		*top_rti = rtindex;
+		*relnamespace = list_make1(rte);
+		*containedRels = bms_make_singleton(rtindex);
+		rtr = makeNode(RangeTblRef);
+		rtr->rtindex = rtindex;
 		return (Node *) rtr;
 	}
 	else if (IsA(n, RangeFunction))
 	{
 		/* function is like a plain relation */
 		RangeTblRef *rtr;
+		RangeTblEntry *rte;
+		int		rtindex;
 
-		rtr = transformRangeFunction(pstate, (RangeFunction *) n);
-		*containedRels = list_make1_int(rtr->rtindex);
+		rte = transformRangeFunction(pstate, (RangeFunction *) n);
+		/* assume new rte is at end */
+		rtindex = list_length(pstate->p_rtable);
+		Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
+		*top_rte = rte;
+		*top_rti = rtindex;
+		*relnamespace = list_make1(rte);
+		*containedRels = bms_make_singleton(rtindex);
+		rtr = makeNode(RangeTblRef);
+		rtr->rtindex = rtindex;
 		return (Node *) rtr;
 	}
 	else if (IsA(n, JoinExpr))
 	{
 		/* A newfangled join expression */
 		JoinExpr   *j = (JoinExpr *) n;
-		List	   *my_containedRels,
-				   *l_containedRels,
-				   *r_containedRels,
+		RangeTblEntry *l_rte;
+		RangeTblEntry *r_rte;
+		int			l_rtindex;
+		int			r_rtindex;
+		Relids		l_containedRels,
+					r_containedRels,
+					my_containedRels;
+		List	   *l_relnamespace,
+				   *r_relnamespace,
+				   *my_relnamespace,
 				   *l_colnames,
 				   *r_colnames,
 				   *res_colnames,
 				   *l_colvars,
 				   *r_colvars,
 				   *res_colvars;
-		Index		leftrti,
-					rightrti;
 		RangeTblEntry *rte;
 
 		/*
 		 * Recursively process the left and right subtrees
 		 */
-		j->larg = transformFromClauseItem(pstate, j->larg, &l_containedRels);
-		j->rarg = transformFromClauseItem(pstate, j->rarg, &r_containedRels);
-
-		/*
-		 * Generate combined list of relation indexes for possible use by
-		 * transformJoinOnClause below.
-		 */
-		my_containedRels = list_concat(l_containedRels, r_containedRels);
+		j->larg = transformFromClauseItem(pstate, j->larg,
+										  &l_rte,
+										  &l_rtindex,
+										  &l_relnamespace,
+										  &l_containedRels);
+		j->rarg = transformFromClauseItem(pstate, j->rarg,
+										  &r_rte,
+										  &r_rtindex,
+										  &r_relnamespace,
+										  &r_containedRels);
 
 		/*
 		 * Check for conflicting refnames in left and right subtrees. Must
 		 * do this because higher levels will assume I hand back a self-
 		 * consistent namespace subtree.
 		 */
-		checkNameSpaceConflicts(pstate, j->larg, j->rarg);
+		checkNameSpaceConflicts(pstate, l_relnamespace, r_relnamespace);
+
+		/*
+		 * Generate combined relation membership info for possible use by
+		 * transformJoinOnClause below.
+		 */
+		my_relnamespace = list_concat(l_relnamespace, r_relnamespace);
+		my_containedRels = bms_join(l_containedRels, r_containedRels);
+
+		pfree(r_relnamespace);	/* free unneeded list header */
 
 		/*
 		 * Extract column name and var lists from both subtrees
 		 *
 		 * Note: expandRTE returns new lists, safe for me to modify
 		 */
-		if (IsA(j->larg, RangeTblRef))
-			leftrti = ((RangeTblRef *) j->larg)->rtindex;
-		else if (IsA(j->larg, JoinExpr))
-			leftrti = ((JoinExpr *) j->larg)->rtindex;
-		else
-		{
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg));
-			leftrti = 0;		/* keep compiler quiet */
-		}
-		rte = rt_fetch(leftrti, pstate->p_rtable);
-		expandRTE(rte, leftrti, 0, false,
+		expandRTE(l_rte, l_rtindex, 0, false,
 				  &l_colnames, &l_colvars);
-
-		if (IsA(j->rarg, RangeTblRef))
-			rightrti = ((RangeTblRef *) j->rarg)->rtindex;
-		else if (IsA(j->rarg, JoinExpr))
-			rightrti = ((JoinExpr *) j->rarg)->rtindex;
-		else
-		{
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg));
-			rightrti = 0;		/* keep compiler quiet */
-		}
-		rte = rt_fetch(rightrti, pstate->p_rtable);
-		expandRTE(rte, rightrti, 0, false,
+		expandRTE(r_rte, r_rtindex, 0, false,
 				  &r_colnames, &r_colvars);
 
 		/*
@@ -829,7 +862,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 		else if (j->quals)
 		{
 			/* User-written ON-condition; transform it */
-			j->quals = transformJoinOnClause(pstate, j, my_containedRels);
+			j->quals = transformJoinOnClause(pstate, j,
+											 l_rte, r_rte,
+											 my_relnamespace,
+											 my_containedRels);
 		}
 		else
 		{
@@ -877,10 +913,27 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 		j->rtindex = list_length(pstate->p_rtable);
 		Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));
 
+		*top_rte = rte;
+		*top_rti = j->rtindex;
+
+		/*
+		 * Prepare returned namespace list.  If the JOIN has an alias
+		 * then it hides the contained RTEs as far as the relnamespace
+		 * goes; otherwise, put the contained RTEs and *not* the JOIN
+		 * into relnamespace.
+		 */
+		if (j->alias)
+		{
+			*relnamespace = list_make1(rte);
+			list_free(my_relnamespace);
+		}
+		else
+			*relnamespace = my_relnamespace;
+
 		/*
-		 * Include join RTE in returned containedRels list
+		 * Include join RTE in returned containedRels set
 		 */
-		*containedRels = lcons_int(j->rtindex, my_containedRels);
+		*containedRels = bms_add_member(my_containedRels, j->rtindex);
 
 		return (Node *) j;
 	}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 39d18ffbf8a38b177b7a7fa7b4dfe9b028775603..5786ac44d0709fb6c54559e3537be849d9e3b67f 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.110 2005/06/04 19:19:42 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.111 2005/06/05 00:38:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,12 +34,9 @@
 /* GUC parameter */
 bool		add_missing_from;
 
-static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
-						const char *refname);
-static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
-					  Oid relid);
-static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
-						 RangeTblEntry *rte1, const char *aliasname1);
+static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
+											  const char *refname);
+static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid);
 static bool isLockedRel(ParseState *pstate, char *refname);
 static void expandRelation(Oid relid, Alias *eref,
 			   int rtindex, int sublevels_up,
@@ -97,188 +94,92 @@ refnameRangeTblEntry(ParseState *pstate,
 
 	while (pstate != NULL)
 	{
-		Node	   *nsnode;
+		RangeTblEntry *result;
 
 		if (OidIsValid(relId))
-			nsnode = scanNameSpaceForRelid(pstate,
-										   (Node *) pstate->p_namespace,
-										   relId);
+			result = scanNameSpaceForRelid(pstate, relId);
 		else
-			nsnode = scanNameSpaceForRefname(pstate,
-											 (Node *) pstate->p_namespace,
-											 refname);
+			result = scanNameSpaceForRefname(pstate, refname);
 
-		if (nsnode)
-		{
-			/* should get an RTE or JoinExpr */
-			if (IsA(nsnode, RangeTblEntry))
-				return (RangeTblEntry *) nsnode;
-			Assert(IsA(nsnode, JoinExpr));
-			return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
-		}
+		if (result)
+			return result;
 
-		pstate = pstate->parentParseState;
 		if (sublevels_up)
 			(*sublevels_up)++;
 		else
 			break;
+
+		pstate = pstate->parentParseState;
 	}
 	return NULL;
 }
 
 /*
- * Recursively search a namespace for an RTE or joinexpr matching the
- * given unqualified refname.  Return the node if a unique match, or NULL
+ * Search the query's table namespace for an RTE matching the
+ * given unqualified refname.  Return the RTE if a unique match, or NULL
  * if no match.  Raise error if multiple matches.
- *
- * The top level of p_namespace is a list, and we recurse into any joins
- * that are not subqueries.
  */
-static Node *
-scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
-						const char *refname)
+static RangeTblEntry *
+scanNameSpaceForRefname(ParseState *pstate, const char *refname)
 {
-	Node	   *result = NULL;
-	Node	   *newresult;
+	RangeTblEntry *result = NULL;
+	ListCell   *l;
 
-	if (nsnode == NULL)
-		return NULL;
-	if (IsA(nsnode, RangeTblRef))
+	foreach(l, pstate->p_relnamespace)
 	{
-		int			varno = ((RangeTblRef *) nsnode)->rtindex;
-		RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
+		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
 		if (strcmp(rte->eref->aliasname, refname) == 0)
-			result = (Node *) rte;
-	}
-	else if (IsA(nsnode, JoinExpr))
-	{
-		JoinExpr   *j = (JoinExpr *) nsnode;
-
-		if (j->alias)
 		{
-			if (strcmp(j->alias->aliasname, refname) == 0)
-				return (Node *) j;		/* matched a join alias */
-
-			/*
-			 * Tables within an aliased join are invisible from outside
-			 * the join, according to the scope rules of SQL92 (the join
-			 * is considered a subquery).  So, stop here.
-			 */
-			return NULL;
-		}
-		result = scanNameSpaceForRefname(pstate, j->larg, refname);
-		newresult = scanNameSpaceForRefname(pstate, j->rarg, refname);
-		if (!result)
-			result = newresult;
-		else if (newresult)
-			ereport(ERROR,
-					(errcode(ERRCODE_AMBIGUOUS_ALIAS),
-					 errmsg("table reference \"%s\" is ambiguous",
-							refname)));
-	}
-	else if (IsA(nsnode, List))
-	{
-		ListCell   *l;
-
-		foreach(l, (List *) nsnode)
-		{
-			newresult = scanNameSpaceForRefname(pstate, lfirst(l), refname);
-			if (!result)
-				result = newresult;
-			else if (newresult)
+			if (result)
 				ereport(ERROR,
 						(errcode(ERRCODE_AMBIGUOUS_ALIAS),
 						 errmsg("table reference \"%s\" is ambiguous",
 								refname)));
+			result = rte;
 		}
 	}
-	else
-		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
 	return result;
 }
 
 /*
- * Recursively search a namespace for a relation RTE matching the
- * given relation OID.	Return the node if a unique match, or NULL
+ * Search the query's table namespace for a relation RTE matching the
+ * given relation OID.	Return the RTE if a unique match, or NULL
  * if no match.  Raise error if multiple matches (which shouldn't
  * happen if the namespace was checked correctly when it was created).
  *
- * The top level of p_namespace is a list, and we recurse into any joins
- * that are not subqueries.
- *
  * See the comments for refnameRangeTblEntry to understand why this
  * acts the way it does.
  */
-static Node *
-scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid)
+static RangeTblEntry *
+scanNameSpaceForRelid(ParseState *pstate, Oid relid)
 {
-	Node	   *result = NULL;
-	Node	   *newresult;
+	RangeTblEntry *result = NULL;
+	ListCell   *l;
 
-	if (nsnode == NULL)
-		return NULL;
-	if (IsA(nsnode, RangeTblRef))
+	foreach(l, pstate->p_relnamespace)
 	{
-		int			varno = ((RangeTblRef *) nsnode)->rtindex;
-		RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
+		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
 		/* yes, the test for alias==NULL should be there... */
 		if (rte->rtekind == RTE_RELATION &&
 			rte->relid == relid &&
 			rte->alias == NULL)
-			result = (Node *) rte;
-	}
-	else if (IsA(nsnode, JoinExpr))
-	{
-		JoinExpr   *j = (JoinExpr *) nsnode;
-
-		if (j->alias)
 		{
-			/*
-			 * Tables within an aliased join are invisible from outside
-			 * the join, according to the scope rules of SQL92 (the join
-			 * is considered a subquery).  So, stop here.
-			 */
-			return NULL;
-		}
-		result = scanNameSpaceForRelid(pstate, j->larg, relid);
-		newresult = scanNameSpaceForRelid(pstate, j->rarg, relid);
-		if (!result)
-			result = newresult;
-		else if (newresult)
-			ereport(ERROR,
-					(errcode(ERRCODE_AMBIGUOUS_ALIAS),
-					 errmsg("table reference %u is ambiguous",
-							relid)));
-	}
-	else if (IsA(nsnode, List))
-	{
-		ListCell   *l;
-
-		foreach(l, (List *) nsnode)
-		{
-			newresult = scanNameSpaceForRelid(pstate, lfirst(l), relid);
-			if (!result)
-				result = newresult;
-			else if (newresult)
+			if (result)
 				ereport(ERROR,
 						(errcode(ERRCODE_AMBIGUOUS_ALIAS),
 						 errmsg("table reference %u is ambiguous",
 								relid)));
+			result = rte;
 		}
 	}
-	else
-		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
 	return result;
 }
 
 /*
- * Recursively check for name conflicts between two namespaces or
- * namespace subtrees.	Raise an error if any is found.
- *
- * Works by recursively scanning namespace1 for RTEs and join nodes,
- * and for each one recursively scanning namespace2 for a match.
+ * Check for relation-name conflicts between two relnamespace lists.
+ * Raise an error if any is found.
  *
  * Note: we assume that each given argument does not contain conflicts
  * itself; we just want to know if the two can be merged together.
@@ -288,108 +189,33 @@ scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid)
  * are for different relation OIDs (implying they are in different schemas).
  */
 void
-checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
-						Node *namespace2)
+checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
+						List *namespace2)
 {
-	if (namespace1 == NULL)
-		return;
-	if (IsA(namespace1, RangeTblRef))
-	{
-		int			varno = ((RangeTblRef *) namespace1)->rtindex;
-		RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
-
-		if (rte->rtekind == RTE_RELATION && rte->alias == NULL)
-			scanNameSpaceForConflict(pstate, namespace2,
-									 rte, rte->eref->aliasname);
-		else
-			scanNameSpaceForConflict(pstate, namespace2,
-									 NULL, rte->eref->aliasname);
-	}
-	else if (IsA(namespace1, JoinExpr))
-	{
-		JoinExpr   *j = (JoinExpr *) namespace1;
-
-		if (j->alias)
-		{
-			scanNameSpaceForConflict(pstate, namespace2,
-									 NULL, j->alias->aliasname);
-
-			/*
-			 * Tables within an aliased join are invisible from outside
-			 * the join, according to the scope rules of SQL92 (the join
-			 * is considered a subquery).  So, stop here.
-			 */
-			return;
-		}
-		checkNameSpaceConflicts(pstate, j->larg, namespace2);
-		checkNameSpaceConflicts(pstate, j->rarg, namespace2);
-	}
-	else if (IsA(namespace1, List))
-	{
-		ListCell   *l;
-
-		foreach(l, (List *) namespace1)
-			checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
-	}
-	else
-		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(namespace1));
-}
+	ListCell   *l1;
 
-/*
- * Subroutine for checkNameSpaceConflicts: scan namespace2
- */
-static void
-scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
-						 RangeTblEntry *rte1, const char *aliasname1)
-{
-	if (nsnode == NULL)
-		return;
-	if (IsA(nsnode, RangeTblRef))
+	foreach(l1, namespace1)
 	{
-		int			varno = ((RangeTblRef *) nsnode)->rtindex;
-		RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
-
-		if (strcmp(rte->eref->aliasname, aliasname1) != 0)
-			return;				/* definitely no conflict */
-		if (rte->rtekind == RTE_RELATION && rte->alias == NULL &&
-			rte1 != NULL && rte->relid != rte1->relid)
-			return;				/* no conflict per SQL92 rule */
-		ereport(ERROR,
-				(errcode(ERRCODE_DUPLICATE_ALIAS),
-				 errmsg("table name \"%s\" specified more than once",
-						aliasname1)));
-	}
-	else if (IsA(nsnode, JoinExpr))
-	{
-		JoinExpr   *j = (JoinExpr *) nsnode;
+		RangeTblEntry *rte1 = (RangeTblEntry *) lfirst(l1);
+		const char *aliasname1 = rte1->eref->aliasname;
+		ListCell   *l2;
 
-		if (j->alias)
+		foreach(l2, namespace2)
 		{
-			if (strcmp(j->alias->aliasname, aliasname1) == 0)
-				ereport(ERROR,
-						(errcode(ERRCODE_DUPLICATE_ALIAS),
+			RangeTblEntry *rte2 = (RangeTblEntry *) lfirst(l2);
+
+			if (strcmp(rte2->eref->aliasname, aliasname1) != 0)
+				continue;		/* definitely no conflict */
+			if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL &&
+				rte2->rtekind == RTE_RELATION && rte2->alias == NULL &&
+				rte1->relid != rte2->relid)
+				continue;		/* no conflict per SQL92 rule */
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_ALIAS),
 					 errmsg("table name \"%s\" specified more than once",
 							aliasname1)));
-
-			/*
-			 * Tables within an aliased join are invisible from outside
-			 * the join, according to the scope rules of SQL92 (the join
-			 * is considered a subquery).  So, stop here.
-			 */
-			return;
 		}
-		scanNameSpaceForConflict(pstate, j->larg, rte1, aliasname1);
-		scanNameSpaceForConflict(pstate, j->rarg, rte1, aliasname1);
 	}
-	else if (IsA(nsnode, List))
-	{
-		ListCell   *l;
-
-		foreach(l, (List *) nsnode)
-			scanNameSpaceForConflict(pstate, lfirst(l), rte1, aliasname1);
-	}
-	else
-		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode));
 }
 
 /*
@@ -541,48 +367,18 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly)
 {
 	Node	   *result = NULL;
 	ParseState *orig_pstate = pstate;
-	int			levels_up = 0;
 
 	while (pstate != NULL)
 	{
-		ListCell   *ns;
-
-		/*
-		 * We need to look only at top-level namespace items, and even for
-		 * those, ignore RTEs that are marked as not inFromCl and not the
-		 * query's target relation.
-		 */
-		foreach(ns, pstate->p_namespace)
-		{
-			Node	   *nsnode = (Node *) lfirst(ns);
-			Node	   *newresult = NULL;
-
-			if (IsA(nsnode, RangeTblRef))
-			{
-				int			varno = ((RangeTblRef *) nsnode)->rtindex;
-				RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
-
-				if (!rte->inFromCl &&
-					rte != pstate->p_target_rangetblentry)
-					continue;
-
-				/* use orig_pstate here to get the right sublevels_up */
-				newresult = scanRTEForColumn(orig_pstate, rte, colname);
-			}
-			else if (IsA(nsnode, JoinExpr))
-			{
-				int			varno = ((JoinExpr *) nsnode)->rtindex;
-				RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
+		ListCell   *l;
 
-				/* joins are always inFromCl, so no need to check */
-				Assert(rte->inFromCl);
+		foreach(l, pstate->p_varnamespace)
+		{
+			RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+			Node	   *newresult;
 
-				/* use orig_pstate here to get the right sublevels_up */
-				newresult = scanRTEForColumn(orig_pstate, rte, colname);
-			}
-			else
-				elog(ERROR, "unrecognized node type: %d",
-					 (int) nodeTag(nsnode));
+			/* use orig_pstate here to get the right sublevels_up */
+			newresult = scanRTEForColumn(orig_pstate, rte, colname);
 
 			if (newresult)
 			{
@@ -599,7 +395,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly)
 			break;				/* found, or don't want to look at parent */
 
 		pstate = pstate->parentParseState;
-		levels_up++;
 	}
 
 	return result;
@@ -1136,22 +931,26 @@ isLockedRel(ParseState *pstate, char *refname)
 
 /*
  * Add the given RTE as a top-level entry in the pstate's join list
- * and/or name space list.	(We assume caller has checked for any
- * namespace conflict.)
+ * and/or name space lists.  (We assume caller has checked for any
+ * namespace conflicts.)
  */
 void
 addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
-			  bool addToJoinList, bool addToNameSpace)
+			  bool addToJoinList,
+			  bool addToRelNameSpace, bool addToVarNameSpace)
 {
-	int			rtindex = RTERangeTablePosn(pstate, rte, NULL);
-	RangeTblRef *rtr = makeNode(RangeTblRef);
-
-	rtr->rtindex = rtindex;
-
 	if (addToJoinList)
+	{
+		int		rtindex = RTERangeTablePosn(pstate, rte, NULL);
+		RangeTblRef *rtr = makeNode(RangeTblRef);
+
+		rtr->rtindex = rtindex;
 		pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
-	if (addToNameSpace)
-		pstate->p_namespace = lappend(pstate->p_namespace, rtr);
+	}
+	if (addToRelNameSpace)
+		pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte);
+	if (addToVarNameSpace)
+		pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
 }
 
 /*
@@ -1166,7 +965,8 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
 	RangeTblEntry *rte;
 
 	rte = addRangeTableEntry(pstate, relation, NULL, false, false);
-	addRTEtoQuery(pstate, rte, true, true);
+	/* Add to joinlist and relnamespace, but not varnamespace */
+	addRTEtoQuery(pstate, rte, true, true, false);
 	warnAutoRange(pstate, relation);
 
 	return rte;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 27e818dcbe2ddfa31af8c6f3a91cf1abf1c63191..dd2c0b4e31c5c5217b97c1c8942653637c783d47 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.135 2005/06/04 19:19:42 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.136 2005/06/05 00:38:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -750,52 +750,32 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
  * ExpandAllTables()
  *		Turns '*' (in the target list) into a list of targetlist entries.
  *
- * tlist entries are generated for each relation appearing at the top level
- * of the query's namespace, except for RTEs marked not inFromCl.  (These
- * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
+ * tlist entries are generated for each relation appearing in the query's
+ * varnamespace.  We do not consider relnamespace because that would include
+ * input tables of aliasless JOINs, NEW/OLD pseudo-entries, implicit RTEs,
+ * etc.
  */
 static List *
 ExpandAllTables(ParseState *pstate)
 {
 	List	   *target = NIL;
-	bool		found_table = false;
-	ListCell   *ns;
-
-	foreach(ns, pstate->p_namespace)
-	{
-		Node	   *n = (Node *) lfirst(ns);
-		int			rtindex;
-		RangeTblEntry *rte;
+	ListCell   *l;
 
-		if (IsA(n, RangeTblRef))
-			rtindex = ((RangeTblRef *) n)->rtindex;
-		else if (IsA(n, JoinExpr))
-			rtindex = ((JoinExpr *) n)->rtindex;
-		else
-		{
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
-			rtindex = 0;		/* keep compiler quiet */
-		}
+	/* Check for SELECT *; */
+	if (!pstate->p_varnamespace)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+			  errmsg("SELECT * with no tables specified is not valid")));
 
-		/*
-		 * Ignore added-on relations that were not listed in the FROM
-		 * clause.
-		 */
-		rte = rt_fetch(rtindex, pstate->p_rtable);
-		if (!rte->inFromCl)
-			continue;
+	foreach(l, pstate->p_varnamespace)
+	{
+		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+		int		rtindex = RTERangeTablePosn(pstate, rte, NULL);
 
-		found_table = true;
 		target = list_concat(target,
 							 expandRelAttrs(pstate, rte, rtindex, 0));
 	}
 
-	/* Check for SELECT *; */
-	if (!found_table)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-			  errmsg("SELECT * with no tables specified is not valid")));
-
 	return target;
 }
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c6de4df714a2600f0fcbc78f554230bb4e5bdc39..bc3e774d640ce8767c6c212da6d2026543d32d15 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.199 2005/06/03 23:05:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.200 2005/06/05 00:38:10 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -4009,8 +4009,8 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
 	 * We use the query's jointree as a guide to what to print.  However,
 	 * we must ignore auto-added RTEs that are marked not inFromCl. (These
 	 * can only appear at the top level of the jointree, so it's
-	 * sufficient to check here.) Also ignore the rule pseudo-RTEs for NEW
-	 * and OLD.
+	 * sufficient to check here.)  This check also ensures we ignore
+	 * the rule pseudo-RTEs for NEW and OLD.
 	 */
 	foreach(l, query->jointree->fromlist)
 	{
@@ -4023,10 +4023,6 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
 
 			if (!rte->inFromCl)
 				continue;
-			if (strcmp(rte->eref->aliasname, "*NEW*") == 0)
-				continue;
-			if (strcmp(rte->eref->aliasname, "*OLD*") == 0)
-				continue;
 		}
 
 		if (first)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1208def12ce63674683d0bce0c19a62e92726e6b..9a525eb1de0c86029aa2c44f799a7564b86a7bf0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.279 2005/06/03 23:05:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.280 2005/06/05 00:38:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -479,10 +479,10 @@ typedef struct DefElem
  *	  FROM clause, but POSTQUEL allows you to refer to tables not listed,
  *	  in which case a range table entry will be generated.	We still support
  *	  this POSTQUEL feature, although there is some doubt whether it's
- *	  convenient or merely confusing.  The flag is needed since an
- *	  implicitly-added RTE shouldn't change the namespace for unqualified
- *	  column names processed later, and it also shouldn't affect the
- *	  expansion of '*'.
+ *	  convenient or merely confusing.  The flag is not actually needed
+ *	  anymore during parsing, since the parser uses a separate "namespace"
+ *	  data structure to control visibility, but it is needed by ruleutils.c
+ *	  to determine whether RTEs should be included in decompiled queries.
  *
  *	  requiredPerms and checkAsUser specify run-time access permissions
  *	  checks to be performed at query startup.	The user must have *all*
@@ -552,7 +552,7 @@ typedef struct RangeTblEntry
 	Alias	   *alias;			/* user-written alias clause, if any */
 	Alias	   *eref;			/* expanded reference names */
 	bool		inh;			/* inheritance requested? */
-	bool		inFromCl;		/* present in FROM clause */
+	bool		inFromCl;		/* present in FROM clause? */
 	AclMode		requiredPerms;	/* bitmask of required access permissions */
 	AclId		checkAsUser;	/* if not zero, check access as this user */
 } RangeTblEntry;
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index bfcf00fd47d7ea922f6e983cbbe7ed5486d74069..291282e09202cee5d97f3496af680ec217337cbe 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.43 2005/04/28 21:47:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.44 2005/06/05 00:38:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,12 +27,19 @@
  * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
  * will become the fromlist of the query's top-level FromExpr node.
  *
- * p_namespace: list of join items that represents the current namespace
- * for table and column lookup.  This may be just a subset of the rtable +
- * joinlist, and/or may contain entries that are not yet added to the main
- * joinlist.  Note that an RTE that is present in p_namespace, but does not
- * have its inFromCl flag set, is accessible only with an explicit qualifier;
- * lookups of unqualified column names should ignore it.
+ * p_relnamespace: list of RTEs that represents the current namespace for
+ * table lookup, ie, those RTEs that are accessible by qualified names.
+ * This may be just a subset of the rtable + joinlist, and/or may contain
+ * entries that are not yet added to the main joinlist.
+ *
+ * p_varnamespace: list of RTEs that represents the current namespace for
+ * column lookup, ie, those RTEs that are accessible by unqualified names.
+ * This is different from p_relnamespace because a JOIN without an alias does
+ * not hide the contained tables (so they must still be in p_relnamespace)
+ * but it does hide their columns (unqualified references to the columns must
+ * refer to the JOIN, not the member tables).  Also, we put POSTQUEL-style
+ * implicit RTEs into p_relnamespace but not p_varnamespace, so that they
+ * do not affect the set of columns available for unqualified references.
  *
  * p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols
  * (zeroth entry in array corresponds to $1).  If p_variableparams is true, the
@@ -49,7 +56,8 @@ typedef struct ParseState
 	List	   *p_rtable;		/* range table so far */
 	List	   *p_joinlist;		/* join items so far (will become FromExpr
 								 * node's fromlist) */
-	List	   *p_namespace;	/* current lookup namespace (join items) */
+	List	   *p_relnamespace;	/* current namespace for relations */
+	List	   *p_varnamespace;	/* current namespace for columns */
 	Oid		   *p_paramtypes;	/* OIDs of types for $n parameter symbols */
 	int			p_numparams;	/* allocated size of p_paramtypes[] */
 	int			p_next_resno;	/* next targetlist resno to assign */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index b74098e10c774705e0a829d78ab89d7d09e0faef..e6d989cf87e4f415850dc2c113979c08a8eedd8f 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.50 2005/06/04 19:19:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.51 2005/06/05 00:38:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,8 +22,8 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
 					 const char *schemaname,
 					 const char *refname,
 					 int *sublevels_up);
-extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
-						Node *namespace2);
+extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
+						List *namespace2);
 extern int RTERangeTablePosn(ParseState *pstate,
 				  RangeTblEntry *rte,
 				  int *sublevels_up);
@@ -64,7 +64,8 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
 						  Alias *alias,
 						  bool inFromCl);
 extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
-			  bool addToJoinList, bool addToNameSpace);
+			  bool addToJoinList,
+			  bool addToRelNameSpace, bool addToVarNameSpace);
 extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation);
 extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
 		  bool include_dropped,