diff --git a/doc/src/sgml/arch-dev.sgml b/doc/src/sgml/arch-dev.sgml
index 7d867c45cae3685f59f1b6213a2c68a287d1363f..93f53c515551576f192ca8724968e5beb0f7c8c6 100644
--- a/doc/src/sgml/arch-dev.sgml
+++ b/doc/src/sgml/arch-dev.sgml
@@ -2475,7 +2475,7 @@ having clause} is found.
 +         if(node->plan.qual != NULL)
 +         {
 +           qual_result =
-+               ExecQual(fix_opids(node->plan.qual),
++               ExecQual(node->plan.qual,
 +                        econtext);
 +         }     
 +         if (oneTuple) pfree(oneTuple);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3644d1ba0426bc99b7ce0a02efaf22d148252442..31d3419bee671b5e2773ca7d6368dfcabd75d800 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.9 1999/07/17 20:16:52 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.10 1999/08/22 20:14:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
 #include "utils/builtins.h"
@@ -142,7 +143,7 @@ DefineIndex(char *heapRelationName,
 	if (predicate != NULL && rangetable != NIL)
 	{
 		cnfPred = cnfify((Expr *) copyObject(predicate), true);
-		fix_opids(cnfPred);
+		fix_opids((Node *) cnfPred);
 		CheckPredicate(cnfPred, rangetable, relationId);
 	}
 
@@ -285,7 +286,7 @@ ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
 	if (rangetable != NIL)
 	{
 		cnfPred = cnfify((Expr *) copyObject(predicate), true);
-		fix_opids(cnfPred);
+		fix_opids((Node *) cnfPred);
 		CheckPredicate(cnfPred, rangetable, relationId);
 	}
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b600c0e3a8f1cdbc2713d194ee298a63cf4f3ad2..2936694f67993a74935dd40b3220d2470bab3f95 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -23,7 +23,6 @@
 #include "executor/executor.h"
 #include "executor/nodeAgg.h"
 #include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
 #include "parser/parse_type.h"
 #include "utils/syscache.h"
 
@@ -443,7 +442,7 @@ ExecAgg(Agg *node)
 		 * qualifications it is ignored and the next group is fetched
 		 */
 		if (node->plan.qual != NULL)
-			qual_result = ExecQual(fix_opids(node->plan.qual), econtext);
+			qual_result = ExecQual(node->plan.qual, econtext);
 		else
 			qual_result = false;
 
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 26ebed1d458ac9b7b5cdcc17aff9273c0f460c51..ddfef9d5eb4508e7a16e553fb374e181e1047826 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.17 1999/08/21 03:48:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.18 1999/08/22 20:14:59 tgl Exp $
  *
  * NOTES
  *	  Creator functions in POSTGRES 4.2 are generated automatically. Most of
@@ -52,9 +52,7 @@ makeVar(Index varno,
 		AttrNumber varattno,
 		Oid vartype,
 		int32 vartypmod,
-		Index varlevelsup,
-		Index varnoold,
-		AttrNumber varoattno)
+		Index varlevelsup)
 {
 	Var		   *var = makeNode(Var);
 
@@ -63,8 +61,14 @@ makeVar(Index varno,
 	var->vartype = vartype;
 	var->vartypmod = vartypmod;
 	var->varlevelsup = varlevelsup;
-	var->varnoold = varnoold;
-	var->varoattno = varoattno;
+	/*
+	 * Since few if any routines ever create Var nodes with varnoold/varoattno
+	 * different from varno/varattno, we don't provide separate arguments
+	 * for them, but just initialize them to the given varno/varattno.
+	 * This reduces code clutter and chance of error for most callers.
+	 */
+	var->varnoold = varno;
+	var->varoattno = varattno;
 
 	return var;
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 55787062dc513f033ccf3c7d8157ad70230df461..fcf462b83eb08a268e2080f1533f58f514ad5d2f 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -18,7 +18,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.44 1999/08/06 04:00:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.45 1999/08/22 20:14:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -431,7 +431,7 @@ compute_rel_size(RelOptInfo *rel)
 int
 compute_rel_width(RelOptInfo *rel)
 {
-	return compute_targetlist_width(get_actual_tlist(rel->targetlist));
+	return compute_targetlist_width(rel->targetlist);
 }
 
 /*
@@ -448,8 +448,7 @@ compute_targetlist_width(List *targetlist)
 
 	foreach(temp_tl, targetlist)
 	{
-		tuple_width = tuple_width +
-			compute_attribute_width(lfirst(temp_tl));
+		tuple_width += compute_attribute_width(lfirst(temp_tl));
 	}
 	return tuple_width;
 }
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 41a3ff35b4813a6112d08762966ae9fe2000ee54..b9a982e828325f4d8b6c6c43d5af442696267c1d 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.15 1999/08/21 03:49:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.16 1999/08/22 20:14:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,8 +24,6 @@
 #include "utils/lsyscache.h"
 
 static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
-static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b);
-static bool pathkeyitem_member(PathKeyItem *a, List *l);
 static Var *find_indexkey_var(int indexkey, List *tlist);
 static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
 								List *joinclauses);
@@ -118,45 +116,6 @@ makePathKeyItem(Node *key, Oid sortop)
  *		PATHKEY COMPARISONS
  ****************************************************************************/
 
-/*
- * Compare two pathkey items for equality.
- *
- * This is unlike straight equal() because when the two keys are both Vars,
- * we want to apply the weaker var_equal() condition (doesn't check varnoold
- * or varoattno).  But if that fails, try equal() so that we recognize
- * functional-index keys.
- */
-static bool
-pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b)
-{
-	Assert(a && IsA(a, PathKeyItem));
-	Assert(b && IsA(b, PathKeyItem));
-
-	if (a->sortop != b->sortop)
-		return false;
-	if (var_equal((Var *) a->key, (Var *) b->key))
-		return true;
-	return equal(a->key, b->key);
-}
-
-/*
- * member() test using pathkeyitem_equal
- */
-static bool
-pathkeyitem_member (PathKeyItem *a, List *l)
-{
-	List	   *i;
-
-	Assert(a && IsA(a, PathKeyItem));
-
-	foreach(i, l)
-	{
-		if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i)))
-			return true;
-	}
-	return false;
-}
-
 /*
  * compare_pathkeys
  *	  Compare two pathkeys to see if they are equivalent, and if not whether
@@ -191,7 +150,7 @@ compare_pathkeys(List *keys1, List *keys2)
 		{
 			foreach(i, subkey1)
 			{
-				if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2))
+				if (! member(lfirst(i), subkey2))
 				{
 					key1_subsetof_key2 = false;
 					break;
@@ -203,7 +162,7 @@ compare_pathkeys(List *keys1, List *keys2)
 		{
 			foreach(i, subkey2)
 			{
-				if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1))
+				if (! member(lfirst(i), subkey1))
 				{
 					key2_subsetof_key1 = false;
 					break;
@@ -336,8 +295,8 @@ build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
 			int32		type_mod = get_atttypmod(reloid, varattno);
 
 			funcargs = lappend(funcargs,
-							   makeVar(relid, varattno, vartypeid, type_mod,
-									   0, relid, varattno));
+							   makeVar(relid, varattno, vartypeid,
+									   type_mod, 0));
 			indexkeys++;
 		}
 
@@ -483,13 +442,13 @@ build_join_pathkey(List *pathkey,
 	foreach(i, pathkey)
 	{
 		PathKeyItem *key = (PathKeyItem *) lfirst(i);
-		Expr	   *tlist_key;
+		Node	   *tlist_key;
 
 		Assert(key && IsA(key, PathKeyItem));
 
-		tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist);
+		tlist_key = matching_tlist_expr(key->key, join_rel_tlist);
 		if (tlist_key)
-			new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+			new_pathkey = lcons(makePathKeyItem(tlist_key,
 												key->sortop),
 								new_pathkey);
 
@@ -498,17 +457,17 @@ build_join_pathkey(List *pathkey,
 			RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
 			Expr	   *joinclause = restrictinfo->clause;
 			/* We assume the clause is a binary opclause... */
-			Var		   *l = get_leftop(joinclause);
-			Var		   *r = get_rightop(joinclause);
-			Var		   *other_var = NULL;
+			Node	   *l = (Node *) get_leftop(joinclause);
+			Node	   *r = (Node *) get_rightop(joinclause);
+			Node	   *other_var = NULL;
 			Oid			other_sortop = InvalidOid;
 
-			if (var_equal((Var *) key->key, l))
+			if (equal(key->key, l))
 			{
 				other_var = r;
 				other_sortop = restrictinfo->right_sortop;
 			}
-			else if (var_equal((Var *) key->key, r))
+			else if (equal(key->key, r))
 			{
 				other_var = l;
 				other_sortop = restrictinfo->left_sortop;
@@ -516,9 +475,9 @@ build_join_pathkey(List *pathkey,
 
 			if (other_var && other_sortop)
 			{
-				tlist_key = matching_tlist_var(other_var, join_rel_tlist);
+				tlist_key = matching_tlist_expr(other_var, join_rel_tlist);
 				if (tlist_key)
-					new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
+					new_pathkey = lcons(makePathKeyItem(tlist_key,
 														other_sortop),
 										new_pathkey);
 			}
@@ -638,20 +597,17 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
 		foreach(j, pathkey)
 		{
 			PathKeyItem	   *keyitem = lfirst(j);
-			Var			   *keyvar = (Var *) keyitem->key;
+			Node		   *key = keyitem->key;
 			List		   *k;
 
-			if (! IsA(keyvar, Var))
-				continue;		/* for now, only Vars can be mergejoined */
-
 			foreach(k, restrictinfos)
 			{
 				RestrictInfo   *restrictinfo = lfirst(k);
 
 				Assert(restrictinfo->mergejoinoperator != InvalidOid);
 
-				if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) ||
-					 var_equal(keyvar, get_rightop(restrictinfo->clause))) &&
+				if ((equal(key, get_leftop(restrictinfo->clause)) ||
+					 equal(key, get_rightop(restrictinfo->clause))) &&
 					! member(restrictinfo, mergeclauses))
 				{
 					matched_restrictinfo = restrictinfo;
@@ -705,23 +661,24 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
 	foreach(i, mergeclauses)
 	{
 		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
-		Var		   *key;
+		Node	   *key;
 		Oid			sortop;
 
+		Assert(restrictinfo->mergejoinoperator != InvalidOid);
+
 		/*
 		 * Find the key and sortop needed for this mergeclause.
 		 *
 		 * We can use either side of the mergeclause, since we haven't yet
 		 * committed to which side will be inner.
 		 */
-		Assert(restrictinfo->mergejoinoperator != InvalidOid);
-		key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause),
-										 tlist);
+		key = matching_tlist_expr((Node *) get_leftop(restrictinfo->clause),
+								  tlist);
 		sortop = restrictinfo->left_sortop;
 		if (! key)
 		{
-			key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause),
-											 tlist);
+			key = matching_tlist_expr((Node *) get_rightop(restrictinfo->clause),
+									  tlist);
 			sortop = restrictinfo->right_sortop;
 		}
 		if (! key)
@@ -730,7 +687,7 @@ make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
 		 * Add a pathkey sublist for this sort item
 		 */
 		pathkeys = lappend(pathkeys,
-						   lcons(makePathKeyItem((Node *) key, sortop),
+						   lcons(makePathKeyItem(key, sortop),
 								 NIL));
 	}
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d1f756fc7c19fc44be0f1a15dd3989fab8c485b8..ae1e2d3266b24700ff8048bcfdd5a5331896d4a5 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1,13 +1,15 @@
 /*-------------------------------------------------------------------------
  *
  * createplan.c
- *	  Routines to create the desired plan for processing a query
+ *	  Routines to create the desired plan for processing a query.
+ *	  Planning is complete, we just need to convert the selected
+ *	  Path into a Plan.
  *
  * Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.75 1999/08/22 20:14:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,13 +72,13 @@ static void copy_costsize(Plan *dest, Plan *src);
  *	  every pathnode found:
  *	  (1) Create a corresponding plan node containing appropriate id,
  *		  target list, and qualification information.
- *	  (2) Modify ALL clauses so that attributes are referenced using
- *		  relative values.
- *	  (3) Target lists are not modified, but will be in another routine.
+ *	  (2) Modify qual clauses of join nodes so that subplan attributes are
+ *		  referenced using relative values.
+ *	  (3) Target lists are not modified, but will be in setrefs.c.
  *
  *	  best_path is the best access path
  *
- *	  Returns the optimal(?) access plan.
+ *	  Returns the access plan.
  */
 Plan *
 create_plan(Path *best_path)
@@ -90,7 +92,7 @@ create_plan(Path *best_path)
 	int			tuples;
 
 	parent_rel = best_path->parent;
-	tlist = get_actual_tlist(parent_rel->targetlist);
+	tlist = parent_rel->targetlist;
 	size = parent_rel->size;
 	width = parent_rel->width;
 	pages = parent_rel->pages;
@@ -152,9 +154,8 @@ create_scan_node(Path *best_path, List *tlist)
 	/*
 	 * Extract the relevant restriction clauses from the parent relation;
 	 * the executor must apply all these restrictions during the scan.
-	 * Fix regproc ids in the restriction clauses.
 	 */
-	scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo));
+	scan_clauses = get_actual_clauses(best_path->parent->restrictinfo);
 
 	switch (best_path->pathtype)
 	{
@@ -235,7 +236,6 @@ create_join_node(JoinPath *best_path, List *tlist)
 												   inner_tlist);
 			break;
 		default:
-			/* do nothing */
 			elog(ERROR, "create_join_node: unknown node type",
 				 best_path->path.pathtype);
 	}
@@ -249,8 +249,7 @@ create_join_node(JoinPath *best_path, List *tlist)
 	if (get_loc_restrictinfo(best_path) != NIL)
 		set_qpqual((Plan) retval,
 				   nconc(get_qpqual((Plan) retval),
-						 fix_opids(get_actual_clauses
-								   (get_loc_restrictinfo(best_path)))));
+						 get_actual_clauses(get_loc_restrictinfo(best_path))));
 #endif
 
 	return retval;
@@ -282,8 +281,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
 
 	scan_node = make_seqscan(tlist,
 							 scan_clauses,
-							 scan_relid,
-							 (Plan *) NULL);
+							 scan_relid);
 
 	scan_node->plan.cost = best_path->path_cost;
 
@@ -386,17 +384,10 @@ create_indexscan_node(IndexPath *best_path,
 		qpqual = NIL;
 
 	/* The executor needs a copy with the indexkey on the left of each clause
-	 * and with index attrs substituted for table ones.
+	 * and with index attr numbers substituted for table ones.
 	 */
 	fixed_indxqual = fix_indxqual_references(indxqual, best_path);
 
-	/*
-	 * Fix opids in the completed indxquals.
-	 * XXX this ought to only happen at final exit from the planner...
-	 */
-	indxqual = fix_opids(indxqual);
-	fixed_indxqual = fix_opids(fixed_indxqual);
-
 	scan_node = make_indexscan(tlist,
 							   qpqual,
 							   lfirsti(best_path->path.parent->relids),
@@ -413,6 +404,21 @@ create_indexscan_node(IndexPath *best_path,
  *
  *	JOIN METHODS
  *
+ * A general note about join_references() processing in these routines:
+ * once we have changed a Var node to refer to a subplan output rather than
+ * the original relation, it is no longer equal() to an unmodified Var node
+ * for the same var.  So, we cannot easily compare reference-adjusted qual
+ * clauses to clauses that have not been adjusted.  Fortunately, that
+ * doesn't seem to be necessary; all the decisions are made before we do
+ * the reference adjustments.
+ *
+ * A cleaner solution would be to not call join_references() here at all,
+ * but leave it for setrefs.c to do at the end of plan tree construction.
+ * But that would make switch_outer() much more complicated, and some care
+ * would be needed to get setrefs.c to do the right thing with nestloop
+ * inner indexscan quals.  So, we do subplan reference adjustment here for
+ * quals of join nodes (and *only* for quals of join nodes).
+ *
  *****************************************************************************/
 
 static NestLoop *
@@ -432,7 +438,7 @@ create_nestloop_node(NestPath *best_path,
 		 * An index is being used to reduce the number of tuples scanned
 		 * in the inner relation.  If there are join clauses being used
 		 * with the index, we must update their outer-rel var nodes to
-		 * refer to the outer relation.
+		 * refer to the outer side of the join.
 		 *
 		 * We can also remove those join clauses from the list of clauses
 		 * that have to be checked as qpquals at the join node, but only
@@ -442,7 +448,12 @@ create_nestloop_node(NestPath *best_path,
 		 * Note: if the index is lossy, the same clauses may also be getting
 		 * checked as qpquals in the indexscan.  We can still remove them
 		 * from the nestloop's qpquals, but we gotta update the outer-rel
-		 * vars in the indexscan's qpquals too...
+		 * vars in the indexscan's qpquals too.
+		 *
+		 * Note: we can safely do set_difference() against my clauses and
+		 * join_references() because the innerscan is a primitive plan,
+		 * and therefore has not itself done join_references renumbering
+		 * of the vars in its quals.
 		 */
 		IndexScan  *innerscan = (IndexScan *) inner_node;
 		List	   *indxqualorig = innerscan->indxqualorig;
@@ -450,6 +461,8 @@ create_nestloop_node(NestPath *best_path,
 		/* No work needed if indxqual refers only to its own relation... */
 		if (NumRelids((Node *) indxqualorig) > 1)
 		{
+			Index		innerrel = innerscan->scan.scanrelid;
+
 			/* Remove redundant tests from my clauses, if possible.
 			 * Note we must compare against indxqualorig not the "fixed"
 			 * indxqual (which has index attnos instead of relation attnos,
@@ -461,20 +474,28 @@ create_nestloop_node(NestPath *best_path,
 			/* only refs to outer vars get changed in the inner indexqual */
 			innerscan->indxqualorig = join_references(indxqualorig,
 													  outer_tlist,
-													  NIL);
+													  NIL,
+													  innerrel);
 			innerscan->indxqual = join_references(innerscan->indxqual,
 												  outer_tlist,
-												  NIL);
+												  NIL,
+												  innerrel);
 			/* fix the inner qpqual too, if it has join clauses */
 			if (NumRelids((Node *) inner_node->qual) > 1)
 				inner_node->qual = join_references(inner_node->qual,
 												   outer_tlist,
-												   NIL);
+												   NIL,
+												   innerrel);
 		}
 	}
 	else if (IsA_Join(inner_node))
 	{
-		/* Materialize the inner join for speed reasons */
+		/*
+		 * Materialize the inner join for speed reasons.
+		 *
+		 * XXX It is probably *not* always fastest to materialize an inner
+		 * join --- how can we estimate whether this is a good thing to do?
+		 */
 		inner_node = (Plan *) make_noname(inner_tlist,
 										  NIL,
 										  inner_node);
@@ -483,7 +504,8 @@ create_nestloop_node(NestPath *best_path,
 	join_node = make_nestloop(tlist,
 							  join_references(clauses,
 											  outer_tlist,
-											  inner_tlist),
+											  inner_tlist,
+											  (Index) 0),
 							  outer_node,
 							  inner_node);
 
@@ -513,7 +535,8 @@ create_mergejoin_node(MergePath *best_path,
 	qpqual = join_references(set_difference(clauses,
 											best_path->path_mergeclauses),
 							 outer_tlist,
-							 inner_tlist);
+							 inner_tlist,
+							 (Index) 0);
 
 	/*
 	 * Now set the references in the mergeclauses and rearrange them so
@@ -521,7 +544,8 @@ create_mergejoin_node(MergePath *best_path,
 	 */
 	mergeclauses = switch_outer(join_references(best_path->path_mergeclauses,
 												outer_tlist,
-												inner_tlist));
+												inner_tlist,
+												(Index) 0));
 
 	/*
 	 * Create explicit sort nodes for the outer and inner join paths if
@@ -578,7 +602,8 @@ create_hashjoin_node(HashPath *best_path,
 	qpqual = join_references(set_difference(clauses,
 											best_path->path_hashclauses),
 							 outer_tlist,
-							 inner_tlist);
+							 inner_tlist,
+							 (Index) 0);
 
 	/*
 	 * Now set the references in the hashclauses and rearrange them so
@@ -586,7 +611,8 @@ create_hashjoin_node(HashPath *best_path,
 	 */
 	hashclauses = switch_outer(join_references(best_path->path_hashclauses,
 											   outer_tlist,
-											   inner_tlist));
+											   inner_tlist,
+											   (Index) 0));
 
 	/* Now the righthand op of the sole hashclause is the inner hash key. */
 	innerhashkey = get_rightop(lfirst(hashclauses));
@@ -839,7 +865,7 @@ set_tlist_sort_info(List *tlist, List *pathkeys)
 		{
 			pathkey = lfirst(j);
 			Assert(IsA(pathkey, PathKeyItem));
-			resdom = tlist_member((Var *) pathkey->key, tlist);
+			resdom = tlist_member(pathkey->key, tlist);
 			if (resdom)
 				break;
 		}
@@ -939,17 +965,16 @@ make_noname(List *tlist,
 SeqScan    *
 make_seqscan(List *qptlist,
 			 List *qpqual,
-			 Index scanrelid,
-			 Plan *lefttree)
+			 Index scanrelid)
 {
 	SeqScan    *node = makeNode(SeqScan);
 	Plan	   *plan = &node->plan;
 
-	copy_costsize(plan, lefttree);
+	copy_costsize(plan, NULL);
 	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
-	plan->lefttree = lefttree;
+	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scanrelid = scanrelid;
 	node->scanstate = (CommonScanState *) NULL;
@@ -1158,9 +1183,7 @@ make_group(List *tlist,
 }
 
 /*
- *	A unique node always has a SORT node in the lefttree.
- *
- *	the uniqueAttr argument must be a null-terminated string,
+ * The uniqueAttr argument must be a null-terminated string,
  * either the name of the attribute to select unique on
  * or "*"
  */
@@ -1186,6 +1209,29 @@ make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
 	return node;
 }
 
+Result *
+make_result(List *tlist,
+			Node *resconstantqual,
+			Plan *subplan)
+{
+	Result	   *node = makeNode(Result);
+	Plan	   *plan = &node->plan;
+
+#ifdef NOT_USED
+	tlist = generate_fjoin(tlist);
+#endif
+	copy_costsize(plan, subplan);
+	plan->state = (EState *) NULL;
+	plan->targetlist = tlist;
+	plan->qual = NIL;
+	plan->lefttree = subplan;
+	plan->righttree = NULL;
+	node->resconstantqual = resconstantqual;
+	node->resstate = NULL;
+
+	return node;
+}
+
 #ifdef NOT_USED
 List *
 generate_fjoin(List *tlist)
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index db97c732070e621e1c65a981afe40c4fb1eaea4f..a89c40b943598bde2181c7637a0f98782cb51806 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.37 1999/08/16 02:17:54 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.38 1999/08/22 20:14:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,8 +104,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
 			/* add it to base_rel_list */
 			rel = get_base_rel(root, varno);
 			/* give it a dummy tlist entry for its OID */
-			var = makeVar(varno, ObjectIdAttributeNumber,
-						  OIDOID, -1, 0, varno, ObjectIdAttributeNumber);
+			var = makeVar(varno, ObjectIdAttributeNumber, OIDOID, -1, 0);
 			add_var_to_tlist(rel, var);
 		}
 		pfree(relids);
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index f6f62abfe0841e8d6b05f7bf3e818cc7669e6dc6..802e5970416558d727334fff1e3408fd34a2ceb3 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.42 1999/08/22 20:14:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,6 @@
 
 
 static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
-static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 
 /*
  * query_planner
@@ -84,11 +83,6 @@ query_planner(Query *root,
 	 * topmost result node.
 	 */
 	qual = pull_constant_clauses(qual, &constant_qual);
-	/*
-	 * The opids for the variable qualifications will be fixed later, but
-	 * someone seems to think that the constant quals need to be fixed here.
-	 */
-	fix_opids(constant_qual);
 
 	/*
 	 * Create a target list that consists solely of (resdom var) target
@@ -124,8 +118,7 @@ query_planner(Query *root,
 				{
 					SeqScan    *scan = make_seqscan(tlist,
 													NIL,
-													root->resultRelation,
-													(Plan *) NULL);
+													root->resultRelation);
 
 					if (constant_qual != NULL)
 						return ((Plan *) make_result(tlist,
@@ -141,14 +134,10 @@ query_planner(Query *root,
 	}
 
 	/*
-	 * Find the subplan (access path) and destructively modify the target
-	 * list of the newly created subplan to contain the appropriate join
-	 * references.
+	 * Choose the best access path and build a plan for it.
 	 */
 	subplan = subplanner(root, level_tlist, qual);
 
-	set_tlist_references(subplan);
-
 	/*
 	 * Build a result node linking the plan if we have constant quals
 	 */
@@ -158,33 +147,24 @@ query_planner(Query *root,
 									   (Node *) constant_qual,
 									   subplan);
 
-		/*
-		 * Fix all varno's of the Result's node target list.
-		 */
-		set_tlist_references(subplan);
-
 		root->query_pathkeys = NIL; /* result is unordered, no? */
 
 		return subplan;
 	}
 
 	/*
-	 * fix up the flattened target list of the plan root node so that
-	 * expressions are evaluated.  this forces expression evaluations that
-	 * may involve expensive function calls to be delayed to the very last
-	 * stage of query execution.  this could be bad. but it is joey's
-	 * responsibility to optimally push these expressions down the plan
-	 * tree.  -- Wei
+	 * Replace the toplevel plan node's flattened target list with the
+	 * targetlist given by my caller, so that expressions are evaluated.
 	 *
-	 * Note: formerly there was a test here to skip the unflatten call if
-	 * we expected union_planner to insert a Group or Agg node above our
-	 * result. However, now union_planner tells us exactly what it wants
-	 * returned, and we just do it.  Much cleaner.
+	 * This implies that all expression evaluations are done at the root
+	 * of the plan tree.  Once upon a time there was code to try to push
+	 * expensive function calls down to lower plan nodes, but that's dead
+	 * code and has been for a long time...
 	 */
 	else
 	{
-		subplan->targetlist = unflatten_tlist(tlist,
-											  subplan->targetlist);
+		subplan->targetlist = tlist;
+
 		return subplan;
 	}
 
@@ -330,36 +310,11 @@ subplanner(Query *root,
 
 	/* Nothing for it but to sort the cheapestpath...
 	 *
-	 * we indicate we failed to sort the plan, and let the caller
-	 * stick the appropriate sortplan on top.
+	 * We indicate we failed to sort the plan, and let the caller
+	 * stick the appropriate sort node on top.  union_planner has to be
+	 * able to add a sort node anyway, so no need for extra code here.
 	 */
 	root->query_pathkeys = NIL; /* sorry, it ain't sorted */
 
 	return create_plan(final_rel->cheapestpath);
 }
-
-/*****************************************************************************
- *
- *****************************************************************************/
-
-static Result *
-make_result(List *tlist,
-			Node *resconstantqual,
-			Plan *subplan)
-{
-	Result	   *node = makeNode(Result);
-	Plan	   *plan = &node->plan;
-
-#ifdef NOT_USED
-	tlist = generate_fjoin(tlist);
-#endif
-	plan->cost = (subplan ? subplan->cost : 0);
-	plan->state = (EState *) NULL;
-	plan->targetlist = tlist;
-	plan->lefttree = subplan;
-	plan->righttree = NULL;
-	node->resconstantqual = resconstantqual;
-	node->resstate = NULL;
-
-	return node;
-}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b328c40f226714a1ed601a004981c302fb5f5a9f..0003262a454e1688befe7733e05363d37d65aa99 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.64 1999/08/22 20:14:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +70,8 @@ planner(Query *parse)
 	}
 	result_plan->nParamExec = length(PlannerParamVar);
 
+	set_plan_references(result_plan);
+
 	return result_plan;
 }
 
@@ -173,8 +175,7 @@ union_planner(Query *parse)
 									0,
 									true);
 
-				var = makeVar(rowmark->rti, -1, TIDOID,
-							  -1, 0, rowmark->rti, -1);
+				var = makeVar(rowmark->rti, -1, TIDOID, -1, 0);
 
 				ctid = makeTargetEntry(resdom, (Node *) var);
 				tlist = lappend(tlist, ctid);
@@ -279,6 +280,8 @@ union_planner(Query *parse)
 	 */
 	if (parse->havingQual)
 	{
+		List	   *ql;
+
 		/* convert the havingQual to conjunctive normal form (cnf) */
 		parse->havingQual = (Node *) cnfify((Expr *) parse->havingQual, true);
 
@@ -295,13 +298,21 @@ union_planner(Query *parse)
 			 * Check for ungrouped variables passed to subplans. (Probably
 			 * this should be done for the targetlist as well???)
 			 */
-			check_having_for_ungrouped_vars(parse->havingQual,
-											parse->groupClause,
-											parse->targetList);
+			if (check_subplans_for_ungrouped_vars(parse->havingQual,
+												  parse->groupClause,
+												  parse->targetList))
+				elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
 		}
 
-		/* Calculate the opfids from the opnos */
-		parse->havingQual = (Node *) fix_opids((List *) parse->havingQual);
+		/*
+		 * Require an aggregate function to appear in each clause of the
+		 * havingQual (else it could have been done as a WHERE constraint).
+		 */
+		foreach(ql, (List *) parse->havingQual)
+		{
+			if (pull_agg_clause(lfirst(ql)) == NIL)
+				elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+		}
 	}
 
 	/*
@@ -314,13 +325,6 @@ union_planner(Query *parse)
 		/* HAVING clause, if any, becomes qual of the Agg node */
 		result_plan->qual = (List *) parse->havingQual;
 
-		/*
-		 * Update vars to refer to subplan result tuples, and
-		 * make sure there is an Aggref in every HAVING clause.
-		 */
-		if (!set_agg_tlist_references((Agg *) result_plan))
-			elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-
 		/*
 		 * Assume result is not ordered suitably for ORDER BY.
 		 * XXX it might be; improve this!
@@ -474,7 +478,6 @@ make_groupplan(List *group_tlist,
 			   Plan *subplan)
 {
 	int			numCols = length(groupClause);
-	Group	   *grpplan;
 
 	if (! is_sorted)
 	{
@@ -515,21 +518,8 @@ make_groupplan(List *group_tlist,
 									 keyno);
 	}
 
-	/*
-	 * Fix variables in tlist (should be done somewhere else?)
-	 */
-	group_tlist = copyObject(group_tlist);	/* necessary?? */
-	replace_tlist_with_subplan_refs(group_tlist,
-									(Index) 0,
-									subplan->targetlist);
-
-	/*
-	 * Make the Group node
-	 */
-	grpplan = make_group(group_tlist, tuplePerGroup, numCols,
-						 grpColIdx, subplan);
-
-	return (Plan *) grpplan;
+	return (Plan *) make_group(group_tlist, tuplePerGroup, numCols,
+							   grpColIdx, subplan);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 1492df8b03015b4ca8615a4a76ec2aa5ce7a4bcf..a983aa32143d41e1b1b5c55e28ee69e430a630c2 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1,13 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * setrefs.c
- *	  Routines to change varno/attno entries to contain references
+ *	  Post-processing of a completed plan tree: fix references to subplan
+ *	  vars, and compute regproc values for operators
  *
  * Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.57 1999/08/22 20:14:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,36 +26,24 @@
 typedef struct {
 	List	   *outer_tlist;
 	List	   *inner_tlist;
-} replace_joinvar_refs_context;
+	Index		acceptable_rel;
+} join_references_context;
 
 typedef struct {
 	Index		subvarno;
 	List	   *subplanTargetList;
 } replace_vars_with_subplan_refs_context;
 
-typedef struct {
-	List	   *groupClause;
-	List	   *targetList;
-} check_having_for_ungrouped_vars_context;
-
-static void set_join_tlist_references(Join *join);
-static void set_nonamescan_tlist_references(SeqScan *nonamescan);
-static void set_noname_tlist_references(Noname *noname);
-static Node *replace_joinvar_refs(Node *clause,
-								  List *outer_tlist,
-								  List *inner_tlist);
-static Node *replace_joinvar_refs_mutator(Node *node,
-					replace_joinvar_refs_context *context);
-static List *tlist_noname_references(Oid nonameid, List *tlist);
-static void set_result_tlist_references(Result *resultNode);
-static void replace_vars_with_subplan_refs(Node *clause,
-										   Index subvarno,
-										   List *subplanTargetList);
-static bool replace_vars_with_subplan_refs_walker(Node *node,
+static void set_join_references(Join *join);
+static void set_uppernode_references(Plan *plan, Index subvarno);
+static Node *join_references_mutator(Node *node,
+									 join_references_context *context);
+static Node *replace_vars_with_subplan_refs(Node *node,
+											Index subvarno,
+											List *subplanTargetList);
+static Node *replace_vars_with_subplan_refs_mutator(Node *node,
 					replace_vars_with_subplan_refs_context *context);
-static bool pull_agg_clause_walker(Node *node, List **listptr);
-static bool check_having_for_ungrouped_vars_walker(Node *node,
-					check_having_for_ungrouped_vars_context *context);
+static bool fix_opids_walker(Node *node, void *context);
 
 /*****************************************************************************
  *
@@ -63,340 +52,298 @@ static bool check_having_for_ungrouped_vars_walker(Node *node,
  *****************************************************************************/
 
 /*
- * set_tlist_references
- *	  Modifies the target list of nodes in a plan to reference target lists
- *	  at lower levels.
+ * set_plan_references
+ *	  This is the final processing pass of the planner/optimizer.  The plan
+ *	  tree is complete; we just have to adjust some representational details
+ *	  for the convenience of the executor.  We update Vars in upper plan nodes
+ *	  to refer to the outputs of their subplans, and we compute regproc OIDs
+ *	  for operators (ie, we look up the function that implements each op).
  *
- * 'plan' is the plan whose target list and children's target lists will
- *		be modified
+ *	  set_plan_references recursively traverses the whole plan tree.
  *
  * Returns nothing of interest, but modifies internal fields of nodes.
- *
  */
 void
-set_tlist_references(Plan *plan)
+set_plan_references(Plan *plan)
 {
+	List	   *pl;
+
 	if (plan == NULL)
 		return;
 
-	if (IsA_Join(plan))
-		set_join_tlist_references((Join *) plan);
-	else if (IsA(plan, SeqScan) && plan->lefttree &&
-			 IsA_Noname(plan->lefttree))
-		set_nonamescan_tlist_references((SeqScan *) plan);
-	else if (IsA_Noname(plan))
-		set_noname_tlist_references((Noname *) plan);
-	else if (IsA(plan, Result))
-		set_result_tlist_references((Result *) plan);
-	else if (IsA(plan, Hash))
-		set_tlist_references(plan->lefttree);
-}
+	/*
+	 * Plan-type-specific fixes
+	 */
+	switch (nodeTag(plan))
+	{
+		case T_SeqScan:
+			/* nothing special */
+			break;
+		case T_IndexScan:
+			fix_opids((Node *) ((IndexScan *) plan)->indxqual);
+			fix_opids((Node *) ((IndexScan *) plan)->indxqualorig);
+			break;
+		case T_NestLoop:
+			set_join_references((Join *) plan);
+			break;
+		case T_MergeJoin:
+			set_join_references((Join *) plan);
+			fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses);
+			break;
+		case T_HashJoin:
+			set_join_references((Join *) plan);
+			fix_opids((Node *) ((HashJoin *) plan)->hashclauses);
+			break;
+		case T_Material:
+		case T_Sort:
+		case T_Unique:
+		case T_Hash:
+			/* These plan types don't actually bother to evaluate their
+			 * targetlists or quals (because they just return their
+			 * unmodified input tuples).  The optimizer is lazy about
+			 * creating really valid targetlists for them.  Best to
+			 * just leave the targetlist alone.
+			 */
+			break;
+		case T_Agg:
+		case T_Group:
+			set_uppernode_references(plan, (Index) 0);
+			break;
+		case T_Result:
+			/* XXX why does Result use a different subvarno? */
+			set_uppernode_references(plan, (Index) OUTER);
+			fix_opids(((Result *) plan)->resconstantqual);
+			break;
+		case T_Append:
+			foreach(pl, ((Append *) plan)->appendplans)
+			{
+				set_plan_references((Plan *) lfirst(pl));
+			}
+			break;
+		default:
+			elog(ERROR, "set_plan_references: unknown plan type %d",
+				 nodeTag(plan));
+			break;
+	}
 
-/*
- * set_join_tlist_references
- *	  Modifies the target list of a join node by setting the varnos and
- *	  varattnos to reference the target list of the outer and inner join
- *	  relations.
- *
- *	  Creates a target list for a join node to contain references by setting
- *	  varno values to OUTER or INNER and setting attno values to the
- *	  result domain number of either the corresponding outer or inner join
- *	  tuple.
- *
- * 'join' is a join plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
- *
- */
-static void
-set_join_tlist_references(Join *join)
-{
-	Plan	   *outer = join->lefttree;
-	Plan	   *inner = join->righttree;
-	List	   *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
-	List	   *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
-	List	   *new_join_targetlist = NIL;
-	List	   *qptlist = join->targetlist;
-	List	   *entry;
+	/*
+	 * For all plan types, fix operators in targetlist and qual expressions
+	 */
+	fix_opids((Node *) plan->targetlist);
+	fix_opids((Node *) plan->qual);
 
-	foreach(entry, qptlist)
+	/*
+	 * Now recurse into subplans, if any
+	 *
+	 * NOTE: it is essential that we recurse into subplans AFTER we set
+	 * subplan references in this plan's tlist and quals.  If we did the
+	 * reference-adjustments bottom-up, then we would fail to match this
+	 * plan's var nodes against the already-modified nodes of the subplans.
+	 */
+	set_plan_references(plan->lefttree);
+	set_plan_references(plan->righttree);
+	foreach(pl, plan->initPlan)
 	{
-		TargetEntry *xtl = (TargetEntry *) lfirst(entry);
-		Node	   *joinexpr = replace_joinvar_refs(xtl->expr,
-													outer_tlist,
-													inner_tlist);
+		SubPlan	   *sp = (SubPlan *) lfirst(pl);
 
-		new_join_targetlist = lappend(new_join_targetlist,
-									  makeTargetEntry(xtl->resdom, joinexpr));
+		Assert(IsA(sp, SubPlan));
+		set_plan_references(sp->plan);
 	}
-	join->targetlist = new_join_targetlist;
+	foreach(pl, plan->subPlan)
+	{
+		SubPlan	   *sp = (SubPlan *) lfirst(pl);
 
-	set_tlist_references(outer);
-	set_tlist_references(inner);
+		Assert(IsA(sp, SubPlan));
+		set_plan_references(sp->plan);
+	}
 }
 
 /*
- * set_nonamescan_tlist_references
- *	  Modifies the target list of a node that scans a noname relation (i.e., a
- *	  sort or materialize node) so that the varnos refer to the child noname.
+ * set_join_references
+ *	  Modifies the target list of a join node to reference its subplans,
+ *	  by setting the varnos to OUTER or INNER and setting attno values to the
+ *	  result domain number of either the corresponding outer or inner join
+ *	  tuple item.
  *
- * 'nonamescan' is a seqscan node
+ * Note: this same transformation has already been applied to the quals
+ * of the join by createplan.c.  It's a little odd to do it here for the
+ * targetlist and there for the quals, but it's easier that way.  (Look
+ * at switch_outer() and the handling of nestloop inner indexscans to
+ * see why.)
  *
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * Because the quals are reference-adjusted sooner, we cannot do equal()
+ * comparisons between qual and tlist var nodes during the time between
+ * creation of a plan node by createplan.c and its fixing by this module.
+ * Fortunately, there doesn't seem to be any need to do that.
  *
+ * 'join' is a join plan node
  */
 static void
-set_nonamescan_tlist_references(SeqScan *nonamescan)
+set_join_references(Join *join)
 {
-	Noname	   *noname = (Noname *) nonamescan->plan.lefttree;
+	Plan	   *outer = join->lefttree;
+	Plan	   *inner = join->righttree;
+	List	   *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
+	List	   *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
 
-	nonamescan->plan.targetlist = tlist_noname_references(noname->nonameid,
-									  nonamescan->plan.targetlist);
-	/* since we know child is a Noname, skip recursion through
-	 * set_tlist_references() and just get the job done
-	 */
-	set_noname_tlist_references(noname);
+	join->targetlist = join_references(join->targetlist,
+									   outer_tlist,
+									   inner_tlist,
+									   (Index) 0);
 }
 
 /*
- * set_noname_tlist_references
- *	  The noname's vars are made consistent with (actually, identical to) the
- *	  modified version of the target list of the node from which noname node
- *	  receives its tuples.
- *
- * 'noname' is a noname (e.g., sort, materialize) plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * set_uppernode_references
+ *	  Update the targetlist and quals of an upper-level plan node
+ *	  to refer to the tuples returned by its lefttree subplan.
  *
+ * This is used for single-input plan types like Agg, Group, Result.
  */
 static void
-set_noname_tlist_references(Noname *noname)
+set_uppernode_references(Plan *plan, Index subvarno)
 {
-	Plan	   *source = noname->plan.lefttree;
+	Plan	   *subplan = plan->lefttree;
+	List	   *subplanTargetList;
 
-	if (source != NULL)
-	{
-		set_tlist_references(source);
-		noname->plan.targetlist = copy_vars(noname->plan.targetlist,
-											source->targetlist);
-	}
+	if (subplan != NULL)
+		subplanTargetList = subplan->targetlist;
 	else
-		elog(ERROR, "calling set_noname_tlist_references with empty lefttree");
+		subplanTargetList = NIL;
+
+	plan->targetlist = (List *)
+		replace_vars_with_subplan_refs((Node *) plan->targetlist,
+									   subvarno,
+									   subplanTargetList);
+
+	plan->qual = (List *)
+		replace_vars_with_subplan_refs((Node *) plan->qual,
+									   subvarno,
+									   subplanTargetList);
 }
 
 /*
  * join_references
- *	   Creates a new set of join clauses by changing the varno/varattno
- *	   values of variables in the clauses to reference target list values
- *	   from the outer and inner join relation target lists.
- *	   This is just an external interface for replace_joinvar_refs.
- *
- * 'clauses' is the list of join clauses
+ *	   Creates a new set of targetlist entries or join qual clauses by
+ *	   changing the varno/varattno values of variables in the clauses
+ *	   to reference target list values from the outer and inner join
+ *	   relation target lists.
+ *
+ * This is used in two different scenarios: a normal join clause, where
+ * all the Vars in the clause *must* be replaced by OUTER or INNER references;
+ * and an indexscan being used on the inner side of a nestloop join.
+ * In the latter case we want to replace the outer-relation Vars by OUTER
+ * references, but not touch the Vars of the inner relation.
+ *
+ * For a normal join, acceptable_rel should be zero so that any failure to
+ * match a Var will be reported as an error.  For the indexscan case,
+ * pass inner_tlist = NIL and acceptable_rel = the ID of the inner relation.
+ *
+ * 'clauses' is the targetlist or list of join clauses
  * 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
+ * 'inner_tlist' is the target list of the inner join relation, or NIL
+ * 'acceptable_rel' is either zero or the rangetable index of a relation
+ *		whose Vars may appear in the clause without provoking an error.
  *
- * Returns the new join clauses.  The original clause structure is
+ * Returns the new expression tree.  The original clause structure is
  * not modified.
- *
  */
 List *
 join_references(List *clauses,
 				List *outer_tlist,
-				List *inner_tlist)
+				List *inner_tlist,
+				Index acceptable_rel)
 {
-	return (List *) replace_joinvar_refs((Node *) clauses,
-										 outer_tlist,
-										 inner_tlist);
-}
-
-/*
- * replace_joinvar_refs
- *
- *	  Replaces all variables within a join clause with a new var node
- *	  whose varno/varattno fields contain a reference to a target list
- *	  element from either the outer or inner join relation.
- *
- *	  Returns a suitably modified copy of the join clause;
- *	  the original is not modified (and must not be!)
- *
- *	  Side effect: also runs fix_opids on the modified join clause.
- *	  Really ought to make that happen in a uniform, consistent place...
- *
- * 'clause' is the join clause
- * 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
- */
-static Node *
-replace_joinvar_refs(Node *clause,
-					 List *outer_tlist,
-					 List *inner_tlist)
-{
-	replace_joinvar_refs_context context;
+	join_references_context context;
 
 	context.outer_tlist = outer_tlist;
 	context.inner_tlist = inner_tlist;
-	return (Node *) fix_opids((List *)
-							  replace_joinvar_refs_mutator(clause, &context));
+	context.acceptable_rel = acceptable_rel;
+	return (List *) join_references_mutator((Node *) clauses, &context);
 }
 
 static Node *
-replace_joinvar_refs_mutator(Node *node,
-							 replace_joinvar_refs_context *context)
+join_references_mutator(Node *node,
+						join_references_context *context)
 {
 	if (node == NULL)
 		return NULL;
 	if (IsA(node, Var))
 	{
 		Var		   *var = (Var *) node;
-		Resdom	   *resdom = tlist_member(var, context->outer_tlist);
-
-		if (resdom != NULL && IsA(resdom, Resdom))
-			return (Node *) makeVar(OUTER,
-									resdom->resno,
-									var->vartype,
-									var->vartypmod,
-									0,
-									var->varnoold,
-									var->varoattno);
-		resdom = tlist_member(var, context->inner_tlist);
-		if (resdom != NULL && IsA(resdom, Resdom))
-			return (Node *) makeVar(INNER,
-									resdom->resno,
-									var->vartype,
-									var->vartypmod,
-									0,
-									var->varnoold,
-									var->varoattno);
-		/* Var not in either tlist, return an unmodified copy. */
-		return copyObject(node);
-	}
-	return expression_tree_mutator(node,
-								   replace_joinvar_refs_mutator,
-								   (void *) context);
-}
+		Var		   *newvar = (Var *) copyObject(var);
+		Resdom	   *resdom;
 
-/*
- * tlist_noname_references
- *	  Creates a new target list for a node that scans a noname relation,
- *	  setting the varnos to the id of the noname relation and setting varids
- *	  if necessary (varids are only needed if this is a targetlist internal
- *	  to the tree, in which case the targetlist entry always contains a var
- *	  node, so we can just copy it from the noname).
- *
- * 'nonameid' is the id of the noname relation
- * 'tlist' is the target list to be modified
- *
- * Returns new target list
- *
- */
-static List *
-tlist_noname_references(Oid nonameid,
-						List *tlist)
-{
-	List	   *t_list = NIL;
-	List	   *entry;
-
-	foreach(entry, tlist)
-	{
-		TargetEntry *xtl = lfirst(entry);
-		AttrNumber	oattno;
-		TargetEntry *noname;
-
-		if (IsA(get_expr(xtl), Var))
-			oattno = ((Var *) xtl->expr)->varoattno;
-		else
-			oattno = 0;
-
-		noname = makeTargetEntry(xtl->resdom,
-								 (Node *) makeVar(nonameid,
-												  xtl->resdom->resno,
-												  xtl->resdom->restype,
-												  xtl->resdom->restypmod,
-												  0,
-												  nonameid,
-												  oattno));
-
-		t_list = lappend(t_list, noname);
+		resdom = tlist_member((Node *) var, context->outer_tlist);
+		if (resdom)
+		{
+			newvar->varno = OUTER;
+			newvar->varattno = resdom->resno;
+			return (Node *) newvar;
+		}
+		resdom = tlist_member((Node *) var, context->inner_tlist);
+		if (resdom)
+		{
+			newvar->varno = INNER;
+			newvar->varattno = resdom->resno;
+			return (Node *) newvar;
+		}
+		/*
+		 * Var not in either tlist --- either raise an error,
+		 * or return the Var unmodified.
+		 */
+		if (var->varno != context->acceptable_rel)
+			elog(ERROR, "join_references: variable not in subplan target lists");
+		return (Node *) newvar;	/* copy is probably not necessary here... */
 	}
-	return t_list;
-}
-
-/*---------------------------------------------------------
- *
- * set_result_tlist_references
- *
- * Change the target list of a Result node, so that it correctly
- * addresses the tuples returned by its left tree subplan.
- *
- * NOTE:
- *	1) we ignore the right tree! (in the current implementation
- *	   it is always nil)
- *	2) this routine will probably *NOT* work with nested dot
- *	   fields....
- */
-static void
-set_result_tlist_references(Result *resultNode)
-{
-	Plan	   *subplan;
-	List	   *resultTargetList;
-	List	   *subplanTargetList;
-
-	resultTargetList = ((Plan *) resultNode)->targetlist;
-
 	/*
-	 * NOTE: we only consider the left tree subplan. This is usually a seq
-	 * scan.
+	 * expression_tree_mutator will copy SubPlan nodes if given a chance.
+	 * We do not want to do that here, because subselect.c has already
+	 * constructed the initPlan and subPlan lists of the current plan node
+	 * and we mustn't leave those dangling (ie, pointing to different
+	 * copies of the nodes than what's in the targetlist & quals...)
+	 * Instead, alter the SubPlan in-place.  Grotty --- is there a better way?
 	 */
-	subplan = ((Plan *) resultNode)->lefttree;
-	if (subplan != NULL)
-		subplanTargetList = subplan->targetlist;
-	else
-		subplanTargetList = NIL;
-
-	replace_tlist_with_subplan_refs(resultTargetList,
-									(Index) OUTER,
-									subplanTargetList);
-}
-
-/*---------------------------------------------------------
- *
- * replace_tlist_with_subplan_refs
- *
- * Applies replace_vars_with_subplan_refs() to each entry of a targetlist.
- */
-void
-replace_tlist_with_subplan_refs(List *tlist,
-								Index subvarno,
-								List *subplanTargetList)
-{
-	List	   *t;
-
-	foreach(t, tlist)
+	if (is_subplan(node))
 	{
-		TargetEntry *entry = (TargetEntry *) lfirst(t);
-
-		replace_vars_with_subplan_refs((Node *) get_expr(entry),
-									   subvarno, subplanTargetList);
+		Expr	   *expr = (Expr *) node;
+		SubLink	   *sublink = ((SubPlan *) expr->oper)->sublink;
+
+		/* transform args list (params to be passed to subplan) */
+		expr->args = (List *)
+			join_references_mutator((Node *) expr->args,
+									context);
+		/* transform sublink's oper list as well */
+		sublink->oper = (List *)
+			join_references_mutator((Node *) sublink->oper,
+									context);
+
+		return (Node *) expr;
 	}
+	return expression_tree_mutator(node,
+								   join_references_mutator,
+								   (void *) context);
 }
 
-/*---------------------------------------------------------
- *
+/*
  * replace_vars_with_subplan_refs
+ *		This routine modifies an expression tree so that all Var nodes
+ *		reference target nodes of a subplan.  It is used to fix up
+ *		target and qual expressions of non-join upper-level plan nodes.
  *
- * This routine modifies (destructively!) an expression tree so that all
- * Var nodes reference target nodes of a subplan.  It is used to fix up
- * target expressions of upper-level plan nodes.
+ * An error is raised if no matching var can be found in the subplan tlist
+ * --- so this routine should only be applied to nodes whose subplans'
+ * targetlists were generated via flatten_tlist() or some such method.
  *
- * 'clause': the tree to be fixed
+ * 'node': the tree to be fixed (a targetlist or qual list)
  * 'subvarno': varno to be assigned to all Vars
  * 'subplanTargetList': target list for subplan
  *
- * Afterwards, all Var nodes have varno = subvarno, varattno = resno
- * of corresponding subplan target.
+ * The resulting tree is a copy of the original in which all Var nodes have
+ * varno = subvarno, varattno = resno of corresponding subplan target.
+ * The original tree is not modified.
  */
-static void
-replace_vars_with_subplan_refs(Node *clause,
+static Node *
+replace_vars_with_subplan_refs(Node *node,
 							   Index subvarno,
 							   List *subplanTargetList)
 {
@@ -404,182 +351,84 @@ replace_vars_with_subplan_refs(Node *clause,
 
 	context.subvarno = subvarno;
 	context.subplanTargetList = subplanTargetList;
-	replace_vars_with_subplan_refs_walker(clause, &context);
+	return replace_vars_with_subplan_refs_mutator(node, &context);
 }
 
-static bool
-replace_vars_with_subplan_refs_walker(Node *node,
+static Node *
+replace_vars_with_subplan_refs_mutator(Node *node,
 							 replace_vars_with_subplan_refs_context *context)
 {
 	if (node == NULL)
-		return false;
+		return NULL;
 	if (IsA(node, Var))
 	{
 		Var		   *var = (Var *) node;
-		TargetEntry *subplanVar;
+		Var		   *newvar = (Var *) copyObject(var);
+		Resdom	   *resdom;
 
-		subplanVar = match_varid(var, context->subplanTargetList);
-		if (!subplanVar)
-			elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list");
+		resdom = tlist_member((Node *) var, context->subplanTargetList);
+		if (!resdom)
+			elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list");
 
-		/*
-		 * Change the varno & varattno fields of the var node.
-		 */
-		var->varno = context->subvarno;
-		var->varattno = subplanVar->resdom->resno;
-		return false;
+		newvar->varno = context->subvarno;
+		newvar->varattno = resdom->resno;
+		return (Node *) newvar;
+	}
+	/*
+	 * expression_tree_mutator will copy SubPlan nodes if given a chance.
+	 * We do not want to do that here, because subselect.c has already
+	 * constructed the initPlan and subPlan lists of the current plan node
+	 * and we mustn't leave those dangling (ie, pointing to different
+	 * copies of the nodes than what's in the targetlist & quals...)
+	 * Instead, alter the SubPlan in-place.  Grotty --- is there a better way?
+	 */
+	if (is_subplan(node))
+	{
+		Expr	   *expr = (Expr *) node;
+		SubLink	   *sublink = ((SubPlan *) expr->oper)->sublink;
+
+		/* transform args list (params to be passed to subplan) */
+		expr->args = (List *)
+			replace_vars_with_subplan_refs_mutator((Node *) expr->args,
+												   context);
+		/* transform sublink's oper list as well */
+		sublink->oper = (List *)
+			replace_vars_with_subplan_refs_mutator((Node *) sublink->oper,
+												   context);
+
+		return (Node *) expr;
 	}
-	return expression_tree_walker(node,
-								  replace_vars_with_subplan_refs_walker,
-								  (void *) context);
+	return expression_tree_mutator(node,
+								   replace_vars_with_subplan_refs_mutator,
+								   (void *) context);
 }
 
 /*****************************************************************************
- *
+ *					OPERATOR REGPROC LOOKUP
  *****************************************************************************/
 
-/*---------------------------------------------------------
- *
- * set_agg_tlist_references -
- *	  This routine has several responsibilities:
- *	* Update the target list of an Agg node so that it points to
- *	  the tuples returned by its left tree subplan.
- *	* If there is a qual list (from a HAVING clause), similarly update
- *	  vars in it to point to the subplan target list.
- *
- * The return value is TRUE if all qual clauses include Aggrefs, or FALSE
- * if any do not (caller may choose to raise an error condition).
- */
-bool
-set_agg_tlist_references(Agg *aggNode)
-{
-	List	   *subplanTargetList;
-	List	   *tl;
-	List	   *ql;
-	bool		all_quals_ok;
-
-	subplanTargetList = aggNode->plan.lefttree->targetlist;
-
-	foreach(tl, aggNode->plan.targetlist)
-	{
-		TargetEntry *tle = lfirst(tl);
-
-		replace_vars_with_subplan_refs(tle->expr,
-									   (Index) 0,
-									   subplanTargetList);
-	}
-
-	all_quals_ok = true;
-	foreach(ql, aggNode->plan.qual)
-	{
-		Node	   *qual = lfirst(ql);
-
-		replace_vars_with_subplan_refs(qual,
-									   (Index) 0,
-									   subplanTargetList);
-		if (pull_agg_clause(qual) == NIL)
-			all_quals_ok = false;		/* this qual clause has no agg
-										 * functions! */
-	}
-
-	return all_quals_ok;
-}
-
 /*
- * pull_agg_clause
- *	  Recursively pulls all Aggref nodes from an expression clause.
+ * fix_opids
+ *	  Calculate opid field from opno for each Oper node in given tree.
+ *	  The given tree can be anything expression_tree_walker handles.
  *
- *	  Returns list of Aggref nodes found.  Note the nodes themselves are not
- *	  copied, only referenced.
+ * The argument is modified in-place.  (This is OK since we'd want the
+ * same change for any node, even if it gets visited more than once due to
+ * shared structure.)
  */
-List *
-pull_agg_clause(Node *clause)
-{
-	List	   *result = NIL;
-
-	pull_agg_clause_walker(clause, &result);
-	return result;
-}
-
-static bool
-pull_agg_clause_walker(Node *node, List **listptr)
-{
-	if (node == NULL)
-		return false;
-	if (IsA(node, Aggref))
-	{
-		*listptr = lappend(*listptr, node);
-		return false;
-	}
-	return expression_tree_walker(node, pull_agg_clause_walker,
-								  (void *) listptr);
-}
-
-/*
- * check_having_for_ungrouped_vars takes the havingQual and the list of
- * GROUP BY clauses and checks for subplans in the havingQual that are being
- * passed ungrouped variables as parameters.  In other contexts, ungrouped
- * vars in the havingQual will be detected by the parser (see parse_agg.c,
- * exprIsAggOrGroupCol()).	But that routine currently does not check subplans,
- * because the necessary info is not computed until the planner runs.
- * This ought to be cleaned up someday.
- */
-
 void
-check_having_for_ungrouped_vars(Node *clause, List *groupClause,
-								List *targetList)
+fix_opids(Node *node)
 {
-	check_having_for_ungrouped_vars_context context;
-
-	context.groupClause = groupClause;
-	context.targetList = targetList;
-	check_having_for_ungrouped_vars_walker(clause, &context);
+	/* This tree walk requires no special setup, so away we go... */
+	fix_opids_walker(node, NULL);
 }
 
 static bool
-check_having_for_ungrouped_vars_walker(Node *node,
-					check_having_for_ungrouped_vars_context *context)
+fix_opids_walker (Node *node, void *context)
 {
 	if (node == NULL)
 		return false;
-	/*
-	 * We can ignore Vars other than in subplan args lists,
-	 * since the parser already checked 'em.
-	 */
-	if (is_subplan(node))
-	{
-		/*
-		 * The args list of the subplan node represents attributes from
-		 * outside passed into the sublink.
-		 */
-		List	*t;
-
-		foreach(t, ((Expr *) node)->args)
-		{
-			Node	   *thisarg = lfirst(t);
-			bool		contained_in_group_clause = false;
-			List	   *gl;
-
-			foreach(gl, context->groupClause)
-			{
-				GroupClause	   *gcl = lfirst(gl);
-				Node		   *groupexpr;
-
-				groupexpr = get_sortgroupclause_expr(gcl,
-													 context->targetList);
-				/* XXX is var_equal correct, or should we use equal()? */
-				if (var_equal((Var *) thisarg, (Var *) groupexpr))
-				{
-					contained_in_group_clause = true;
-					break;
-				}
-			}
-
-			if (!contained_in_group_clause)
-				elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
-		}
-	}
-	return expression_tree_walker(node,
-								  check_having_for_ungrouped_vars_walker,
-								  (void *) context);
+	if (is_opclause(node))
+		replace_opid((Oper *) ((Expr *) node)->oper);
+	return expression_tree_walker(node, fix_opids_walker, context);
 }
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 188379c9a2dbf0fac71b8aa04a9aa9cf49c8bf25..c275b7adc457a819af76ee383bbeeda6e00f3ac1 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -6,7 +6,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.23 1999/08/22 20:14:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,29 +48,13 @@ int			PlannerPlanId;		/* to assign unique ID to subquery plans */
 static int
 _new_param(Var *var, int varlevel)
 {
-	List	   *last;
-	int			i = 0;
+	Var		   *paramVar = (Var *) copyObject(var);
 
-	if (PlannerParamVar == NULL)
-		last = PlannerParamVar = makeNode(List);
-	else
-	{
-		for (last = PlannerParamVar;;)
-		{
-			i++;
-			if (lnext(last) == NULL)
-				break;
-			last = lnext(last);
-		}
-		lnext(last) = makeNode(List);
-		last = lnext(last);
-	}
+	paramVar->varlevelsup = varlevel;
 
-	lnext(last) = NULL;
-	lfirst(last) = makeVar(var->varno, var->varattno, var->vartype,
-				var->vartypmod, varlevel, var->varnoold, var->varoattno);
+	PlannerParamVar = lappend(PlannerParamVar, paramVar);
 
-	return i;
+	return length(PlannerParamVar) - 1;
 }
 
 /*
@@ -193,8 +177,7 @@ _make_subplan(SubLink *slink)
 			List	   *rside = lnext(((Expr *) lfirst(lst))->args);
 			TargetEntry *te = nth(i, plan->targetlist);
 			Var		   *var = makeVar(0, 0, te->resdom->restype,
-									  te->resdom->restypmod,
-									  0, 0, 0);
+									  te->resdom->restypmod, 0);
 			Param	   *prm = makeNode(Param);
 
 			prm->paramkind = PARAM_EXEC;
@@ -214,7 +197,7 @@ _make_subplan(SubLink *slink)
 	}
 	else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
 	{
-		Var		   *var = makeVar(0, 0, BOOLOID, -1, 0, 0, 0);
+		Var		   *var = makeVar(0, 0, BOOLOID, -1, 0);
 		Param	   *prm = makeNode(Param);
 
 		prm->paramkind = PARAM_EXEC;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 2a9ddfc716a2dc2ef4cb906544d01fd883a4eb76..95e5ddbc9db923277571ed8a09aceabd7763c9b9 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.30 1999/08/21 03:49:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.31 1999/08/22 20:14:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,8 +59,6 @@ preprocess_targetlist(List *tlist,
 	 */
 	expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation);
 
-	/* XXX should the fix-opids be this early?? */
-	fix_opids(expanded_tlist);
 	t_list = copyObject(expanded_tlist);
 
 	/* ------------------
@@ -87,7 +85,7 @@ preprocess_targetlist(List *tlist,
 							0,
 							true);
 
-		var = makeVar(result_relation, -1, TIDOID, -1, 0, result_relation, -1);
+		var = makeVar(result_relation, -1, TIDOID, -1, 0);
 
 		ctid = makeTargetEntry(resdom, (Node *) var);
 		t_list = lappend(t_list, ctid);
@@ -340,8 +338,8 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
 					Var		   *temp_var;
 					TargetEntry *temp_tle;
 
-					temp_var = makeVar(rt_index, attno, atttype, atttypmod,
-									   0, rt_index, attno);
+					temp_var = makeVar(rt_index, attno, atttype,
+									   atttypmod, 0);
 
 					temp_tle = makeTargetEntry(makeResdom(attno,
 														  atttype,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index ca4353f60852309cc139af9aa337dcf932a31a57..fbb5a98e83db1501a7f779c863010f061a60c3ea 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.47 1999/08/16 02:17:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.48 1999/08/22 20:14:53 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -24,11 +24,19 @@
 #include "nodes/plannodes.h"
 #include "optimizer/clauses.h"
 #include "optimizer/internal.h"
+#include "optimizer/tlist.h"
 #include "optimizer/var.h"
 #include "utils/lsyscache.h"
 
 
-static bool fix_opids_walker(Node *node, void *context);
+typedef struct {
+	List	   *groupClause;
+	List	   *targetList;
+} check_subplans_for_ungrouped_vars_context;
+
+static bool pull_agg_clause_walker(Node *node, List **listptr);
+static bool check_subplans_for_ungrouped_vars_walker(Node *node,
+					check_subplans_for_ungrouped_vars_context *context);
 static int is_single_func(Node *node);
 
 
@@ -351,11 +359,117 @@ pull_constant_clauses(List *quals, List **constantQual)
 		else
 			restqual = lcons(lfirst(q), restqual);
 	}
-	freeList(quals);			/* XXX seems a tad risky? */
 	*constantQual = constqual;
 	return restqual;
 }
 
+/*
+ * pull_agg_clause
+ *	  Recursively pulls all Aggref nodes from an expression tree.
+ *
+ *	  Returns list of Aggref nodes found.  Note the nodes themselves are not
+ *	  copied, only referenced.
+ */
+List *
+pull_agg_clause(Node *clause)
+{
+	List	   *result = NIL;
+
+	pull_agg_clause_walker(clause, &result);
+	return result;
+}
+
+static bool
+pull_agg_clause_walker(Node *node, List **listptr)
+{
+	if (node == NULL)
+		return false;
+	if (IsA(node, Aggref))
+	{
+		*listptr = lappend(*listptr, node);
+		/* continue, to iterate over agg's arg as well (do nested aggregates
+		 * actually work?)
+		 */
+	}
+	return expression_tree_walker(node, pull_agg_clause_walker,
+								  (void *) listptr);
+}
+
+/*
+ * check_subplans_for_ungrouped_vars
+ *		Check for subplans that are being passed ungrouped variables as
+ *		parameters; return TRUE if any are found.
+ *
+ * In most contexts, ungrouped variables will be detected by the parser (see
+ * parse_agg.c, exprIsAggOrGroupCol()). But that routine currently does not
+ * check subplans, because the necessary info is not computed until the
+ * planner runs.  So we do it here, after we have processed the subplan.
+ * This ought to be cleaned up someday.
+ *
+ * 'clause' is the expression tree to be searched for subplans.
+ * 'groupClause' is the GROUP BY list (a list of GroupClause nodes).
+ * 'targetList' is the target list that the group clauses refer to.
+ */
+bool
+check_subplans_for_ungrouped_vars(Node *clause,
+								  List *groupClause,
+								  List *targetList)
+{
+	check_subplans_for_ungrouped_vars_context context;
+
+	context.groupClause = groupClause;
+	context.targetList = targetList;
+	return check_subplans_for_ungrouped_vars_walker(clause, &context);
+}
+
+static bool
+check_subplans_for_ungrouped_vars_walker(Node *node,
+					check_subplans_for_ungrouped_vars_context *context)
+{
+	if (node == NULL)
+		return false;
+	/*
+	 * We can ignore Vars other than in subplan args lists,
+	 * since the parser already checked 'em.
+	 */
+	if (is_subplan(node))
+	{
+		/*
+		 * The args list of the subplan node represents attributes from
+		 * outside passed into the sublink.
+		 */
+		List	*t;
+
+		foreach(t, ((Expr *) node)->args)
+		{
+			Node	   *thisarg = lfirst(t);
+			bool		contained_in_group_clause = false;
+			List	   *gl;
+
+			foreach(gl, context->groupClause)
+			{
+				GroupClause	   *gcl = lfirst(gl);
+				Node		   *groupexpr;
+
+				groupexpr = get_sortgroupclause_expr(gcl,
+													 context->targetList);
+				if (equal(thisarg, groupexpr))
+				{
+					contained_in_group_clause = true;
+					break;
+				}
+			}
+
+			if (!contained_in_group_clause)
+				return true;	/* found an ungrouped argument */
+		}
+	}
+	return expression_tree_walker(node,
+								  check_subplans_for_ungrouped_vars_walker,
+								  (void *) context);
+}
+
+
 /*
  * clause_relids_vars
  *	  Retrieves distinct relids and vars appearing within a clause.
@@ -416,31 +530,6 @@ NumRelids(Node *clause)
 	return result;
 }
 
-/*
- * fix_opids
- *	  Calculate opid field from opno for each Oper node in given tree.
- *	  (The given tree can be anything expression_tree_walker handles.)
- *
- * Returns its argument, which has been modified in-place.
- */
-List *
-fix_opids(List *clauses)
-{
-	/* This tree walk requires no special setup, so away we go... */
-	fix_opids_walker((Node *) clauses, NULL);
-	return clauses;
-}
-
-static bool
-fix_opids_walker (Node *node, void *context)
-{
-	if (node == NULL)
-		return false;
-	if (is_opclause(node))
-		replace_opid((Oper *) ((Expr *) node)->oper);
-	return expression_tree_walker(node, fix_opids_walker, context);
-}
-
 /*
  * get_relattval
  *		Extract information from a restriction or join clause for
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 37a790cc3dd5cabd1f27c32ad5d652c0f7862696..dfe2963581feec6d9b934956226bfd64ea3272b1 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.40 1999/08/22 20:14:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,8 +19,6 @@
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
 
-static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
-
 /*****************************************************************************
  *	---------- RELATION node target list routines ----------
  *****************************************************************************/
@@ -28,39 +26,38 @@ static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
 /*
  * tlistentry_member
  *	  Finds the (first) member of the given tlist whose expression is
- *	  var_equal() to the given var.  Result is NULL if no such member.
+ *	  equal() to the given expression.  Result is NULL if no such member.
  */
 TargetEntry *
-tlistentry_member(Var *var, List *targetlist)
+tlistentry_member(Node *node, List *targetlist)
 {
-	if (var && IsA(var, Var))
+	List	   *temp;
+
+	foreach(temp, targetlist)
 	{
-		List	   *temp;
+		TargetEntry	   *tlentry = (TargetEntry *) lfirst(temp);
 
-		foreach(temp, targetlist)
-		{
-			if (var_equal(var, get_expr(lfirst(temp))))
-				return (TargetEntry *) lfirst(temp);
-		}
+		if (equal(node, tlentry->expr))
+			return tlentry;
 	}
 	return NULL;
 }
 
 /*
- * matching_tlist_var
+ * matching_tlist_expr
  *	  Same as tlistentry_member(), except returns the tlist expression
  *	  rather than its parent TargetEntry node.
  */
-Expr *
-matching_tlist_var(Var *var, List *targetlist)
+Node *
+matching_tlist_expr(Node *node, List *targetlist)
 {
 	TargetEntry *tlentry;
 
-	tlentry = tlistentry_member(var, targetlist);
+	tlentry = tlistentry_member(node, targetlist);
 	if (tlentry)
-		return (Expr *) get_expr(tlentry);
+		return tlentry->expr;
 
-	return (Expr *) NULL;
+	return (Node *) NULL;
 }
 
 /*
@@ -69,11 +66,11 @@ matching_tlist_var(Var *var, List *targetlist)
  *	  rather than its parent TargetEntry node.
  */
 Resdom *
-tlist_member(Var *var, List *tlist)
+tlist_member(Node *node, List *targetlist)
 {
 	TargetEntry *tlentry;
 
-	tlentry = tlistentry_member(var, tlist);
+	tlentry = tlistentry_member(node, targetlist);
 	if (tlentry)
 		return tlentry->resdom;
 
@@ -89,7 +86,7 @@ tlist_member(Var *var, List *tlist)
 void
 add_var_to_tlist(RelOptInfo *rel, Var *var)
 {
-	if (! tlistentry_member(var, rel->targetlist))
+	if (! tlistentry_member((Node *) var, rel->targetlist))
 	{
 		/* XXX is copyObject necessary here? */
 		rel->targetlist = lappend(rel->targetlist,
@@ -116,84 +113,10 @@ create_tl_element(Var *var, int resdomno)
 						   (Node *) var);
 }
 
-/*
- * get_actual_tlist
- *	  Returns the targetlist elements from a relation tlist.
- *
- */
-List *
-get_actual_tlist(List *tlist)
-{
-
-	/*
-	 * this function is not making sense. - ay 10/94
-	 */
-#ifdef NOT_USED
-	List	   *element = NIL;
-	List	   *result = NIL;
-
-	if (tlist == NULL)
-	{
-		elog(DEBUG, "calling get_actual_tlist with empty tlist");
-		return NIL;
-	}
-
-	/*
-	 * XXX - it is unclear to me what exactly get_entry should be doing,
-	 * as it is unclear to me the exact relationship between "TL" "TLE"
-	 * and joinlists
-	 */
-
-	foreach(element, tlist)
-		result = lappend(result, lfirst((List *) lfirst(element)));
-
-	return result;
-#endif
-	return tlist;
-}
-
 /*****************************************************************************
  *		---------- GENERAL target list routines ----------
  *****************************************************************************/
 
-/*
- * match_varid
- *	  Searches a target list for an entry matching a given var.
- *
- * Returns the target list entry (resdom var) of the matching var,
- * or NULL if no match.
- */
-TargetEntry *
-match_varid(Var *test_var, List *tlist)
-{
-	List	   *tl;
-
-	Assert(test_var->varlevelsup == 0);	/* XXX why? */
-
-	foreach(tl, tlist)
-	{
-		TargetEntry *entry = lfirst(tl);
-		Var		   *tlvar = get_expr(entry);
-
-		if (!IsA(tlvar, Var))
-			continue;
-
-		/*
-		 * we test the original varno, instead of varno which might be
-		 * changed to INNER/OUTER.  XXX is test on vartype necessary?
-		 */
-		Assert(tlvar->varlevelsup == 0);
-
-		if (tlvar->varnoold == test_var->varnoold &&
-			tlvar->varoattno == test_var->varoattno &&
-			tlvar->vartype == test_var->vartype)
-			return entry;
-	}
-
-	return NULL;
-}
-
-
 /*
  * new_unsorted_tlist
  *	  Creates a copy of a target list by creating new resdom nodes
@@ -220,37 +143,6 @@ new_unsorted_tlist(List *targetlist)
 	return new_targetlist;
 }
 
-/*
- * copy_vars
- *	  Replaces the var nodes in the first target list with those from
- *	  the second target list.  The two target lists are assumed to be
- *	  identical except their actual resdoms and vars are different.
- *
- * 'target' is the target list to be replaced
- * 'source' is the target list to be copied
- *
- * Returns a new target list.
- *
- */
-List *
-copy_vars(List *target, List *source)
-{
-	List	   *result = NIL;
-	List	   *src;
-	List	   *dest;
-
-	for (src = source, dest = target;
-		 src != NIL && dest != NIL;
-		 src = lnext(src), dest = lnext(dest))
-	{
-		TargetEntry *temp = makeTargetEntry(((TargetEntry *) lfirst(dest))->resdom,
-										 (Node *) get_expr(lfirst(src)));
-
-		result = lappend(result, temp);
-	}
-	return result;
-}
-
 /*
  * flatten_tlist
  *	  Create a target list that only contains unique variables.
@@ -292,7 +184,7 @@ add_to_flat_tlist(List *tlist, List *vars)
 	{
 		Var		   *var = lfirst(v);
 
-		if (! tlistentry_member(var, tlist))
+		if (! tlistentry_member((Node *) var, tlist))
 		{
 			Resdom	   *r;
 
@@ -310,39 +202,6 @@ add_to_flat_tlist(List *tlist, List *vars)
 	return tlist;
 }
 
-/*
- * unflatten_tlist
- *	  Reconstructs the target list of a query by replacing vars within
- *	  target expressions with vars from the 'flattened' target list.
- *
- * XXX is this really necessary?  Why can't we just use the tlist as is?
- *
- * 'full_tlist' is the original target list
- * 'flat_tlist' is the flattened (var-only) target list
- *
- * Returns the rebuilt target list.  The original is not modified.
- *
- */
-List *
-unflatten_tlist(List *full_tlist, List *flat_tlist)
-{
-	return (List *) unflatten_tlist_mutator((Node *) full_tlist,
-											flat_tlist);
-}
-
-static Node *
-unflatten_tlist_mutator(Node *node, List *flat_tlist)
-{
-	if (node == NULL)
-		return NULL;
-	if (IsA(node, Var))
-		return (Node *) get_expr(match_varid((Var *) node,
-											 flat_tlist));
-	return expression_tree_mutator(node, unflatten_tlist_mutator,
-								   (void *) flat_tlist);
-}
-
-
 Var *
 get_expr(TargetEntry *tle)
 {
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index be181ea626f2bce9d8826359700eb6a6bd6ee006..a544041122b2f4aff43b692330665c4ce9a33b76 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.22 1999/08/10 03:00:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.23 1999/08/22 20:14:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,29 +105,3 @@ pull_var_clause_walker(Node *node, List **listptr)
 	return expression_tree_walker(node, pull_var_clause_walker,
 								  (void *) listptr);
 }
-
-/*
- *		var_equal
- *
- *		This is like equal() except that it does NOT test varnoold and
- *		varoattno.  Also, it will not compare non-Var nodes.
- *
- *		Returns t iff two var nodes correspond to the same attribute.
- */
-bool
-var_equal(Var *var1, Var *var2)
-{
-	if (var1 != NULL && IsA(var1, Var) &&
-		var2 != NULL && IsA(var2, Var) &&
-		var1->varno == var2->varno &&
-		var1->varattno == var2->varattno &&
-		var1->vartype == var2->vartype &&
-		var1->vartypmod == var2->vartypmod &&
-		var1->varlevelsup == var2->varlevelsup)
-	{
-		Assert(var1->varlevelsup == 0);	/* XXX why do this here??? */
-		return true;
-	}
-	else
-		return false;
-}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 4a227de2c5ba32620ffd39f6e2eac11121914c06..19a287d99e882fb5bb7637cbadb0ab83a09c7295 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.53 1999/08/21 03:48:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.54 1999/08/22 20:15:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -444,7 +444,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 			 */
 			toid = typeTypeId(typenameType(relname));
 			/* replace it in the arg list */
-			lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0, vnum, 0);
+			lfirst(fargs) = makeVar(vnum, 0, toid, -1, 0);
 		}
 		else if (!attisset)
 		{						/* set functions don't have parameters */
@@ -1300,7 +1300,7 @@ setup_tlist(char *attname, Oid relid)
 						 0,
 						 InvalidOid,
 						 false);
-	varnode = makeVar(-1, attno, typeid, type_mod, 0, -1, attno);
+	varnode = makeVar(-1, attno, typeid, type_mod, 0);
 
 	tle = makeTargetEntry(resnode, (Node *) varnode);
 	return lcons(tle, NIL);
@@ -1325,7 +1325,7 @@ setup_base_tlist(Oid typeid)
 						 0,
 						 InvalidOid,
 						 false);
-	varnode = makeVar(-1, 1, typeid, -1, 0, -1, 1);
+	varnode = makeVar(-1, 1, typeid, -1, 0);
 	tle = makeTargetEntry(resnode, (Node *) varnode);
 
 	return lcons(tle, NIL);
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 80a8543d5a5addf8d9fe871bda674a4e0492a90d..48da11d8d2394fd27a3c1e1514a3eff517ecfc7a 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.30 1999/08/22 20:15:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -216,8 +216,7 @@ make_var(ParseState *pstate, Oid relid, char *refname,
 	vartypeid = get_atttype(relid, attid);
 	type_mod = get_atttypmod(relid, attid);
 
-	varnode = makeVar(vnum, attid, vartypeid, type_mod,
-					  sublevels_up, vnum, attid);
+	varnode = makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
 
 	return varnode;
 }
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 6a387fd5c5c05fe2a84af3d1d57c9bad174a930e..1aaead722d8aced8e501942e2468ea3bd236b33c 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: makefuncs.h,v 1.20 1999/07/15 23:03:52 momjian Exp $
+ * $Id: makefuncs.h,v 1.21 1999/08/22 20:15:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,9 +25,7 @@ extern Var *makeVar(Index varno,
 		AttrNumber varattno,
 		Oid vartype,
 		int32 vartypmod,
-		Index varlevelsup,
-		Index varnoold,
-		AttrNumber varoattno);
+		Index varlevelsup);
 
 extern TargetEntry *makeTargetEntry(Resdom *resdom, Node *expr);
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4eea81446b2e23877c9a03774a56c013278d3b24..10e51e40268eed36463e7e0edd453d888c7c19eb 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.34 1999/08/21 03:49:09 tgl Exp $
+ * $Id: primnodes.h,v 1.35 1999/08/22 20:15:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,15 +120,23 @@ typedef struct Expr
 /* ----------------
  * Var
  *		varno			- index of this var's relation in the range table
- *						  (could be INNER or OUTER)
+ *						  (could also be INNER or OUTER)
  *		varattno		- attribute number of this var, or zero for all
- *		vartype			- pg_type tuple oid for the type of this var
+ *		vartype			- pg_type tuple OID for the type of this var
  *		vartypmod		- pg_attribute typmod value
- *		varlevelsup		- for subquery variables referencing outer relations
- *		varnoold		- keep varno around in case it got changed to INNER/
- *						  OUTER (see match_varid)
- *		varoattno		- attribute number of this var
- *						  [ '(varnoold varoattno) was varid   -ay 2/95]
+ *		varlevelsup		- for subquery variables referencing outer relations;
+ *						  0 in a normal var, >0 means N levels up
+ *		varnoold		- original value of varno
+ *		varoattno		- original value of varattno
+ *
+ * Note: during parsing/planning, varnoold/varoattno are always just copies
+ * of varno/varattno.  At the tail end of planning, Var nodes appearing in
+ * upper-level plan nodes are reassigned to point to the outputs of their
+ * subplans; for example, in a join node varno becomes INNER or OUTER and
+ * varattno becomes the index of the proper element of that subplan's target
+ * list.  But varnoold/varoattno continue to hold the original values.
+ * The code doesn't really need varnoold/varoattno, but they are very useful
+ * for debugging and interpreting completed plans, so we keep them around.
  * ----------------
  */
 #define    INNER		65000
@@ -145,8 +153,8 @@ typedef struct Var
 	Oid			vartype;
 	int32		vartypmod;
 	Index		varlevelsup;	/* erased by upper optimizer */
-	Index		varnoold;		/* only used by optimizer */
-	AttrNumber	varoattno;		/* only used by optimizer */
+	Index		varnoold;		/* mainly for debugging --- see above */
+	AttrNumber	varoattno;
 } Var;
 
 /* ----------------
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index ec2dce883fccbedded84148895ef73e6e6f03967..6ea6b4f97eadc74cc625fa48d23c0214fa1ea09c 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.28 1999/08/16 02:17:44 tgl Exp $
+ * $Id: clauses.h,v 1.29 1999/08/22 20:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,9 +38,13 @@ extern Expr *make_ands_explicit(List *andclauses);
 extern List *make_ands_implicit(Expr *clause);
 
 extern List *pull_constant_clauses(List *quals, List **constantQual);
+extern List *pull_agg_clause(Node *clause);
+extern bool check_subplans_for_ungrouped_vars(Node *clause,
+											  List *groupClause,
+											  List *targetList);
+
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int	NumRelids(Node *clause);
-extern List *fix_opids(List *clauses);
 extern void get_relattval(Node *clause, int targetrelid,
 						  int *relid, AttrNumber *attno,
 						  Datum *constval, int *flag);
@@ -53,8 +57,8 @@ extern bool expression_tree_walker(Node *node, bool (*walker) (),
 extern Node *expression_tree_mutator(Node *node, Node * (*mutator) (),
 									 void *context);
 
-#define is_subplan(clause)	((Node*) (clause) != NULL && \
-						nodeTag((Node*) (clause)) == T_Expr && \
-						((Expr *) (clause))->opType == SUBPLAN_EXPR)
+#define is_subplan(clause)	((clause) != NULL && \
+							 IsA(clause, Expr) && \
+							 ((Expr *) (clause))->opType == SUBPLAN_EXPR)
 
 #endif	 /* CLAUSES_H */
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 38ff367384be5b1d08089b1664dcd36d9fabbabb..3abda02d932c13436e3966ebb677025ec8521b51 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.31 1999/08/21 03:49:15 tgl Exp $
+ * $Id: planmain.h,v 1.32 1999/08/22 20:14:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,14 +27,14 @@ extern Plan *query_planner(Query *root,
  * prototypes for plan/createplan.c
  */
 extern Plan *create_plan(Path *best_path);
-extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid,
-			 Plan *lefttree);
+extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree,
 		  int keycount);
 extern Agg *make_agg(List *tlist, Plan *lefttree);
 extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
 		   AttrNumber *grpColIdx, Plan *lefttree);
 extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
+extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 
 /*
  * prototypes for plan/initsplan.c
@@ -47,17 +47,10 @@ extern void add_missing_vars_to_tlist(Query *root, List *tlist);
 /*
  * prototypes for plan/setrefs.c
  */
-extern void set_tlist_references(Plan *plan);
+extern void set_plan_references(Plan *plan);
 extern List *join_references(List *clauses, List *outer_tlist,
-							 List *inner_tlist);
-extern void replace_tlist_with_subplan_refs(List *tlist,
-								Index subvarno,
-								List *subplanTargetList);
-extern bool set_agg_tlist_references(Agg *aggNode);
-extern List *pull_agg_clause(Node *clause);
-extern void check_having_for_ungrouped_vars(Node *clause,
-								List *groupClause,
-								List *targetList);
+							 List *inner_tlist, Index acceptable_rel);
+extern void fix_opids(Node *node);
 
 /*
  * prep/prepkeyset.c
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index f0ddb9ac9dcc4b406ae668384a043042f834813f..58b1b7b2e994fd5ba3c33ef2e6a72e302c001b95 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tlist.h,v 1.21 1999/08/21 03:49:15 tgl Exp $
+ * $Id: tlist.h,v 1.22 1999/08/22 20:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,20 +15,16 @@
 
 #include "nodes/relation.h"
 
-extern TargetEntry *tlistentry_member(Var *var, List *targetlist);
-extern Expr *matching_tlist_var(Var *var, List *targetlist);
+extern TargetEntry *tlistentry_member(Node *node, List *targetlist);
+extern Node *matching_tlist_expr(Node *node, List *targetlist);
+extern Resdom *tlist_member(Node *node, List *targetlist);
+
 extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
 extern TargetEntry *create_tl_element(Var *var, int resdomno);
-extern List *get_actual_tlist(List *tlist);
-extern Resdom *tlist_member(Var *var, List *tlist);
 
-extern TargetEntry *match_varid(Var *test_var, List *tlist);
 extern List *new_unsorted_tlist(List *targetlist);
-extern List *copy_vars(List *target, List *source);
 extern List *flatten_tlist(List *tlist);
 extern List *add_to_flat_tlist(List *tlist, List *vars);
-extern List *unflatten_tlist(List *full_tlist,
-							 List *flat_tlist);
 
 extern Var *get_expr(TargetEntry *tle);
 extern Node *get_sortgroupclause_expr(SortClause *sortClause,
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index 16f9f4f6634a01dee9a2345f64a0e9f95b7a4598..440b62f49adc75d5b6bf5c693c9bfb48f8958288 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: var.h,v 1.8 1999/07/15 15:21:23 momjian Exp $
+ * $Id: var.h,v 1.9 1999/08/22 20:14:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,5 @@
 extern List *pull_varnos(Node *me);
 extern bool contain_var_clause(Node *clause);
 extern List *pull_var_clause(Node *clause);
-extern bool var_equal(Var *var1, Var *var2);
 
 #endif	 /* VAR_H */