diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 8fb977e725c31e966a7d75951b2aabce9b796350..53ad37bd61ad5d748c25740c2ae2a0adb85728b1 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
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.9 2002/09/04 20:31:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.10 2002/09/11 14:48:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -684,6 +684,12 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 
 /*
  * Recursively search an expression tree for object references.
+ *
+ * Note: we avoid creating references to columns of tables that participate
+ * in an SQL JOIN construct, but are not actually used anywhere in the query.
+ * To do so, we do not scan the joinaliasvars list of a join RTE while
+ * scanning the query rangetable, but instead scan each individual entry
+ * of the alias list when we find a reference to it.
  */
 static bool
 find_expr_references_walker(Node *node,
@@ -716,10 +722,24 @@ find_expr_references_walker(Node *node,
 			elog(ERROR, "find_expr_references_walker: bogus varno %d",
 				 var->varno);
 		rte = rt_fetch(var->varno, rtable);
-		/* If it's a plain relation, reference this column */
 		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);
+		}
+		else if (rte->rtekind == RTE_JOIN)
+		{
+			/* Scan join output column to add references to join inputs */
+			if (var->varattno <= 0 ||
+				var->varattno > length(rte->joinaliasvars))
+				elog(ERROR, "find_expr_references_walker: bogus varattno %d",
+					 var->varattno);
+			find_expr_references_walker((Node *) nth(var->varattno - 1,
+													 rte->joinaliasvars),
+										context);
+		}
 		return false;
 	}
 	if (IsA(node, Expr))
@@ -766,7 +786,7 @@ find_expr_references_walker(Node *node,
 		 * Add whole-relation refs for each plain relation mentioned in
 		 * the subquery's rtable.  (Note: query_tree_walker takes care of
 		 * recursing into RTE_FUNCTION and RTE_SUBQUERY RTEs, so no need
-		 * to do that here.)
+		 * to do that here.  But keep it from looking at join alias lists.)
 		 */
 		foreach(rtable, query->rtable)
 		{
@@ -781,7 +801,8 @@ find_expr_references_walker(Node *node,
 		context->rtables = lcons(query->rtable, context->rtables);
 		result = query_tree_walker(query,
 								   find_expr_references_walker,
-								   (void *) context, true);
+								   (void *) context,
+								   QTW_IGNORE_JOINALIASES);
 		context->rtables = lnext(context->rtables);
 		return result;
 	}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index fb4fd3e1d345a7505e44c1a179b15e794f4020eb..414e26c54cbeef538205d4ef7e31f1171f8f3b98 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.78 2002/09/04 20:31:22 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.79 2002/09/11 14:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -774,7 +774,7 @@ adjust_inherited_attrs(Node *node,
 		if (newnode->resultRelation == old_rt_index)
 			newnode->resultRelation = new_rt_index;
 		query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
-						   (void *) &context, false);
+						   (void *) &context, QTW_IGNORE_SUBQUERIES);
 		return (Node *) newnode;
 	}
 	else
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index edf77faff02442e20c5056d227b09c24c16a6a62..ee037974769a65de0c7707b1ef282ce2421a55c6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.108 2002/09/04 20:31:22 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.109 2002/09/11 14:48:54 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1788,7 +1788,7 @@ simplify_op_or_func(Expr *expr, List *args)
  *		{
  *			adjust context for subquery;
  *			result = query_tree_walker((Query *) node, my_walker, context,
- *									   true); // to visit subquery RTEs too
+ *									   0); // to visit rtable items too
  *			restore context if needed;
  *			return result;
  *		}
@@ -1994,16 +1994,17 @@ expression_tree_walker(Node *node,
  * walker intends to descend into subqueries.  It is also useful for
  * descending into subqueries within a walker.
  *
- * If visitQueryRTEs is true, the walker will also be called on sub-Query
- * nodes present in subquery rangetable entries of the given Query.  This
- * is optional since some callers handle those sub-queries separately,
- * or don't really want to see subqueries anyway.
+ * Some callers want to suppress visitation of certain items in the sub-Query,
+ * typically because they need to process them specially, or don't actually
+ * want to recurse into subqueries.  This is supported by the flags argument,
+ * which is the bitwise OR of flag values to suppress visitation of
+ * indicated items.  (More flag bits may be added as needed.)
  */
 bool
 query_tree_walker(Query *query,
 				  bool (*walker) (),
 				  void *context,
-				  bool visitQueryRTEs)
+				  int flags)
 {
 	List	   *rt;
 
@@ -2028,13 +2029,14 @@ query_tree_walker(Query *query,
 				/* nothing to do */
 				break;
 			case RTE_SUBQUERY:
-				if (visitQueryRTEs)
+				if (! (flags & QTW_IGNORE_SUBQUERIES))
 					if (walker(rte->subquery, context))
 						return true;
 				break;
 			case RTE_JOIN:
-				if (walker(rte->joinaliasvars, context))
-					return true;
+				if (! (flags & QTW_IGNORE_JOINALIASES))
+					if (walker(rte->joinaliasvars, context))
+						return true;
 				break;
 			case RTE_FUNCTION:
 				if (walker(rte->funcexpr, context))
@@ -2388,16 +2390,17 @@ expression_tree_mutator(Node *node,
  * if you don't want to change the original.  All substructure is safely
  * copied, however.
  *
- * If visitQueryRTEs is true, the mutator will also be called on sub-Query
- * nodes present in subquery rangetable entries of the given Query.  This
- * is optional since some callers handle those sub-queries separately,
- * or don't really want to see subqueries anyway.
+ * Some callers want to suppress mutating of certain items in the sub-Query,
+ * typically because they need to process them specially, or don't actually
+ * want to recurse into subqueries.  This is supported by the flags argument,
+ * which is the bitwise OR of flag values to suppress mutating of
+ * indicated items.  (More flag bits may be added as needed.)
  */
 void
 query_tree_mutator(Query *query,
 				   Node *(*mutator) (),
 				   void *context,
-				   bool visitQueryRTEs)
+				   int flags)
 {
 	List	   *newrt = NIL;
 	List	   *rt;
@@ -2420,7 +2423,7 @@ query_tree_mutator(Query *query,
 				/* nothing to do, don't bother to make a copy */
 				break;
 			case RTE_SUBQUERY:
-				if (visitQueryRTEs)
+				if (! (flags & QTW_IGNORE_SUBQUERIES))
 				{
 					FLATCOPY(newrte, rte, RangeTblEntry);
 					CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
@@ -2429,9 +2432,12 @@ query_tree_mutator(Query *query,
 				}
 				break;
 			case RTE_JOIN:
-				FLATCOPY(newrte, rte, RangeTblEntry);
-				MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
-				rte = newrte;
+				if (! (flags & QTW_IGNORE_JOINALIASES))
+				{
+					FLATCOPY(newrte, rte, RangeTblEntry);
+					MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
+					rte = newrte;
+				}
 				break;
 			case RTE_FUNCTION:
 				FLATCOPY(newrte, rte, RangeTblEntry);
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index ccccdbf1808bb845911e3e1e5464682ab9677f7c..23b824dbcb019d239ee32ba79c3b44198645220c 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.39 2002/09/04 20:31:22 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.40 2002/09/11 14:48:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,7 +82,7 @@ pull_varnos(Node *node)
 	 */
 	if (node && IsA(node, Query))
 		query_tree_walker((Query *) node, pull_varnos_walker,
-						  (void *) &context, true);
+						  (void *) &context, 0);
 	else
 		pull_varnos_walker(node, &context);
 
@@ -128,7 +128,7 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
 
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node, pull_varnos_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
@@ -165,7 +165,7 @@ contain_var_reference(Node *node, int varno, int varattno, int levelsup)
 	if (node && IsA(node, Query))
 		return query_tree_walker((Query *) node,
 								 contain_var_reference_walker,
-								 (void *) &context, true);
+								 (void *) &context, 0);
 	else
 		return contain_var_reference_walker(node, &context);
 }
@@ -212,7 +212,7 @@ contain_var_reference_walker(Node *node,
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node,
 								   contain_var_reference_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 2aa4fdd892ecba9ba99b541e44a9c94ac4dbfe86..1933482faab6c00a2c07be35c4d0d63f39b01bc8 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.79 2002/09/04 20:31:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.80 2002/09/11 14:48:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -464,9 +464,10 @@ setRuleCheckAsUser(Query *qry, Oid userid)
 	}
 
 	/* If there are sublinks, search for them and process their RTEs */
+	/* ignore subqueries in rtable because we already processed them */
 	if (qry->hasSubLinks)
 		query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid,
-						  false /* already did the ones in rtable */ );
+						  QTW_IGNORE_SUBQUERIES);
 }
 
 /*
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index d434e9e2fb8ed215bc58108fcd1e0529b08798d1..95fc726227f802bb7619bf81e11465b6c0f2f0a8 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
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.108 2002/09/04 20:31:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.109 2002/09/11 14:48:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -822,11 +822,12 @@ fireRIRrules(Query *parsetree)
 	}
 
 	/*
-	 * Recurse into sublink subqueries, too.
+	 * Recurse into sublink subqueries, too.  But we already did the ones
+	 * in the rtable.
 	 */
 	if (parsetree->hasSubLinks)
 		query_tree_walker(parsetree, fireRIRonSubLink, NULL,
-						false /* already handled the ones in rtable */ );
+						  QTW_IGNORE_SUBQUERIES);
 
 	/*
 	 * If the query was marked having aggregates, check if this is still
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 16eef1bfb8554cf3bd7df2da12e4e8404df7def8..12154b8da6e8c9c395252da8ea6c9bd8bad41517 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.65 2002/09/04 20:31:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.66 2002/09/11 14:48:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,7 +49,7 @@ checkExprHasAggs(Node *node)
 	 */
 	if (node && IsA(node, Query))
 		return query_tree_walker((Query *) node, checkExprHasAggs_walker,
-								 NULL, false);
+								 NULL, QTW_IGNORE_SUBQUERIES);
 	else
 		return checkExprHasAggs_walker(node, NULL);
 }
@@ -79,7 +79,7 @@ checkExprHasSubLink(Node *node)
 	 */
 	if (node && IsA(node, Query))
 		return query_tree_walker((Query *) node, checkExprHasSubLink_walker,
-								 NULL, false);
+								 NULL, QTW_IGNORE_SUBQUERIES);
 	else
 		return checkExprHasSubLink_walker(node, NULL);
 }
@@ -155,7 +155,7 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
 
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
@@ -196,7 +196,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
 				lfirsti(l) += offset;
 		}
 		query_tree_walker(qry, OffsetVarNodes_walker,
-						  (void *) &context, true);
+						  (void *) &context, 0);
 	}
 	else
 		OffsetVarNodes_walker(node, &context);
@@ -265,7 +265,7 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
@@ -310,7 +310,7 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
 			}
 		}
 		query_tree_walker(qry, ChangeVarNodes_walker,
-						  (void *) &context, true);
+						  (void *) &context, 0);
 	}
 	else
 		ChangeVarNodes_walker(node, &context);
@@ -361,7 +361,7 @@ IncrementVarSublevelsUp_walker(Node *node,
 		context->min_sublevels_up++;
 		result = query_tree_walker((Query *) node,
 								   IncrementVarSublevelsUp_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->min_sublevels_up--;
 		return result;
 	}
@@ -385,7 +385,7 @@ IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
 	 */
 	if (node && IsA(node, Query))
 		query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker,
-						  (void *) &context, true);
+						  (void *) &context, 0);
 	else
 		IncrementVarSublevelsUp_walker(node, &context);
 }
@@ -443,7 +443,7 @@ rangeTableEntry_used_walker(Node *node,
 
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
@@ -466,7 +466,7 @@ rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
 	 */
 	if (node && IsA(node, Query))
 		return query_tree_walker((Query *) node, rangeTableEntry_used_walker,
-								 (void *) &context, true);
+								 (void *) &context, 0);
 	else
 		return rangeTableEntry_used_walker(node, &context);
 }
@@ -508,7 +508,7 @@ attribute_used_walker(Node *node,
 
 		context->sublevels_up++;
 		result = query_tree_walker((Query *) node, attribute_used_walker,
-								   (void *) context, true);
+								   (void *) context, 0);
 		context->sublevels_up--;
 		return result;
 	}
@@ -532,7 +532,7 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
 	 */
 	if (node && IsA(node, Query))
 		return query_tree_walker((Query *) node, attribute_used_walker,
-								 (void *) &context, true);
+								 (void *) &context, 0);
 	else
 		return attribute_used_walker(node, &context);
 }
@@ -851,7 +851,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
 
 		FLATCOPY(newnode, query, Query);
 		context->sublevels_up++;
-		query_tree_mutator(newnode, ResolveNew_mutator, context, true);
+		query_tree_mutator(newnode, ResolveNew_mutator, context, 0);
 		context->sublevels_up--;
 		return (Node *) newnode;
 	}
@@ -883,7 +883,7 @@ ResolveNew(Node *node, int target_varno, int sublevels_up,
 
 		FLATCOPY(newnode, query, Query);
 		query_tree_mutator(newnode, ResolveNew_mutator,
-						   (void *) &context, true);
+						   (void *) &context, 0);
 		return (Node *) newnode;
 	}
 	else
@@ -991,7 +991,7 @@ HandleRIRAttributeRule_mutator(Node *node,
 		FLATCOPY(newnode, query, Query);
 		context->sublevels_up++;
 		query_tree_mutator(newnode, HandleRIRAttributeRule_mutator,
-						   context, true);
+						   context, 0);
 		context->sublevels_up--;
 		return (Node *) newnode;
 	}
@@ -1019,7 +1019,7 @@ HandleRIRAttributeRule(Query *parsetree,
 	context.sublevels_up = 0;
 
 	query_tree_mutator(parsetree, HandleRIRAttributeRule_mutator,
-					   (void *) &context, true);
+					   (void *) &context, 0);
 }
 
 #endif   /* NOT_USED */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 7c36586a34ca3ec7d35c0ba9308405645cb49102..844e7d949023bebad96230a32ffb13597d73b988 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.53 2002/06/20 20:29:51 momjian Exp $
+ * $Id: clauses.h,v 1.54 2002/09/11 14:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -66,10 +66,15 @@ extern bool expression_tree_walker(Node *node, bool (*walker) (),
 											   void *context);
 extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
 												 void *context);
+
+/* flags bits for query_tree_walker and query_tree_mutator */
+#define QTW_IGNORE_SUBQUERIES	0x01	/* subqueries in rtable */
+#define QTW_IGNORE_JOINALIASES	0x02	/* JOIN alias var lists */
+
 extern bool query_tree_walker(Query *query, bool (*walker) (),
-									 void *context, bool visitQueryRTEs);
+							  void *context, int flags);
 extern void query_tree_mutator(Query *query, Node *(*mutator) (),
-									 void *context, bool visitQueryRTEs);
+							   void *context, int flags);
 
 #define is_subplan(clause)	((clause) != NULL && \
 							 IsA(clause, Expr) && \