diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index d559be0b6eb5bada5f380a22228ff960edbfcbe3..247f7f48cbef4568fbcff47c6b4a0953d03ed527 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.21 2007/01/31 20:56:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.22 2007/02/22 22:00:22 tgl Exp $ -->
 
 <chapter id="indexam">
  <title>Index Access Method Interface Definition</title>
@@ -903,7 +903,7 @@ amcostestimate (PlannerInfo *root,
      * Also, we charge for evaluation of the indexquals at each index row.
      * All the costs are assumed to be paid incrementally during the scan.
      */
-    cost_qual_eval(&amp;index_qual_cost, indexQuals);
+    cost_qual_eval(&amp;index_qual_cost, indexQuals, root);
     *indexStartupCost = index_qual_cost.startup;
     *indexTotalCost = seq_page_cost * numIndexPages +
         (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 58b7e6ded9a48b1a74b344a45022fba02159e298..34784eb078e8d47306d0ea21247b729df52ca7bb 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.157 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@ typedef struct ExplainState
 	bool		printNodes;		/* do nodeToString() too */
 	bool		printAnalyze;	/* print actual times */
 	/* other states */
+	PlannedStmt *pstmt;			/* top of plan */
 	List	   *rtable;			/* range table */
 } ExplainState;
 
@@ -260,6 +261,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
 
 	es->printNodes = stmt->verbose;
 	es->printAnalyze = stmt->analyze;
+	es->pstmt = queryDesc->plannedstmt;
 	es->rtable = queryDesc->plannedstmt->rtable;
 
 	if (es->printNodes)
@@ -858,7 +860,6 @@ explain_outNode(StringInfo str,
 	/* initPlan-s */
 	if (plan->initPlan)
 	{
-		List	   *saved_rtable = es->rtable;
 		ListCell   *lst;
 
 		for (i = 0; i < indent; i++)
@@ -869,16 +870,15 @@ explain_outNode(StringInfo str,
 			SubPlanState *sps = (SubPlanState *) lfirst(lst);
 			SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
-			es->rtable = sp->rtable;
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "    ->  ");
-			explain_outNode(str, sp->plan,
+			explain_outNode(str,
+							exec_subplan_get_plan(es->pstmt, sp),
 							sps->planstate,
 							NULL,
 							indent + 4, es);
 		}
-		es->rtable = saved_rtable;
 	}
 
 	/* lefttree */
@@ -994,12 +994,6 @@ explain_outNode(StringInfo str,
 		SubqueryScan *subqueryscan = (SubqueryScan *) plan;
 		SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
 		Plan	   *subnode = subqueryscan->subplan;
-		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
-									  es->rtable);
-		List	   *saved_rtable = es->rtable;
-
-		Assert(rte->rtekind == RTE_SUBQUERY);
-		es->rtable = rte->subquery->rtable;
 
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
@@ -1009,14 +1003,11 @@ explain_outNode(StringInfo str,
 						subquerystate->subplan,
 						NULL,
 						indent + 3, es);
-
-		es->rtable = saved_rtable;
 	}
 
 	/* subPlan-s */
 	if (planstate->subPlan)
 	{
-		List	   *saved_rtable = es->rtable;
 		ListCell   *lst;
 
 		for (i = 0; i < indent; i++)
@@ -1027,16 +1018,15 @@ explain_outNode(StringInfo str,
 			SubPlanState *sps = (SubPlanState *) lfirst(lst);
 			SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
-			es->rtable = sp->rtable;
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "    ->  ");
-			explain_outNode(str, sp->plan,
+			explain_outNode(str,
+							exec_subplan_get_plan(es->pstmt, sp),
 							sps->planstate,
 							NULL,
 							indent + 4, es);
 		}
-		es->rtable = saved_rtable;
 	}
 }
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 405b58f9fd6b087ec18ecda7541f7b1a98e63c9c..d0df0ea6f472f28e48f822234b6cab7bfa85ba8c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.288 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,9 +92,9 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning,
 					 DestReceiver *dest);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
+static void ExecCheckRTPerms(List *rangeTable);
 static void ExecCheckRTEPerms(RangeTblEntry *rte);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
-static void ExecCheckRangeTblReadOnly(List *rtable);
 static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
 				  evalPlanQual *priorepq);
 static void EvalPlanQualStop(evalPlanQual *epq);
@@ -348,16 +348,14 @@ ExecutorRewind(QueryDesc *queryDesc)
  * ExecCheckRTPerms
  *		Check access permissions for all relations listed in a range table.
  */
-void
+static void
 ExecCheckRTPerms(List *rangeTable)
 {
 	ListCell   *l;
 
 	foreach(l, rangeTable)
 	{
-		RangeTblEntry *rte = lfirst(l);
-
-		ExecCheckRTEPerms(rte);
+		ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
 	}
 }
 
@@ -373,12 +371,9 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 	Oid			userid;
 
 	/*
-	 * Only plain-relation RTEs need to be checked here.  Subquery RTEs are
-	 * checked by ExecInitSubqueryScan if the subquery is still a separate
-	 * subquery --- if it's been pulled up into our query level then the RTEs
-	 * are in our rangetable and will be checked here. Function RTEs are
+	 * Only plain-relation RTEs need to be checked here.  Function RTEs are
 	 * checked by init_fcache when the function is prepared for execution.
-	 * Join and special RTEs need no checks.
+	 * Join, subquery, and special RTEs need no checks.
 	 */
 	if (rte->rtekind != RTE_RELATION)
 		return;
@@ -417,6 +412,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 static void
 ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
+	ListCell   *l;
+
 	/*
 	 * CREATE TABLE AS or SELECT INTO?
 	 *
@@ -426,32 +423,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 		goto fail;
 
 	/* Fail if write permissions are requested on any non-temp table */
-	ExecCheckRangeTblReadOnly(plannedstmt->rtable);
-
-	return;
-
-fail:
-	ereport(ERROR,
-			(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
-			 errmsg("transaction is read-only")));
-}
-
-static void
-ExecCheckRangeTblReadOnly(List *rtable)
-{
-	ListCell   *l;
-
-	/* Fail if write permissions are requested on any non-temp table */
-	foreach(l, rtable)
+	foreach(l, plannedstmt->rtable)
 	{
-		RangeTblEntry *rte = lfirst(l);
-
-		if (rte->rtekind == RTE_SUBQUERY)
-		{
-			Assert(!rte->subquery->into);
-			ExecCheckRangeTblReadOnly(rte->subquery->rtable);
-			continue;
-		}
+		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
 		if (rte->rtekind != RTE_RELATION)
 			continue;
@@ -494,9 +468,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	ListCell   *l;
 
 	/*
-	 * Do permissions checks.  It's sufficient to examine the query's top
-	 * rangetable here --- subplan RTEs will be checked during
-	 * ExecInitSubPlan().
+	 * Do permissions checks
 	 */
 	ExecCheckRTPerms(rangeTable);
 
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index d9957573883b3bf909c44f2a5ef775feec3f5b77..d188a38489d1e470202e306d2a9fc1dc82f7e841 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.146 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -847,7 +847,6 @@ ExecRelationIsTargetRelation(EState *estate, Index scanrelid)
 Relation
 ExecOpenScanRelation(EState *estate, Index scanrelid)
 {
-	RangeTblEntry *rtentry;
 	Oid			reloid;
 	LOCKMODE	lockmode;
 	ResultRelInfo *resultRelInfos;
@@ -885,8 +884,7 @@ ExecOpenScanRelation(EState *estate, Index scanrelid)
 	}
 
 	/* OK, open the relation and acquire lock as needed */
-	rtentry = rt_fetch(scanrelid, estate->es_range_table);
-	reloid = rtentry->relid;
+	reloid = getrelid(scanrelid, estate->es_range_table);
 
 	return heap_open(reloid, lockmode);
 }
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index d3d9886e3c3aee128500537974eec19cf7893b27..87ac754a1e37e556282a78ed1edcf469bf05f52b 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.43 2007/02/19 02:23:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.44 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,6 @@
 
 #include "executor/nodeFunctionscan.h"
 #include "funcapi.h"
-#include "parser/parsetree.h"
 #include "utils/builtins.h"
 
 
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 32167a94efbff491b18797034acac2422c90ad5d..9bc96921f416fcd77736707e71f2142cbb88f779 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.85 2007/02/06 02:59:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.86 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -637,13 +637,9 @@ void
 ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
 {
 	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+	Plan	   *plan = exec_subplan_get_plan(estate->es_plannedstmt, subplan);
 	EState	   *sp_estate;
 
-	/*
-	 * Do access checking on the rangetable entries in the subquery.
-	 */
-	ExecCheckRTPerms(subplan->rtable);
-
 	/*
 	 * initialize my state
 	 */
@@ -668,18 +664,21 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
 	 * shares our Param ID space and es_query_cxt, however.  XXX if rangetable
 	 * access were done differently, the subquery could share our EState,
 	 * which would eliminate some thrashing about in this module...
+	 *
+	 * XXX make that happen!
 	 */
 	sp_estate = CreateSubExecutorState(estate);
 	node->sub_estate = sp_estate;
 
-	sp_estate->es_range_table = subplan->rtable;
+	sp_estate->es_range_table = estate->es_range_table;
 	sp_estate->es_param_list_info = estate->es_param_list_info;
 	sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
 	sp_estate->es_tupleTable =
-		ExecCreateTupleTable(ExecCountSlotsNode(subplan->plan) + 10);
+		ExecCreateTupleTable(ExecCountSlotsNode(plan) + 10);
 	sp_estate->es_snapshot = estate->es_snapshot;
 	sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
 	sp_estate->es_instrument = estate->es_instrument;
+	sp_estate->es_plannedstmt = estate->es_plannedstmt;
 
 	/*
 	 * Start up the subplan (this is a very cut-down form of InitPlan())
@@ -692,7 +691,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
 	if (subplan->parParam == NIL && subplan->setParam == NIL)
 		eflags |= EXEC_FLAG_REWIND;
 
-	node->planstate = ExecInitNode(subplan->plan, sp_estate, eflags);
+	node->planstate = ExecInitNode(plan, sp_estate, eflags);
 
 	node->needShutdown = true;	/* now we need to shutdown the subplan */
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 24d470d8edd1f914698ecc312eb3705d6c7ae86c..6d58a8cad4e63b92b273dde8f6fabb934a473099 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.35 2007/01/05 22:19:28 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.36 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,7 +29,6 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeSubqueryscan.h"
-#include "parser/parsetree.h"
 
 static TupleTableSlot *SubqueryNext(SubqueryScanState *node);
 
@@ -104,17 +103,18 @@ SubqueryScanState *
 ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 {
 	SubqueryScanState *subquerystate;
-	RangeTblEntry *rte;
 	EState	   *sp_estate;
 
 	/* check for unsupported flags */
 	Assert(!(eflags & EXEC_FLAG_MARK));
 
 	/*
-	 * SubqueryScan should not have any "normal" children.
+	 * SubqueryScan should not have any "normal" children.  Also, if planner
+	 * left anything in subrtable, it's fishy.
 	 */
 	Assert(outerPlan(node) == NULL);
 	Assert(innerPlan(node) == NULL);
+	Assert(node->subrtable == NIL);
 
 	/*
 	 * create state structure
@@ -152,25 +152,18 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	 * initialize subquery
 	 *
 	 * This should agree with ExecInitSubPlan
-	 */
-	rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
-	Assert(rte->rtekind == RTE_SUBQUERY);
-
-	/*
-	 * Do access checking on the rangetable entries in the subquery.
-	 */
-	ExecCheckRTPerms(rte->subquery->rtable);
-
-	/*
+	 *
 	 * The subquery needs its own EState because it has its own rangetable. It
 	 * shares our Param ID space and es_query_cxt, however.  XXX if rangetable
 	 * access were done differently, the subquery could share our EState,
 	 * which would eliminate some thrashing about in this module...
+	 *
+	 * XXX make that happen!
 	 */
 	sp_estate = CreateSubExecutorState(estate);
 	subquerystate->sss_SubEState = sp_estate;
 
-	sp_estate->es_range_table = rte->subquery->rtable;
+	sp_estate->es_range_table = estate->es_range_table;
 	sp_estate->es_param_list_info = estate->es_param_list_info;
 	sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
 	sp_estate->es_tupleTable =
@@ -178,6 +171,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	sp_estate->es_snapshot = estate->es_snapshot;
 	sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
 	sp_estate->es_instrument = estate->es_instrument;
+	sp_estate->es_plannedstmt = estate->es_plannedstmt;
 
 	/*
 	 * Start up the subplan (this is a very cut-down form of InitPlan())
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 96e4b98a4e25cd947c30d2b5f16ca85796264810..bfd837e0985900937723c7b6abbbeaa707cfb3c2 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.6 2007/02/19 02:23:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.7 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 
 #include "executor/executor.h"
 #include "executor/nodeValuesscan.h"
-#include "parser/parsetree.h"
 #include "utils/memutils.h"
 
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4d10afc022db95447f238c1fc51894d048fb9bf0..efa1f86945337a9857df0cb0b6fc8f5b6d4d88ba 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.368 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,6 +78,7 @@ _copyPlannedStmt(PlannedStmt *from)
 	COPY_NODE_FIELD(rtable);
 	COPY_NODE_FIELD(resultRelations);
 	COPY_NODE_FIELD(into);
+	COPY_NODE_FIELD(subplans);
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(rowMarks);
 	COPY_SCALAR_FIELD(nParamExec);
@@ -366,6 +367,7 @@ _copySubqueryScan(SubqueryScan *from)
 	 * copy remainder of node
 	 */
 	COPY_NODE_FIELD(subplan);
+	COPY_NODE_FIELD(subrtable);
 
 	return newnode;
 }
@@ -957,9 +959,8 @@ _copySubPlan(SubPlan *from)
 	COPY_SCALAR_FIELD(subLinkType);
 	COPY_NODE_FIELD(testexpr);
 	COPY_NODE_FIELD(paramIds);
-	COPY_NODE_FIELD(plan);
 	COPY_SCALAR_FIELD(plan_id);
-	COPY_NODE_FIELD(rtable);
+	COPY_SCALAR_FIELD(firstColType);
 	COPY_SCALAR_FIELD(useHashTable);
 	COPY_SCALAR_FIELD(unknownEqFalse);
 	COPY_NODE_FIELD(setParam);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index dafa15f0287dcd849e36d1a8e3222a1de9acd5b6..1007930814ac36694122856b47582bb99621fd0e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.300 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -306,9 +306,8 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
 	COMPARE_SCALAR_FIELD(subLinkType);
 	COMPARE_NODE_FIELD(testexpr);
 	COMPARE_NODE_FIELD(paramIds);
-	/* should compare plans, but have to settle for comparing plan IDs */
 	COMPARE_SCALAR_FIELD(plan_id);
-	COMPARE_NODE_FIELD(rtable);
+	COMPARE_SCALAR_FIELD(firstColType);
 	COMPARE_SCALAR_FIELD(useHashTable);
 	COMPARE_SCALAR_FIELD(unknownEqFalse);
 	COMPARE_NODE_FIELD(setParam);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c8cc25abb1330ce9c8cc25d8dd90375bafbf0f6c..64f235201d0def2ba1d63eeae6d7b5b029d729b8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.301 2007/02/22 22:00:23 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -245,6 +245,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node)
 	WRITE_NODE_FIELD(rtable);
 	WRITE_NODE_FIELD(resultRelations);
 	WRITE_NODE_FIELD(into);
+	WRITE_NODE_FIELD(subplans);
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(rowMarks);
 	WRITE_INT_FIELD(nParamExec);
@@ -415,6 +416,7 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
 	_outScanInfo(str, (Scan *) node);
 
 	WRITE_NODE_FIELD(subplan);
+	WRITE_NODE_FIELD(subrtable);
 }
 
 static void
@@ -823,9 +825,8 @@ _outSubPlan(StringInfo str, SubPlan *node)
 	WRITE_ENUM_FIELD(subLinkType, SubLinkType);
 	WRITE_NODE_FIELD(testexpr);
 	WRITE_NODE_FIELD(paramIds);
-	WRITE_NODE_FIELD(plan);
 	WRITE_INT_FIELD(plan_id);
-	WRITE_NODE_FIELD(rtable);
+	WRITE_OID_FIELD(firstColType);
 	WRITE_BOOL_FIELD(useHashTable);
 	WRITE_BOOL_FIELD(unknownEqFalse);
 	WRITE_NODE_FIELD(setParam);
@@ -1259,7 +1260,9 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
 
 	/* NB: this isn't a complete set of fields */
 	WRITE_NODE_FIELD(paramlist);
-	WRITE_INT_FIELD(next_plan_id);
+	WRITE_NODE_FIELD(subplans);
+	WRITE_NODE_FIELD(subrtables);
+	WRITE_NODE_FIELD(finalrtable);
 }
 
 static void
@@ -1317,6 +1320,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
 	WRITE_UINT_FIELD(pages);
 	WRITE_FLOAT_FIELD(tuples, "%.0f");
 	WRITE_NODE_FIELD(subplan);
+	WRITE_NODE_FIELD(subrtable);
 	WRITE_NODE_FIELD(baserestrictinfo);
 	WRITE_NODE_FIELD(joininfo);
 	WRITE_BOOL_FIELD(has_eclass_joins);
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 06d28b56b21641c959e113c50ede6915dba6594d..c6edfbed8a054d465d55b01c8d35eb4e54a72127 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.84 2007/02/10 14:58:54 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.85 2007/02/22 22:00:23 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -25,7 +25,6 @@
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
-static char *plannode_type(Plan *p);
 
 /*
  * print
@@ -488,171 +487,3 @@ print_slot(TupleTableSlot *slot)
 
 	debugtup(slot, NULL);
 }
-
-static char *
-plannode_type(Plan *p)
-{
-	switch (nodeTag(p))
-	{
-		case T_Plan:
-			return "PLAN";
-		case T_Result:
-			return "RESULT";
-		case T_Append:
-			return "APPEND";
-		case T_BitmapAnd:
-			return "BITMAPAND";
-		case T_BitmapOr:
-			return "BITMAPOR";
-		case T_Scan:
-			return "SCAN";
-		case T_SeqScan:
-			return "SEQSCAN";
-		case T_IndexScan:
-			return "INDEXSCAN";
-		case T_BitmapIndexScan:
-			return "BITMAPINDEXSCAN";
-		case T_BitmapHeapScan:
-			return "BITMAPHEAPSCAN";
-		case T_TidScan:
-			return "TIDSCAN";
-		case T_SubqueryScan:
-			return "SUBQUERYSCAN";
-		case T_FunctionScan:
-			return "FUNCTIONSCAN";
-		case T_ValuesScan:
-			return "VALUESSCAN";
-		case T_Join:
-			return "JOIN";
-		case T_NestLoop:
-			return "NESTLOOP";
-		case T_MergeJoin:
-			return "MERGEJOIN";
-		case T_HashJoin:
-			return "HASHJOIN";
-		case T_Material:
-			return "MATERIAL";
-		case T_Sort:
-			return "SORT";
-		case T_Agg:
-			return "AGG";
-		case T_Unique:
-			return "UNIQUE";
-		case T_SetOp:
-			return "SETOP";
-		case T_Limit:
-			return "LIMIT";
-		case T_Hash:
-			return "HASH";
-		case T_Group:
-			return "GROUP";
-		default:
-			return "UNKNOWN";
-	}
-}
-
-/*
- * Recursively prints a simple text description of the plan tree
- */
-void
-print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
-{
-	int			i;
-	char		extraInfo[NAMEDATALEN + 100];
-
-	if (!p)
-		return;
-	for (i = 0; i < indentLevel; i++)
-		printf(" ");
-	printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
-		   p->startup_cost, p->total_cost,
-		   p->plan_rows, p->plan_width);
-	if (IsA(p, Scan) ||
-		IsA(p, SeqScan) ||
-		IsA(p, BitmapHeapScan))
-	{
-		RangeTblEntry *rte;
-
-		rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
-		strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-	}
-	else if (IsA(p, IndexScan))
-	{
-		RangeTblEntry *rte;
-
-		rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
-		strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-	}
-	else if (IsA(p, FunctionScan))
-	{
-		RangeTblEntry *rte;
-
-		rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
-		strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-	}
-	else if (IsA(p, ValuesScan))
-	{
-		RangeTblEntry *rte;
-
-		rte = rt_fetch(((ValuesScan *) p)->scan.scanrelid, parsetree->rtable);
-		strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-	}
-	else
-		extraInfo[0] = '\0';
-	if (extraInfo[0] != '\0')
-		printf(" ( %s )\n", extraInfo);
-	else
-		printf("\n");
-	print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
-	print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
-
-	if (IsA(p, Append))
-	{
-		ListCell   *l;
-		Append	   *appendplan = (Append *) p;
-
-		foreach(l, appendplan->appendplans)
-		{
-			Plan	   *subnode = (Plan *) lfirst(l);
-
-			print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-		}
-	}
-
-	if (IsA(p, BitmapAnd))
-	{
-		ListCell   *l;
-		BitmapAnd  *bitmapandplan = (BitmapAnd *) p;
-
-		foreach(l, bitmapandplan->bitmapplans)
-		{
-			Plan	   *subnode = (Plan *) lfirst(l);
-
-			print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-		}
-	}
-
-	if (IsA(p, BitmapOr))
-	{
-		ListCell   *l;
-		BitmapOr   *bitmaporplan = (BitmapOr *) p;
-
-		foreach(l, bitmaporplan->bitmapplans)
-		{
-			Plan	   *subnode = (Plan *) lfirst(l);
-
-			print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-		}
-	}
-}
-
-/*
- * print_plan
- *
- * prints just the plan node types
- */
-void
-print_plan(Plan *p, Query *parsetree)
-{
-	print_plan_recursive(p, parsetree, 0, "");
-}
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 6c6e80071c1ccfc12bbeed1a9d0c92373a750bb4..7aa2bd7e6fb2c050aefce6853fe93da4ea5ddee9 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.161 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -521,6 +521,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 									root->query_level + 1,
 									tuple_fraction,
 									&subroot);
+	rel->subrtable = subroot->parse->rtable;
 
 	/* Copy number of output rows from subplan */
 	rel->tuples = rel->subplan->plan_rows;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 422ef9232226ad82dd22d73f2d712b5eb80a0c90..3dbb3bd802dbd8cd7cef7b14d27c8eb62f97a604 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.177 2007/01/22 20:00:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,11 +107,16 @@ bool		enable_nestloop = true;
 bool		enable_mergejoin = true;
 bool		enable_hashjoin = true;
 
+typedef struct
+{
+	PlannerInfo *root;
+	QualCost	total;
+} cost_qual_eval_context;
 
 static MergeScanSelCache *cached_scansel(PlannerInfo *root,
 										 RestrictInfo *rinfo,
 										 PathKey *pathkey);
-static bool cost_qual_eval_walker(Node *node, QualCost *total);
+static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
 static Selectivity approx_selectivity(PlannerInfo *root, List *quals,
 				   JoinType jointype);
 static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root);
@@ -362,7 +367,7 @@ cost_index(IndexPath *path, PlannerInfo *root,
 	{
 		QualCost	index_qual_cost;
 
-		cost_qual_eval(&index_qual_cost, indexQuals);
+		cost_qual_eval(&index_qual_cost, indexQuals, root);
 		/* any startup cost still has to be paid ... */
 		cpu_per_tuple -= index_qual_cost.per_tuple;
 	}
@@ -855,7 +860,7 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
 	Assert(rte->rtekind == RTE_FUNCTION);
 
 	/* Estimate costs of executing the function expression */
-	cost_qual_eval_node(&exprcost, rte->funcexpr);
+	cost_qual_eval_node(&exprcost, rte->funcexpr, root);
 
 	startup_cost += exprcost.startup;
 	cpu_per_tuple = exprcost.per_tuple;
@@ -1241,7 +1246,7 @@ cost_nestloop(NestPath *path, PlannerInfo *root)
 	ntuples = outer_path_rows * inner_path_rows * joininfactor;
 
 	/* CPU costs */
-	cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo);
+	cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root);
 	startup_cost += restrict_qual_cost.startup;
 	cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
 	run_cost += cpu_per_tuple * ntuples;
@@ -1301,8 +1306,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
 	 */
 	merge_selec = approx_selectivity(root, mergeclauses,
 									 path->jpath.jointype);
-	cost_qual_eval(&merge_qual_cost, mergeclauses);
-	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo);
+	cost_qual_eval(&merge_qual_cost, mergeclauses, root);
+	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
 	qp_qual_cost.startup -= merge_qual_cost.startup;
 	qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple;
 
@@ -1587,8 +1592,8 @@ cost_hashjoin(HashPath *path, PlannerInfo *root)
 	 */
 	hash_selec = approx_selectivity(root, hashclauses,
 									path->jpath.jointype);
-	cost_qual_eval(&hash_qual_cost, hashclauses);
-	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo);
+	cost_qual_eval(&hash_qual_cost, hashclauses, root);
+	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
 	qp_qual_cost.startup -= hash_qual_cost.startup;
 	qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple;
 
@@ -1756,12 +1761,14 @@ cost_hashjoin(HashPath *path, PlannerInfo *root)
  *		and a per-evaluation component.
  */
 void
-cost_qual_eval(QualCost *cost, List *quals)
+cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
 {
+	cost_qual_eval_context context;
 	ListCell   *l;
 
-	cost->startup = 0;
-	cost->per_tuple = 0;
+	context.root = root;
+	context.total.startup = 0;
+	context.total.per_tuple = 0;
 
 	/* We don't charge any cost for the implicit ANDing at top level ... */
 
@@ -1769,8 +1776,10 @@ cost_qual_eval(QualCost *cost, List *quals)
 	{
 		Node	   *qual = (Node *) lfirst(l);
 
-		cost_qual_eval_walker(qual, cost);
+		cost_qual_eval_walker(qual, &context);
 	}
+
+	*cost = context.total;
 }
 
 /*
@@ -1778,15 +1787,21 @@ cost_qual_eval(QualCost *cost, List *quals)
  *		As above, for a single RestrictInfo or expression.
  */
 void
-cost_qual_eval_node(QualCost *cost, Node *qual)
+cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
 {
-	cost->startup = 0;
-	cost->per_tuple = 0;
-	cost_qual_eval_walker(qual, cost);
+	cost_qual_eval_context context;
+
+	context.root = root;
+	context.total.startup = 0;
+	context.total.per_tuple = 0;
+
+	cost_qual_eval_walker(qual, &context);
+
+	*cost = context.total;
 }
 
 static bool
-cost_qual_eval_walker(Node *node, QualCost *total)
+cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 {
 	if (node == NULL)
 		return false;
@@ -1803,18 +1818,19 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 
 		if (rinfo->eval_cost.startup < 0)
 		{
-			rinfo->eval_cost.startup = 0;
-			rinfo->eval_cost.per_tuple = 0;
+			cost_qual_eval_context locContext;
+
+			locContext.root = context->root;
+			locContext.total.startup = 0;
+			locContext.total.per_tuple = 0;
 			/*
 			 * For an OR clause, recurse into the marked-up tree so that
 			 * we set the eval_cost for contained RestrictInfos too.
 			 */
 			if (rinfo->orclause)
-				cost_qual_eval_walker((Node *) rinfo->orclause,
-									  &rinfo->eval_cost);
+				cost_qual_eval_walker((Node *) rinfo->orclause, &locContext);
 			else
-				cost_qual_eval_walker((Node *) rinfo->clause,
-									  &rinfo->eval_cost);
+				cost_qual_eval_walker((Node *) rinfo->clause, &locContext);
 			/*
 			 * If the RestrictInfo is marked pseudoconstant, it will be tested
 			 * only once, so treat its cost as all startup cost.
@@ -1822,12 +1838,13 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 			if (rinfo->pseudoconstant)
 			{
 				/* count one execution during startup */
-				rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple;
-				rinfo->eval_cost.per_tuple = 0;
+				locContext.total.startup += locContext.total.per_tuple;
+				locContext.total.per_tuple = 0;
 			}
+			rinfo->eval_cost = locContext.total;
 		}
-		total->startup += rinfo->eval_cost.startup;
-		total->per_tuple += rinfo->eval_cost.per_tuple;
+		context->total.startup += rinfo->eval_cost.startup;
+		context->total.per_tuple += rinfo->eval_cost.per_tuple;
 		/* do NOT recurse into children */
 		return false;
 	}
@@ -1849,8 +1866,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 	 */
 	if (IsA(node, FuncExpr))
 	{
-		total->per_tuple += get_func_cost(((FuncExpr *) node)->funcid) *
-			cpu_operator_cost;
+		context->total.per_tuple +=
+			get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
 	}
 	else if (IsA(node, OpExpr) ||
 			 IsA(node, DistinctExpr) ||
@@ -1858,8 +1875,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 	{
 		/* rely on struct equivalence to treat these all alike */
 		set_opfuncid((OpExpr *) node);
-		total->per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) *
-			cpu_operator_cost;
+		context->total.per_tuple +=
+			get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
 	}
 	else if (IsA(node, ScalarArrayOpExpr))
 	{
@@ -1871,7 +1888,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 		Node	   *arraynode = (Node *) lsecond(saop->args);
 
 		set_sa_opfuncid(saop);
-		total->per_tuple += get_func_cost(saop->opfuncid) *
+		context->total.per_tuple += get_func_cost(saop->opfuncid) *
 			cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
 	}
 	else if (IsA(node, RowCompareExpr))
@@ -1884,7 +1901,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 		{
 			Oid		opid = lfirst_oid(lc);
 
-			total->per_tuple += get_func_cost(get_opcode(opid)) *
+			context->total.per_tuple += get_func_cost(get_opcode(opid)) *
 				cpu_operator_cost;
 		}
 	}
@@ -1905,7 +1922,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 		 * subplan by hashing.
 		 */
 		SubPlan    *subplan = (SubPlan *) node;
-		Plan	   *plan = subplan->plan;
+		Plan	   *plan = planner_subplan_get_plan(context->root, subplan);
 
 		if (subplan->useHashTable)
 		{
@@ -1915,7 +1932,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 			 * cpu_operator_cost per tuple for the work of loading the
 			 * hashtable, too.
 			 */
-			total->startup += plan->total_cost +
+			context->total.startup += plan->total_cost +
 				cpu_operator_cost * plan->plan_rows;
 
 			/*
@@ -1941,20 +1958,21 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 			if (subplan->subLinkType == EXISTS_SUBLINK)
 			{
 				/* we only need to fetch 1 tuple */
-				total->per_tuple += plan_run_cost / plan->plan_rows;
+				context->total.per_tuple += plan_run_cost / plan->plan_rows;
 			}
 			else if (subplan->subLinkType == ALL_SUBLINK ||
 					 subplan->subLinkType == ANY_SUBLINK)
 			{
 				/* assume we need 50% of the tuples */
-				total->per_tuple += 0.50 * plan_run_cost;
+				context->total.per_tuple += 0.50 * plan_run_cost;
 				/* also charge a cpu_operator_cost per row examined */
-				total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
+				context->total.per_tuple +=
+					0.50 * plan->plan_rows * cpu_operator_cost;
 			}
 			else
 			{
 				/* assume we need all tuples */
-				total->per_tuple += plan_run_cost;
+				context->total.per_tuple += plan_run_cost;
 			}
 
 			/*
@@ -1967,15 +1985,15 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 			if (subplan->parParam == NIL &&
 				(IsA(plan, Sort) ||
 				 IsA(plan, Material)))
-				total->startup += plan->startup_cost;
+				context->total.startup += plan->startup_cost;
 			else
-				total->per_tuple += plan->startup_cost;
+				context->total.per_tuple += plan->startup_cost;
 		}
 	}
 
 	/* recurse into children */
 	return expression_tree_walker(node, cost_qual_eval_walker,
-								  (void *) total);
+								  (void *) context);
 }
 
 
@@ -2042,7 +2060,7 @@ set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
 
 	rel->rows = clamp_row_est(nrows);
 
-	cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
+	cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
 
 	set_rel_width(root, rel);
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 4d3d926a167fa58b1d2f06af0ae60013cbba26a3..dbfa2c4e58e415981f8b538a7ef535580dea9697 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.225 2007/02/19 02:23:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.226 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -128,12 +128,12 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  * create_plan
  *	  Creates the access plan for a query by tracing backwards through the
  *	  desired chain of pathnodes, starting at the node 'best_path'.  For
- *	  every pathnode found:
- *	  (1) Create a corresponding plan node containing appropriate id,
- *		  target list, and qualification information.
- *	  (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.
+ *	  every pathnode found, we create a corresponding plan node containing
+ *	  appropriate id, target list, and qualification information.
+ *
+ *	  The tlists and quals in the plan tree are still in planner format,
+ *	  ie, Vars still correspond to the parser's numbering.  This will be
+ *	  fixed later by setrefs.c.
  *
  *	  best_path is the best access path
  *
@@ -421,7 +421,8 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
 	if (!pseudoconstants)
 		return plan;
 
-	return (Plan *) make_result((List *) copyObject(plan->targetlist),
+	return (Plan *) make_result(root,
+								plan->targetlist,
 								(Node *) pseudoconstants,
 								plan);
 }
@@ -519,7 +520,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 	if (best_path->subpaths == NIL)
 	{
 		/* Generate a Result plan with constant-FALSE gating qual */
-		return (Plan *) make_result(tlist,
+		return (Plan *) make_result(root,
+									tlist,
 									(Node *) list_make1(makeBoolConst(false,
 																	  false)),
 									NULL);
@@ -559,7 +561,7 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path)
 
 	quals = order_qual_clauses(root, best_path->quals);
 
-	return make_result(tlist, (Node *) quals, NULL);
+	return make_result(root, tlist, (Node *) quals, NULL);
 }
 
 /*
@@ -682,7 +684,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
 		 * node to help it along.
 		 */
 		if (!is_projection_capable_plan(subplan))
-			subplan = (Plan *) make_result(newtlist, NULL, subplan);
+			subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
 		else
 			subplan->targetlist = newtlist;
 	}
@@ -1065,12 +1067,6 @@ create_bitmap_scan_plan(PlannerInfo *root,
 	 */
 	bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual);
 
-	/*
-	 * Copy the finished bitmapqualorig to make sure we have an independent
-	 * copy --- needed in case there are subplans in the index quals
-	 */
-	bitmapqualorig = copyObject(bitmapqualorig);
-
 	/* Finally ready to build the plan node */
 	scan_plan = make_bitmap_heapscan(tlist,
 									 qpqual,
@@ -1333,7 +1329,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
 	scan_plan = make_subqueryscan(tlist,
 								  scan_clauses,
 								  scan_relid,
-								  best_path->parent->subplan);
+								  best_path->parent->subplan,
+								  best_path->parent->subrtable);
 
 	copy_path_costsize(&scan_plan->scan.plan, best_path);
 
@@ -2115,7 +2112,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 		Node	   *clause = (Node *) lfirst(lc);
 		QualCost	qcost;
 
-		cost_qual_eval_node(&qcost, clause);
+		cost_qual_eval_node(&qcost, clause, root);
 		items[i].clause = clause;
 		items[i].cost = qcost.per_tuple;
 		i++;
@@ -2326,7 +2323,8 @@ SubqueryScan *
 make_subqueryscan(List *qptlist,
 				  List *qpqual,
 				  Index scanrelid,
-				  Plan *subplan)
+				  Plan *subplan,
+				  List *subrtable)
 {
 	SubqueryScan *node = makeNode(SubqueryScan);
 	Plan	   *plan = &node->scan.plan;
@@ -2345,6 +2343,7 @@ make_subqueryscan(List *qptlist,
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
 	node->subplan = subplan;
+	node->subrtable = subrtable;
 
 	return node;
 }
@@ -2524,7 +2523,7 @@ make_hash(Plan *lefttree)
 	 * plan; this only affects EXPLAIN display not decisions.
 	 */
 	plan->startup_cost = plan->total_cost;
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -2583,7 +2582,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
 			  lefttree->plan_width);
 	plan->startup_cost = sort_path.startup_cost;
 	plan->total_cost = sort_path.total_cost;
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -2741,10 +2740,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
 			 * Do we need to insert a Result node?
 			 */
 			if (!is_projection_capable_plan(lefttree))
-			{
-				tlist = copyObject(tlist);
-				lefttree = (Plan *) make_result(tlist, NULL, lefttree);
-			}
+				lefttree = (Plan *) make_result(root, tlist, NULL, lefttree);
 
 			/*
 			 * Add resjunk entry to input's tlist
@@ -2905,7 +2901,7 @@ make_material(Plan *lefttree)
 	Plan	   *plan = &node->plan;
 
 	/* cost should be inserted by caller */
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -2996,12 +2992,12 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
 	 */
 	if (qual)
 	{
-		cost_qual_eval(&qual_cost, qual);
+		cost_qual_eval(&qual_cost, qual, root);
 		plan->startup_cost += qual_cost.startup;
 		plan->total_cost += qual_cost.startup;
 		plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
 	}
-	cost_qual_eval(&qual_cost, tlist);
+	cost_qual_eval(&qual_cost, tlist, root);
 	plan->startup_cost += qual_cost.startup;
 	plan->total_cost += qual_cost.startup;
 	plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
@@ -3059,12 +3055,12 @@ make_group(PlannerInfo *root,
 	 */
 	if (qual)
 	{
-		cost_qual_eval(&qual_cost, qual);
+		cost_qual_eval(&qual_cost, qual, root);
 		plan->startup_cost += qual_cost.startup;
 		plan->total_cost += qual_cost.startup;
 		plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
 	}
-	cost_qual_eval(&qual_cost, tlist);
+	cost_qual_eval(&qual_cost, tlist, root);
 	plan->startup_cost += qual_cost.startup;
 	plan->total_cost += qual_cost.startup;
 	plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
@@ -3108,7 +3104,7 @@ make_unique(Plan *lefttree, List *distinctList)
 	 * has a better idea.
 	 */
 
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -3174,7 +3170,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree,
 	if (plan->plan_rows < 1)
 		plan->plan_rows = 1;
 
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -3272,7 +3268,7 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
 			plan->plan_rows = 1;
 	}
 
-	plan->targetlist = copyObject(lefttree->targetlist);
+	plan->targetlist = lefttree->targetlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
@@ -3293,7 +3289,8 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
  * cost.  In either case, tlist eval cost is not to be included here.
  */
 Result *
-make_result(List *tlist,
+make_result(PlannerInfo *root,
+			List *tlist,
 			Node *resconstantqual,
 			Plan *subplan)
 {
@@ -3312,7 +3309,7 @@ make_result(List *tlist,
 		{
 			QualCost	qual_cost;
 
-			cost_qual_eval(&qual_cost, (List *) resconstantqual);
+			cost_qual_eval(&qual_cost, (List *) resconstantqual, root);
 			/* resconstantqual is evaluated once at startup */
 			plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
 			plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 406cc9dd49665845ef9d889565dbd1226c72b236..5411072b8dbb5f15e2c365f874b817ebc21bd197 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.29 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -188,10 +188,10 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
 	/*
 	 * Generate the output plan --- basically just a Result
 	 */
-	plan = (Plan *) make_result(tlist, hqual, NULL);
+	plan = (Plan *) make_result(root, tlist, hqual, NULL);
 
 	/* Account for evaluation cost of the tlist (make_result did the rest) */
-	cost_qual_eval(&tlist_cost, tlist);
+	cost_qual_eval(&tlist_cost, tlist, root);
 	plan->startup_cost += tlist_cost.startup;
 	plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple;
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3bb603a0f614bb53ff4cbb0eebefee770afbd819..b45288dc5b8faafe9a827aacb56fbcccd69c6a0c 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.215 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,6 +88,8 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 	double		tuple_fraction;
 	PlannerInfo *root;
 	Plan	   *top_plan;
+	ListCell   *lp,
+			   *lr;
 
 	/*
 	 * Set up global state for this planner invocation.  This data is needed
@@ -99,7 +101,9 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 
 	glob->boundParams = boundParams;
 	glob->paramlist = NIL;
-	glob->next_plan_id = 0;
+	glob->subplans = NIL;
+	glob->subrtables = NIL;
+	glob->finalrtable = NIL;
 
 	/* Determine what fraction of the plan is likely to be scanned */
 	if (isCursor)
@@ -132,7 +136,17 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 	}
 
 	/* final cleanup of the plan */
-	top_plan = set_plan_references(top_plan, parse->rtable);
+	Assert(glob->finalrtable == NIL);
+	top_plan = set_plan_references(glob, top_plan, root->parse->rtable);
+	/* ... and the subplans (both regular subplans and initplans) */
+	Assert(list_length(glob->subplans) == list_length(glob->subrtables));
+	forboth(lp, glob->subplans, lr, glob->subrtables)
+	{
+		Plan   *subplan = (Plan *) lfirst(lp);
+		List   *subrtable = (List *) lfirst(lr);
+
+		lfirst(lp) = set_plan_references(glob, subplan, subrtable);
+	}
 
 	/* build the PlannedStmt result */
 	result = makeNode(PlannedStmt);
@@ -140,9 +154,10 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 	result->commandType = parse->commandType;
 	result->canSetTag = parse->canSetTag;
 	result->planTree = top_plan;
-	result->rtable = parse->rtable;
+	result->rtable = glob->finalrtable;
 	result->resultRelations = root->resultRelations;
 	result->into = parse->into;
+	result->subplans = glob->subplans;
 	result->returningLists = root->returningLists;
 	result->rowMarks = parse->rowMarks;
 	result->nParamExec = list_length(glob->paramlist);
@@ -182,7 +197,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 				 Index level, double tuple_fraction,
 				 PlannerInfo **subroot)
 {
-	int			saved_plan_id = glob->next_plan_id;
+	int			num_old_subplans = list_length(glob->subplans);
 	PlannerInfo *root;
 	Plan	   *plan;
 	List	   *newHaving;
@@ -384,7 +399,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	 * initPlan list and extParam/allParam sets for plan nodes, and attach the
 	 * initPlans to the top plan node.
 	 */
-	if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
+	if (list_length(glob->subplans) != num_old_subplans ||
+		root->query_level > 1)
 		SS_finalize_plan(root, plan);
 
 	/* Return internal info if caller wants it */
@@ -621,7 +637,8 @@ inheritance_planner(PlannerInfo *root)
 	 * If we managed to exclude every child rel, return a dummy plan
 	 */
 	if (subplans == NIL)
-		return (Plan *) make_result(tlist,
+		return (Plan *) make_result(root,
+									tlist,
 									(Node *) list_make1(makeBoolConst(false,
 																	  false)),
 									NULL);
@@ -639,6 +656,10 @@ inheritance_planner(PlannerInfo *root)
 	 */
 	parse->rtable = rtable;
 
+	/* Suppress Append if there's only one surviving child rel */
+	if (list_length(subplans) == 1)
+		return (Plan *) linitial(subplans);
+
 	return (Plan *) make_append(subplans, true, tlist);
 }
 
@@ -897,7 +918,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				 */
 				if (!is_projection_capable_plan(result_plan))
 				{
-					result_plan = (Plan *) make_result(sub_tlist, NULL,
+					result_plan = (Plan *) make_result(root,
+													   sub_tlist,
+													   NULL,
 													   result_plan);
 				}
 				else
@@ -928,7 +951,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				 * tuples) --- so make_agg() and make_group() are responsible
 				 * for computing the added cost.
 				 */
-				cost_qual_eval(&tlist_cost, sub_tlist);
+				cost_qual_eval(&tlist_cost, sub_tlist, root);
 				result_plan->startup_cost += tlist_cost.startup;
 				result_plan->total_cost += tlist_cost.startup +
 					tlist_cost.per_tuple * result_plan->plan_rows;
@@ -1051,7 +1074,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				 * this routine to avoid having to generate the plan in the
 				 * first place.
 				 */
-				result_plan = (Plan *) make_result(tlist,
+				result_plan = (Plan *) make_result(root,
+												   tlist,
 												   parse->havingQual,
 												   NULL);
 			}
@@ -1110,6 +1134,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 	{
 		List	   *rlist;
 
+		Assert(parse->resultRelation);
 		rlist = set_returning_clause_references(parse->returningList,
 												result_plan,
 												parse->resultRelation);
@@ -1544,7 +1569,7 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
  * pass down only c,d,a+b, but it's not really worth the trouble to
  * eliminate simple var references from the subplan.  We will avoid doing
  * the extra computation to recompute a+b at the outer level; see
- * replace_vars_with_subplan_refs() in setrefs.c.)
+ * fix_upper_expr() in setrefs.c.)
  *
  * If we are grouping or aggregating, *and* there are no non-Var grouping
  * expressions, then the returned tlist is effectively dummy; we do not
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 3d9f5486bccfce75f312eee1be38fb6b7ec90a75..aaa383742e7bdb2b798f72920a934aaec2176dff 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.130 2007/02/19 02:23:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.131 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,48 +40,60 @@ typedef struct
 	tlist_vinfo vars[1];		/* VARIABLE LENGTH ARRAY */
 } indexed_tlist;				/* VARIABLE LENGTH STRUCT */
 
+typedef struct
+{
+	int			rtoffset;
+} fix_scan_expr_context;
+
 typedef struct
 {
 	indexed_tlist *outer_itlist;
 	indexed_tlist *inner_itlist;
 	Index		acceptable_rel;
-} join_references_context;
+	int			rtoffset;
+} fix_join_expr_context;
 
 typedef struct
 {
 	indexed_tlist *subplan_itlist;
 	Index		subvarno;
-} replace_vars_with_subplan_refs_context;
+	int			rtoffset;
+} fix_upper_expr_context;
+
+#define fix_scan_list(lst, rtoffset) \
+	((List *) fix_scan_expr((Node *) (lst), rtoffset))
 
-static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable);
+static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset);
+static Plan *set_subqueryscan_references(PlannerGlobal *glob,
+										 SubqueryScan *plan,
+										 int rtoffset);
 static bool trivial_subqueryscan(SubqueryScan *plan);
-static void adjust_plan_varnos(Plan *plan, int rtoffset);
-static void adjust_expr_varnos(Node *node, int rtoffset);
-static bool adjust_expr_varnos_walker(Node *node, int *context);
-static void fix_expr_references(Plan *plan, Node *node);
-static bool fix_expr_references_walker(Node *node, void *context);
-static void set_join_references(Join *join);
+static Node *fix_scan_expr(Node *node, int rtoffset);
+static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
+static void set_join_references(Join *join, int rtoffset);
 static void set_inner_join_references(Plan *inner_plan,
 						  indexed_tlist *outer_itlist);
-static void set_uppernode_references(Plan *plan, Index subvarno);
+static void set_upper_references(Plan *plan, Index subvarno, int rtoffset);
 static indexed_tlist *build_tlist_index(List *tlist);
 static Var *search_indexed_tlist_for_var(Var *var,
 							 indexed_tlist *itlist,
-							 Index newvarno);
+							 Index newvarno,
+							 int rtoffset);
 static Var *search_indexed_tlist_for_non_var(Node *node,
 								 indexed_tlist *itlist,
 								 Index newvarno);
-static List *join_references(List *clauses,
-				indexed_tlist *outer_itlist,
-				indexed_tlist *inner_itlist,
-				Index acceptable_rel);
-static Node *join_references_mutator(Node *node,
-						join_references_context *context);
-static Node *replace_vars_with_subplan_refs(Node *node,
-							   indexed_tlist *subplan_itlist,
-							   Index subvarno);
-static Node *replace_vars_with_subplan_refs_mutator(Node *node,
-							replace_vars_with_subplan_refs_context *context);
+static List *fix_join_expr(List *clauses,
+						   indexed_tlist *outer_itlist,
+						   indexed_tlist *inner_itlist,
+						   Index acceptable_rel, int rtoffset);
+static Node *fix_join_expr_mutator(Node *node,
+								   fix_join_expr_context *context);
+static Node *fix_upper_expr(Node *node,
+							indexed_tlist *subplan_itlist,
+							Index subvarno,
+							int rtoffset);
+static Node *fix_upper_expr_mutator(Node *node,
+									fix_upper_expr_context *context);
 static bool fix_opfuncids_walker(Node *node, void *context);
 
 
@@ -96,34 +108,87 @@ static bool fix_opfuncids_walker(Node *node, void *context);
  *
  * 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).
+ * for the convenience of the executor:
+ *
+ * 1. We flatten the various subquery rangetables into a single list, and
+ * zero out RangeTblEntry fields that are not useful to the executor.
+ *
+ * 2. We adjust Vars in scan nodes to be consistent with the flat rangetable.
+ *
+ * 3. We adjust Vars in upper plan nodes to refer to the outputs of their
+ * subplans.
+ *
+ * 4. We compute regproc OIDs for operators (ie, we look up the function
+ * that implements each op).
  *
  * We also perform one final optimization step, which is to delete
  * SubqueryScan plan nodes that aren't doing anything useful (ie, have
  * no qual and a no-op targetlist).  The reason for doing this last is that
  * it can't readily be done before set_plan_references, because it would
- * break set_uppernode_references: the Vars in the subquery's top tlist
- * won't match up with the Vars in the outer plan tree.  The SubqueryScan
+ * break set_upper_references: the Vars in the subquery's top tlist
+ * wouldn't match up with the Vars in the outer plan tree.  The SubqueryScan
  * serves a necessary function as a buffer between outer query and subquery
- * variable numbering ... but the executor doesn't care about that, only the
- * planner.
+ * variable numbering ... but after we've flattened the rangetable this is
+ * no longer a problem, since there's only one rtindex namespace.
  *
  * set_plan_references recursively traverses the whole plan tree.
  *
+ * Inputs:
+ *	glob: global data for planner run
+ *	plan: the topmost node of the plan
+ *	rtable: the rangetable for the current subquery
+ *
  * The return value is normally the same Plan node passed in, but can be
  * different when the passed-in Plan is a SubqueryScan we decide isn't needed.
  *
- * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes
- * and append the modified subquery rangetable to the outer rangetable.
- * Therefore "rtable" is an in/out argument and really should be declared
- * "List **".  But in the interest of notational simplicity we don't do that.
- * (Since rtable can't be NIL if there's a SubqueryScan, the list header
- * address won't change when we append a subquery rangetable.)
+ * The flattened rangetable entries are appended to glob->finalrtable.
+ *
+ * Notice that we modify Plan nodes in-place, but use expression_tree_mutator
+ * to process targetlist and qual expressions.  We can assume that the Plan
+ * nodes were just built by the planner and are not multiply referenced, but
+ * it's not so safe to assume that for expression tree nodes.
  */
 Plan *
-set_plan_references(Plan *plan, List *rtable)
+set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable)
+{
+	int			rtoffset = list_length(glob->finalrtable);
+	ListCell   *lc;
+
+	/*
+	 * In the flat rangetable, we zero out substructure pointers that are
+	 * not needed by the executor; this reduces the storage space and
+	 * copying cost for cached plans.
+	 */
+	foreach(lc, rtable)
+	{
+		RangeTblEntry  *rte = (RangeTblEntry *) lfirst(lc);
+		RangeTblEntry  *newrte;
+
+		/* flat copy to duplicate all the scalar fields */
+		newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
+		memcpy(newrte, rte, sizeof(RangeTblEntry));
+
+		/* zap unneeded sub-structure (we keep only the eref Alias) */
+		newrte->subquery = NULL;
+		newrte->funcexpr = NULL;
+		newrte->funccoltypes = NIL;
+		newrte->funccoltypmods = NIL;
+		newrte->values_lists = NIL;
+		newrte->joinaliasvars = NIL;
+		newrte->alias = NULL;
+
+		glob->finalrtable = lappend(glob->finalrtable, newrte);
+	}
+
+	/* Now fix the Plan tree */
+	return set_plan_refs(glob, plan, rtoffset);
+}
+
+/*
+ * set_plan_refs: recurse through the Plan nodes of a single subquery level
+ */
+static Plan *
+set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
 {
 	ListCell   *l;
 
@@ -136,73 +201,109 @@ set_plan_references(Plan *plan, List *rtable)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
+			{
+				SeqScan *splan = (SeqScan *) plan;
+
+				splan->scanrelid += rtoffset;
+				splan->plan.targetlist =
+					fix_scan_list(splan->plan.targetlist, rtoffset);
+				splan->plan.qual =
+					fix_scan_list(splan->plan.qual, rtoffset);
+			}
 			break;
 		case T_IndexScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan,
-								(Node *) ((IndexScan *) plan)->indexqual);
-			fix_expr_references(plan,
-								(Node *) ((IndexScan *) plan)->indexqualorig);
+			{
+				IndexScan *splan = (IndexScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+				splan->scan.plan.qual =
+					fix_scan_list(splan->scan.plan.qual, rtoffset);
+				splan->indexqual =
+					fix_scan_list(splan->indexqual, rtoffset);
+				splan->indexqualorig =
+					fix_scan_list(splan->indexqualorig, rtoffset);
+			}
 			break;
 		case T_BitmapIndexScan:
-			/* no need to fix targetlist and qual */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			fix_expr_references(plan,
-							 (Node *) ((BitmapIndexScan *) plan)->indexqual);
-			fix_expr_references(plan,
-						 (Node *) ((BitmapIndexScan *) plan)->indexqualorig);
+			{
+				BitmapIndexScan *splan = (BitmapIndexScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				/* no need to fix targetlist and qual */
+				Assert(splan->scan.plan.targetlist == NIL);
+				Assert(splan->scan.plan.qual == NIL);
+				splan->indexqual =
+					fix_scan_list(splan->indexqual, rtoffset);
+				splan->indexqualorig =
+					fix_scan_list(splan->indexqualorig, rtoffset);
+			}
 			break;
 		case T_BitmapHeapScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan,
-						 (Node *) ((BitmapHeapScan *) plan)->bitmapqualorig);
+			{
+				BitmapHeapScan *splan = (BitmapHeapScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+				splan->scan.plan.qual =
+					fix_scan_list(splan->scan.plan.qual, rtoffset);
+				splan->bitmapqualorig =
+					fix_scan_list(splan->bitmapqualorig, rtoffset);
+			}
 			break;
 		case T_TidScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals);
+			{
+				TidScan *splan = (TidScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+				splan->scan.plan.qual =
+					fix_scan_list(splan->scan.plan.qual, rtoffset);
+				splan->tidquals =
+					fix_scan_list(splan->tidquals, rtoffset);
+			}
 			break;
 		case T_SubqueryScan:
 			/* Needs special treatment, see comments below */
-			return set_subqueryscan_references((SubqueryScan *) plan, rtable);
+			return set_subqueryscan_references(glob,
+											   (SubqueryScan *) plan,
+											   rtoffset);
 		case T_FunctionScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, ((FunctionScan *) plan)->funcexpr);
+			{
+				FunctionScan *splan = (FunctionScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+				splan->scan.plan.qual =
+					fix_scan_list(splan->scan.plan.qual, rtoffset);
+				splan->funcexpr =
+					fix_scan_expr(splan->funcexpr, rtoffset);
+			}
 			break;
 		case T_ValuesScan:
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan,
-								(Node *) ((ValuesScan *) plan)->values_lists);
+			{
+				ValuesScan *splan = (ValuesScan *) plan;
+
+				splan->scan.scanrelid += rtoffset;
+				splan->scan.plan.targetlist =
+					fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+				splan->scan.plan.qual =
+					fix_scan_list(splan->scan.plan.qual, rtoffset);
+				splan->values_lists =
+					fix_scan_list(splan->values_lists, rtoffset);
+			}
 			break;
+
 		case T_NestLoop:
-			set_join_references((Join *) plan);
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-			break;
 		case T_MergeJoin:
-			set_join_references((Join *) plan);
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-			fix_expr_references(plan,
-								(Node *) ((MergeJoin *) plan)->mergeclauses);
-			break;
 		case T_HashJoin:
-			set_join_references((Join *) plan);
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-			fix_expr_references(plan,
-								(Node *) ((HashJoin *) plan)->hashclauses);
+			set_join_references((Join *) plan, rtoffset);
 			break;
+
 		case T_Hash:
 		case T_Material:
 		case T_Sort:
@@ -211,75 +312,113 @@ set_plan_references(Plan *plan, List *rtable)
 
 			/*
 			 * These plan types don't actually bother to evaluate their
-			 * targetlists (because they just return their unmodified input
-			 * tuples).  The optimizer is lazy about creating really valid
-			 * targetlists for them --- it tends to just put in a pointer to
-			 * the child plan node's tlist.  Hence, we leave the tlist alone.
-			 * In particular, we do not want to process subplans in the tlist,
-			 * since we will likely end up reprocessing subplans that also
-			 * appear in lower levels of the plan tree!
-			 *
+			 * targetlists, because they just return their unmodified input
+			 * tuples.  Even though the targetlist won't be used by the
+			 * executor, we fix it up for possible use by EXPLAIN (not to
+			 * mention ease of debugging --- wrong varnos are very confusing).
+			 */
+			plan->targetlist =
+				fix_scan_list(plan->targetlist, rtoffset);
+			/*
 			 * Since these plan types don't check quals either, we should not
 			 * find any qual expression attached to them.
 			 */
 			Assert(plan->qual == NIL);
 			break;
 		case T_Limit:
-
-			/*
-			 * Like the plan types above, Limit doesn't evaluate its tlist or
-			 * quals.  It does have live expressions for limit/offset,
-			 * however.
-			 */
-			Assert(plan->qual == NIL);
-			fix_expr_references(plan, ((Limit *) plan)->limitOffset);
-			fix_expr_references(plan, ((Limit *) plan)->limitCount);
+			{
+				Limit *splan = (Limit *) plan;
+
+				/*
+				 * Like the plan types above, Limit doesn't evaluate its tlist
+				 * or quals.  It does have live expressions for limit/offset,
+				 * however.
+				 */
+				splan->plan.targetlist =
+					fix_scan_list(splan->plan.targetlist, rtoffset);
+				Assert(splan->plan.qual == NIL);
+				splan->limitOffset =
+					fix_scan_expr(splan->limitOffset, rtoffset);
+				splan->limitCount =
+					fix_scan_expr(splan->limitCount, rtoffset);
+			}
 			break;
 		case T_Agg:
 		case T_Group:
-			set_uppernode_references(plan, (Index) 0);
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
+			set_upper_references(plan, (Index) 0, rtoffset);
 			break;
 		case T_Result:
-
-			/*
-			 * Result may or may not have a subplan; no need to fix up subplan
-			 * references if it hasn't got one...
-			 *
-			 * XXX why does Result use a different subvarno from Agg/Group?
-			 */
-			if (plan->lefttree != NULL)
-				set_uppernode_references(plan, (Index) OUTER);
-			fix_expr_references(plan, (Node *) plan->targetlist);
-			fix_expr_references(plan, (Node *) plan->qual);
-			fix_expr_references(plan, ((Result *) plan)->resconstantqual);
+			{
+				Result *splan = (Result *) plan;
+
+				/*
+				 * Result may or may not have a subplan; if not, it's more
+				 * like a scan node than an upper node.
+				 *
+				 * XXX why does Result use a different subvarno from Agg/Group?
+				 */
+				if (splan->plan.lefttree != NULL)
+					set_upper_references(plan, (Index) OUTER, rtoffset);
+				else
+				{
+					splan->plan.targetlist =
+						fix_scan_list(splan->plan.targetlist, rtoffset);
+					splan->plan.qual =
+						fix_scan_list(splan->plan.qual, rtoffset);
+				}
+				/* resconstantqual can't contain any subplan variable refs */
+				splan->resconstantqual =
+					fix_scan_expr(splan->resconstantqual, rtoffset);
+			}
 			break;
 		case T_Append:
-
-			/*
-			 * Append, like Sort et al, doesn't actually evaluate its
-			 * targetlist or check quals, and we haven't bothered to give it
-			 * its own tlist copy. So, don't fix targetlist/qual. But do
-			 * recurse into child plans.
-			 */
-			Assert(plan->qual == NIL);
-			foreach(l, ((Append *) plan)->appendplans)
-				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+			{
+				Append *splan = (Append *) plan;
+
+				/*
+				 * Append, like Sort et al, doesn't actually evaluate its
+				 * targetlist or check quals.
+				 */
+				splan->plan.targetlist =
+					fix_scan_list(splan->plan.targetlist, rtoffset);
+				Assert(splan->plan.qual == NIL);
+				foreach(l, splan->appendplans)
+				{
+					lfirst(l) = set_plan_refs(glob,
+											  (Plan *) lfirst(l),
+											  rtoffset);
+				}
+			}
 			break;
 		case T_BitmapAnd:
-			/* BitmapAnd works like Append, but has no tlist */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			foreach(l, ((BitmapAnd *) plan)->bitmapplans)
-				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+			{
+				BitmapAnd *splan = (BitmapAnd *) plan;
+
+				/* BitmapAnd works like Append, but has no tlist */
+				Assert(splan->plan.targetlist == NIL);
+				Assert(splan->plan.qual == NIL);
+				foreach(l, splan->bitmapplans)
+				{
+					lfirst(l) = set_plan_refs(glob,
+											  (Plan *) lfirst(l),
+											  rtoffset);
+				}
+			}
 			break;
 		case T_BitmapOr:
-			/* BitmapOr works like Append, but has no tlist */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			foreach(l, ((BitmapOr *) plan)->bitmapplans)
-				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+			{
+				BitmapOr *splan = (BitmapOr *) plan;
+
+				/* BitmapOr works like Append, but has no tlist */
+				Assert(splan->plan.targetlist == NIL);
+				Assert(splan->plan.qual == NIL);
+				foreach(l, splan->bitmapplans)
+				{
+					lfirst(l) = set_plan_refs(glob,
+											  (Plan *) lfirst(l),
+											  rtoffset);
+				}
+			}
 			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
@@ -288,25 +427,15 @@ set_plan_references(Plan *plan, List *rtable)
 	}
 
 	/*
-	 * Now recurse into child plans and initplans, if any
+	 * Now recurse into child plans, if any
 	 *
 	 * NOTE: it is essential that we recurse into child plans 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 children.
-	 * Fortunately, that consideration doesn't apply to SubPlan nodes; else
-	 * we'd need two passes over the expression trees.
 	 */
-	plan->lefttree = set_plan_references(plan->lefttree, rtable);
-	plan->righttree = set_plan_references(plan->righttree, rtable);
-
-	foreach(l, plan->initPlan)
-	{
-		SubPlan    *sp = (SubPlan *) lfirst(l);
-
-		Assert(IsA(sp, SubPlan));
-		sp->plan = set_plan_references(sp->plan, sp->rtable);
-	}
+	plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset);
+	plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset);
 
 	return plan;
 }
@@ -319,54 +448,29 @@ set_plan_references(Plan *plan, List *rtable)
  * to do the normal processing on it.
  */
 static Plan *
-set_subqueryscan_references(SubqueryScan *plan, List *rtable)
+set_subqueryscan_references(PlannerGlobal *glob,
+							SubqueryScan *plan,
+							int rtoffset)
 {
 	Plan	   *result;
-	RangeTblEntry *rte;
-	ListCell   *l;
 
 	/* First, recursively process the subplan */
-	rte = rt_fetch(plan->scan.scanrelid, rtable);
-	Assert(rte->rtekind == RTE_SUBQUERY);
-	plan->subplan = set_plan_references(plan->subplan,
-										rte->subquery->rtable);
-
-	/*
-	 * We have to process any initplans too; set_plan_references can't do it
-	 * for us because of the possibility of double-processing.
-	 */
-	foreach(l, plan->scan.plan.initPlan)
-	{
-		SubPlan    *sp = (SubPlan *) lfirst(l);
+	plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable);
 
-		Assert(IsA(sp, SubPlan));
-		sp->plan = set_plan_references(sp->plan, sp->rtable);
-	}
+	/* subrtable is no longer needed in the plan tree */
+	plan->subrtable = NIL;
 
 	if (trivial_subqueryscan(plan))
 	{
 		/*
-		 * We can omit the SubqueryScan node and just pull up the subplan. We
-		 * have to merge its rtable into the outer rtable, which means
-		 * adjusting varnos throughout the subtree.
+		 * We can omit the SubqueryScan node and just pull up the subplan.
 		 */
-		int			rtoffset = list_length(rtable);
-		List	   *sub_rtable;
 		ListCell   *lp,
 				   *lc;
 
-		sub_rtable = copyObject(rte->subquery->rtable);
-		rtable = list_concat(rtable, sub_rtable);
-
-		/*
-		 * we have to copy the subplan to make sure there are no duplicately
-		 * linked nodes in it, else adjust_plan_varnos might increment some
-		 * varnos twice
-		 */
-		result = copyObject(plan->subplan);
-
-		adjust_plan_varnos(result, rtoffset);
+		result = plan->subplan;
 
+		/* We have to be sure we don't lose any initplans */
 		result->initPlan = list_concat(plan->scan.plan.initPlan,
 									   result->initPlan);
 
@@ -391,14 +495,17 @@ set_subqueryscan_references(SubqueryScan *plan, List *rtable)
 		/*
 		 * Keep the SubqueryScan node.	We have to do the processing that
 		 * set_plan_references would otherwise have done on it.  Notice we do
-		 * not do set_uppernode_references() here, because a SubqueryScan will
+		 * not do set_upper_references() here, because a SubqueryScan will
 		 * always have been created with correct references to its subplan's
 		 * outputs to begin with.
 		 */
-		result = (Plan *) plan;
+		plan->scan.scanrelid += rtoffset;
+		plan->scan.plan.targetlist =
+			fix_scan_list(plan->scan.plan.targetlist, rtoffset);
+		plan->scan.plan.qual =
+			fix_scan_list(plan->scan.plan.qual, rtoffset);
 
-		fix_expr_references(result, (Node *) result->targetlist);
-		fix_expr_references(result, (Node *) result->qual);
+		result = (Plan *) plan;
 	}
 
 	return result;
@@ -463,261 +570,72 @@ trivial_subqueryscan(SubqueryScan *plan)
 }
 
 /*
- * adjust_plan_varnos
- *		Offset varnos and other rangetable indexes in a plan tree by rtoffset.
- */
-static void
-adjust_plan_varnos(Plan *plan, int rtoffset)
-{
-	ListCell   *l;
-
-	if (plan == NULL)
-		return;
-
-	/*
-	 * Plan-type-specific fixes
-	 */
-	switch (nodeTag(plan))
-	{
-		case T_SeqScan:
-			((SeqScan *) plan)->scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			break;
-		case T_IndexScan:
-			((IndexScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual,
-							   rtoffset);
-			adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig,
-							   rtoffset);
-			break;
-		case T_BitmapIndexScan:
-			((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset;
-			/* no need to fix targetlist and qual */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual,
-							   rtoffset);
-			adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig,
-							   rtoffset);
-			break;
-		case T_BitmapHeapScan:
-			((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
-							   rtoffset);
-			break;
-		case T_TidScan:
-			((TidScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals,
-							   rtoffset);
-			break;
-		case T_SubqueryScan:
-			((SubqueryScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			/* we should not recurse into the subquery! */
-			break;
-		case T_FunctionScan:
-			((FunctionScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos(((FunctionScan *) plan)->funcexpr,
-							   rtoffset);
-			break;
-		case T_ValuesScan:
-			((ValuesScan *) plan)->scan.scanrelid += rtoffset;
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((ValuesScan *) plan)->values_lists,
-							   rtoffset);
-			break;
-		case T_NestLoop:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-			break;
-		case T_MergeJoin:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-			adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses,
-							   rtoffset);
-			break;
-		case T_HashJoin:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-			adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses,
-							   rtoffset);
-			break;
-		case T_Hash:
-		case T_Material:
-		case T_Sort:
-		case T_Unique:
-		case T_SetOp:
-
-			/*
-			 * Even though the targetlist won't be used by the executor, we
-			 * fix it up for possible use by EXPLAIN (not to mention ease of
-			 * debugging --- wrong varnos are very confusing).
-			 */
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			Assert(plan->qual == NIL);
-			break;
-		case T_Limit:
-
-			/*
-			 * Like the plan types above, Limit doesn't evaluate its tlist or
-			 * quals.  It does have live expressions for limit/offset,
-			 * however.
-			 */
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			Assert(plan->qual == NIL);
-			adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset);
-			adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset);
-			break;
-		case T_Agg:
-		case T_Group:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			break;
-		case T_Result:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			adjust_expr_varnos((Node *) plan->qual, rtoffset);
-			adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset);
-			break;
-		case T_Append:
-			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-			Assert(plan->qual == NIL);
-			foreach(l, ((Append *) plan)->appendplans)
-				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-			break;
-		case T_BitmapAnd:
-			/* BitmapAnd works like Append, but has no tlist */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			foreach(l, ((BitmapAnd *) plan)->bitmapplans)
-				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-			break;
-		case T_BitmapOr:
-			/* BitmapOr works like Append, but has no tlist */
-			Assert(plan->targetlist == NIL);
-			Assert(plan->qual == NIL);
-			foreach(l, ((BitmapOr *) plan)->bitmapplans)
-				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-			break;
-		default:
-			elog(ERROR, "unrecognized node type: %d",
-				 (int) nodeTag(plan));
-			break;
-	}
-
-	/*
-	 * Now recurse into child plans.
-	 *
-	 * We don't need to (and in fact mustn't) recurse into subqueries, so no
-	 * need to examine initPlan list.
-	 */
-	adjust_plan_varnos(plan->lefttree, rtoffset);
-	adjust_plan_varnos(plan->righttree, rtoffset);
-}
-
-/*
- * adjust_expr_varnos
- *		Offset varnos of Vars in an expression by rtoffset.
+ * fix_scan_expr
+ *		Do set_plan_references processing on a scan-level expression
  *
- * This is different from the rewriter's OffsetVarNodes in that it has to
- * work on an already-planned expression tree; in particular, we should not
- * disturb INNER and OUTER references.	On the other hand, we don't have to
- * recurse into subqueries nor deal with outer-level Vars, so it's pretty
- * simple.
+ * This consists of incrementing all Vars' varnos by rtoffset and
+ * looking up operator opcode info for OpExpr and related nodes.
  */
-static void
-adjust_expr_varnos(Node *node, int rtoffset)
+static Node *
+fix_scan_expr(Node *node, int rtoffset)
 {
-	/* This tree walk requires no special setup, so away we go... */
-	adjust_expr_varnos_walker(node, &rtoffset);
+	fix_scan_expr_context context;
+
+	context.rtoffset = rtoffset;
+	return fix_scan_expr_mutator(node, &context);
 }
 
-static bool
-adjust_expr_varnos_walker(Node *node, int *context)
+static Node *
+fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
 {
 	if (node == NULL)
-		return false;
+		return NULL;
 	if (IsA(node, Var))
 	{
-		Var		   *var = (Var *) node;
+		Var		   *var = (Var *) copyObject(node);
 
 		Assert(var->varlevelsup == 0);
-		if (var->varno > 0 && var->varno != INNER && var->varno != OUTER)
-			var->varno += *context;
+		/*
+		 * We should not see any Vars marked INNER, but in a nestloop inner
+		 * scan there could be OUTER Vars.  Leave them alone.
+		 */
+		Assert(var->varno != INNER);
+		if (var->varno > 0 && var->varno != OUTER)
+			var->varno += context->rtoffset;
 		if (var->varnoold > 0)
-			var->varnoold += *context;
-		return false;
+			var->varnoold += context->rtoffset;
+		return (Node *) var;
 	}
-	return expression_tree_walker(node, adjust_expr_varnos_walker,
-								  (void *) context);
-}
-
-/*
- * fix_expr_references
- *	  Do final cleanup on expressions (targetlists or quals).
- *
- * This consists of looking up operator opcode info for OpExpr nodes
- * and recursively performing set_plan_references on subplans.
- *
- * The Plan argument is currently unused, but might be needed again someday.
- */
-static void
-fix_expr_references(Plan *plan, Node *node)
-{
-	/* This tree walk requires no special setup, so away we go... */
-	fix_expr_references_walker(node, NULL);
-}
-
-static bool
-fix_expr_references_walker(Node *node, void *context)
-{
-	if (node == NULL)
-		return false;
+	/*
+	 * Since we update opcode info in-place, this part could possibly
+	 * scribble on the planner's input data structures, but it's OK.
+	 */
 	if (IsA(node, OpExpr))
 		set_opfuncid((OpExpr *) node);
 	else if (IsA(node, DistinctExpr))
 		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
-	else if (IsA(node, ScalarArrayOpExpr))
-		set_sa_opfuncid((ScalarArrayOpExpr *) node);
 	else if (IsA(node, NullIfExpr))
 		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
-	else if (IsA(node, SubPlan))
-	{
-		SubPlan    *sp = (SubPlan *) node;
-
-		sp->plan = set_plan_references(sp->plan, sp->rtable);
-	}
-	return expression_tree_walker(node, fix_expr_references_walker, context);
+	else if (IsA(node, ScalarArrayOpExpr))
+		set_sa_opfuncid((ScalarArrayOpExpr *) node);
+	return expression_tree_mutator(node, fix_scan_expr_mutator,
+								   (void *) context);
 }
 
 /*
  * set_join_references
- *	  Modifies the target list and quals of a join node to reference its
+ *	  Modify the target list and quals 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.
+ *	  or inner join tuple item.  Also perform opcode lookup for these
+ *	  expressions.
  *
  * In the case of a nestloop with inner indexscan, we will also need to
  * apply the same transformation to any outer vars appearing in the
  * quals of the child indexscan.  set_inner_join_references does that.
- *
- *	'join' is a join plan node
  */
 static void
-set_join_references(Join *join)
+set_join_references(Join *join, int rtoffset)
 {
 	Plan	   *outer_plan = join->plan.lefttree;
 	Plan	   *inner_plan = join->plan.righttree;
@@ -728,43 +646,47 @@ set_join_references(Join *join)
 	inner_itlist = build_tlist_index(inner_plan->targetlist);
 
 	/* All join plans have tlist, qual, and joinqual */
-	join->plan.targetlist = join_references(join->plan.targetlist,
-											outer_itlist,
-											inner_itlist,
-											(Index) 0);
-	join->plan.qual = join_references(join->plan.qual,
-									  outer_itlist,
-									  inner_itlist,
-									  (Index) 0);
-	join->joinqual = join_references(join->joinqual,
-									 outer_itlist,
-									 inner_itlist,
-									 (Index) 0);
+	join->plan.targetlist = fix_join_expr(join->plan.targetlist,
+										  outer_itlist,
+										  inner_itlist,
+										  (Index) 0,
+										  rtoffset);
+	join->plan.qual = fix_join_expr(join->plan.qual,
+									outer_itlist,
+									inner_itlist,
+									(Index) 0,
+									rtoffset);
+	join->joinqual = fix_join_expr(join->joinqual,
+								   outer_itlist,
+								   inner_itlist,
+								   (Index) 0,
+								   rtoffset);
 
 	/* Now do join-type-specific stuff */
 	if (IsA(join, NestLoop))
 	{
 		/* This processing is split out to handle possible recursion */
-		set_inner_join_references(inner_plan,
-								  outer_itlist);
+		set_inner_join_references(inner_plan, outer_itlist);
 	}
 	else if (IsA(join, MergeJoin))
 	{
 		MergeJoin  *mj = (MergeJoin *) join;
 
-		mj->mergeclauses = join_references(mj->mergeclauses,
-										   outer_itlist,
-										   inner_itlist,
-										   (Index) 0);
+		mj->mergeclauses = fix_join_expr(mj->mergeclauses,
+										 outer_itlist,
+										 inner_itlist,
+										 (Index) 0,
+										 rtoffset);
 	}
 	else if (IsA(join, HashJoin))
 	{
 		HashJoin   *hj = (HashJoin *) join;
 
-		hj->hashclauses = join_references(hj->hashclauses,
-										  outer_itlist,
-										  inner_itlist,
-										  (Index) 0);
+		hj->hashclauses = fix_join_expr(hj->hashclauses,
+										outer_itlist,
+										inner_itlist,
+										(Index) 0,
+										rtoffset);
 	}
 
 	pfree(outer_itlist);
@@ -779,6 +701,10 @@ set_join_references(Join *join)
  * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans
  * require recursing through Append nodes.	This is split out as a separate
  * function so that it can recurse.
+ *
+ * Note we do *not* apply any rtoffset for non-join Vars; this is because
+ * the quals will be processed again by fix_scan_expr when the set_plan_refs
+ * recursion reaches the inner indexscan, and so we'd have done it twice.
  */
 static void
 set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
@@ -800,14 +726,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 			Index		innerrel = innerscan->scan.scanrelid;
 
 			/* only refs to outer vars get changed in the inner qual */
-			innerscan->indexqualorig = join_references(indexqualorig,
-													   outer_itlist,
-													   NULL,
-													   innerrel);
-			innerscan->indexqual = join_references(innerscan->indexqual,
-												   outer_itlist,
-												   NULL,
-												   innerrel);
+			innerscan->indexqualorig = fix_join_expr(indexqualorig,
+													 outer_itlist,
+													 NULL,
+													 innerrel,
+													 0);
+			innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+												 outer_itlist,
+												 NULL,
+												 innerrel,
+												 0);
 
 			/*
 			 * We must fix the inner qpqual too, if it has join clauses (this
@@ -815,10 +743,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 			 * may get rechecked as qpquals).
 			 */
 			if (NumRelids((Node *) inner_plan->qual) > 1)
-				inner_plan->qual = join_references(inner_plan->qual,
-												   outer_itlist,
-												   NULL,
-												   innerrel);
+				inner_plan->qual = fix_join_expr(inner_plan->qual,
+												 outer_itlist,
+												 NULL,
+												 innerrel,
+												 0);
 		}
 	}
 	else if (IsA(inner_plan, BitmapIndexScan))
@@ -835,14 +764,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 			Index		innerrel = innerscan->scan.scanrelid;
 
 			/* only refs to outer vars get changed in the inner qual */
-			innerscan->indexqualorig = join_references(indexqualorig,
-													   outer_itlist,
-													   NULL,
-													   innerrel);
-			innerscan->indexqual = join_references(innerscan->indexqual,
-												   outer_itlist,
-												   NULL,
-												   innerrel);
+			innerscan->indexqualorig = fix_join_expr(indexqualorig,
+													 outer_itlist,
+													 NULL,
+													 innerrel,
+													 0);
+			innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+												 outer_itlist,
+												 NULL,
+												 innerrel,
+												 0);
 			/* no need to fix inner qpqual */
 			Assert(inner_plan->qual == NIL);
 		}
@@ -862,10 +793,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
 		/* only refs to outer vars get changed in the inner qual */
 		if (NumRelids((Node *) bitmapqualorig) > 1)
-			innerscan->bitmapqualorig = join_references(bitmapqualorig,
-														outer_itlist,
-														NULL,
-														innerrel);
+			innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig,
+													  outer_itlist,
+													  NULL,
+													  innerrel,
+													  0);
 
 		/*
 		 * We must fix the inner qpqual too, if it has join clauses (this
@@ -873,14 +805,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 		 * get rechecked as qpquals).
 		 */
 		if (NumRelids((Node *) inner_plan->qual) > 1)
-			inner_plan->qual = join_references(inner_plan->qual,
-											   outer_itlist,
-											   NULL,
-											   innerrel);
+			inner_plan->qual = fix_join_expr(inner_plan->qual,
+											 outer_itlist,
+											 NULL,
+											 innerrel,
+											 0);
 
 		/* Now recurse */
-		set_inner_join_references(inner_plan->lefttree,
-								  outer_itlist);
+		set_inner_join_references(inner_plan->lefttree, outer_itlist);
 	}
 	else if (IsA(inner_plan, BitmapAnd))
 	{
@@ -890,8 +822,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
 		foreach(l, innerscan->bitmapplans)
 		{
-			set_inner_join_references((Plan *) lfirst(l),
-									  outer_itlist);
+			set_inner_join_references((Plan *) lfirst(l), outer_itlist);
 		}
 	}
 	else if (IsA(inner_plan, BitmapOr))
@@ -902,10 +833,20 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
 		foreach(l, innerscan->bitmapplans)
 		{
-			set_inner_join_references((Plan *) lfirst(l),
-									  outer_itlist);
+			set_inner_join_references((Plan *) lfirst(l), outer_itlist);
 		}
 	}
+	else if (IsA(inner_plan, TidScan))
+	{
+		TidScan    *innerscan = (TidScan *) inner_plan;
+		Index		innerrel = innerscan->scan.scanrelid;
+
+		innerscan->tidquals = fix_join_expr(innerscan->tidquals,
+											outer_itlist,
+											NULL,
+											innerrel,
+											0);
+	}
 	else if (IsA(inner_plan, Append))
 	{
 		/*
@@ -917,8 +858,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
 		foreach(l, appendplan->appendplans)
 		{
-			set_inner_join_references((Plan *) lfirst(l),
-									  outer_itlist);
+			set_inner_join_references((Plan *) lfirst(l), outer_itlist);
 		}
 	}
 	else if (IsA(inner_plan, Result))
@@ -929,22 +869,13 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 		if (result->plan.lefttree)
 			set_inner_join_references(result->plan.lefttree, outer_itlist);
 	}
-	else if (IsA(inner_plan, TidScan))
-	{
-		TidScan    *innerscan = (TidScan *) inner_plan;
-		Index		innerrel = innerscan->scan.scanrelid;
-
-		innerscan->tidquals = join_references(innerscan->tidquals,
-											  outer_itlist,
-											  NULL,
-											  innerrel);
-	}
 }
 
 /*
- * set_uppernode_references
+ * set_upper_references
  *	  Update the targetlist and quals of an upper-level plan node
  *	  to refer to the tuples returned by its lefttree subplan.
+ *	  Also perform opcode lookup for these expressions.
  *
  * This is used for single-input plan types like Agg, Group, Result.
  *
@@ -958,7 +889,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
  * the expression.
  */
 static void
-set_uppernode_references(Plan *plan, Index subvarno)
+set_upper_references(Plan *plan, Index subvarno, int rtoffset)
 {
 	Plan	   *subplan = plan->lefttree;
 	indexed_tlist *subplan_itlist;
@@ -976,9 +907,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
 		TargetEntry *tle = (TargetEntry *) lfirst(l);
 		Node	   *newexpr;
 
-		newexpr = replace_vars_with_subplan_refs((Node *) tle->expr,
-												 subplan_itlist,
-												 subvarno);
+		newexpr = fix_upper_expr((Node *) tle->expr,
+								 subplan_itlist,
+								 subvarno,
+								 rtoffset);
 		tle = flatCopyTargetEntry(tle);
 		tle->expr = (Expr *) newexpr;
 		output_targetlist = lappend(output_targetlist, tle);
@@ -986,9 +918,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
 	plan->targetlist = output_targetlist;
 
 	plan->qual = (List *)
-		replace_vars_with_subplan_refs((Node *) plan->qual,
-									   subplan_itlist,
-									   subvarno);
+		fix_upper_expr((Node *) plan->qual,
+					   subplan_itlist,
+					   subvarno,
+					   rtoffset);
 
 	pfree(subplan_itlist);
 }
@@ -1096,10 +1029,12 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
  *
  * If a match is found, return a copy of the given Var with suitably
  * modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
+ * Also ensure that varnoold is incremented by rtoffset.
  * If no match, return NULL.
  */
 static Var *
-search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
+search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
+							 Index newvarno, int rtoffset)
 {
 	Index		varno = var->varno;
 	AttrNumber	varattno = var->varattno;
@@ -1117,6 +1052,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
 
 			newvar->varno = newvarno;
 			newvar->varattno = vinfo->resno;
+			if (newvar->varnoold > 0)
+				newvar->varnoold += rtoffset;
 			return newvar;
 		}
 		vinfo++;
@@ -1157,22 +1094,23 @@ search_indexed_tlist_for_non_var(Node *node,
 }
 
 /*
- * join_references
- *	   Creates a new set of targetlist entries or join qual clauses by
+ * fix_join_expr
+ *	   Create 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.
+ *	   relation target lists.  Also perform opcode lookup.
  *
  * 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.  (We also
- * implement RETURNING clause fixup using this second scenario.)
+ * references, while Vars of the inner relation should be adjusted by rtoffset.
+ * (We also implement RETURNING clause fixup using this second scenario.)
  *
  * 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_itlist = NULL and acceptable_rel = the ID of the inner relation.
+ * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID
+ * of the inner relation.
  *
  * 'clauses' is the targetlist or list of join clauses
  * 'outer_itlist' is the indexed target list of the outer join relation
@@ -1180,27 +1118,29 @@ search_indexed_tlist_for_non_var(Node *node,
  *		or NULL
  * 'acceptable_rel' is either zero or the rangetable index of a relation
  *		whose Vars may appear in the clause without provoking an error.
+ * 'rtoffset' is what to add to varno for Vars of acceptable_rel.
  *
  * Returns the new expression tree.  The original clause structure is
  * not modified.
  */
 static List *
-join_references(List *clauses,
-				indexed_tlist *outer_itlist,
-				indexed_tlist *inner_itlist,
-				Index acceptable_rel)
+fix_join_expr(List *clauses,
+			  indexed_tlist *outer_itlist,
+			  indexed_tlist *inner_itlist,
+			  Index acceptable_rel,
+			  int rtoffset)
 {
-	join_references_context context;
+	fix_join_expr_context context;
 
 	context.outer_itlist = outer_itlist;
 	context.inner_itlist = inner_itlist;
 	context.acceptable_rel = acceptable_rel;
-	return (List *) join_references_mutator((Node *) clauses, &context);
+	context.rtoffset = rtoffset;
+	return (List *) fix_join_expr_mutator((Node *) clauses, &context);
 }
 
 static Node *
-join_references_mutator(Node *node,
-						join_references_context *context)
+fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 {
 	Var		   *newvar;
 
@@ -1213,21 +1153,28 @@ join_references_mutator(Node *node,
 		/* First look for the var in the input tlists */
 		newvar = search_indexed_tlist_for_var(var,
 											  context->outer_itlist,
-											  OUTER);
+											  OUTER,
+											  context->rtoffset);
 		if (newvar)
 			return (Node *) newvar;
 		if (context->inner_itlist)
 		{
 			newvar = search_indexed_tlist_for_var(var,
 												  context->inner_itlist,
-												  INNER);
+												  INNER,
+												  context->rtoffset);
 			if (newvar)
 				return (Node *) newvar;
 		}
 
-		/* Return the Var unmodified, if it's for acceptable_rel */
+		/* If it's for acceptable_rel, adjust and return it */
 		if (var->varno == context->acceptable_rel)
-			return (Node *) copyObject(var);
+		{
+			var = (Var *) copyObject(var);
+			var->varno += context->rtoffset;
+			var->varnoold += context->rtoffset;
+			return (Node *) var;
+		}
 
 		/* No referent found for Var */
 		elog(ERROR, "variable not found in subplan target lists");
@@ -1249,16 +1196,30 @@ join_references_mutator(Node *node,
 		if (newvar)
 			return (Node *) newvar;
 	}
+	/*
+	 * Since we update opcode info in-place, this part could possibly
+	 * scribble on the planner's input data structures, but it's OK.
+	 */
+	if (IsA(node, OpExpr))
+		set_opfuncid((OpExpr *) node);
+	else if (IsA(node, DistinctExpr))
+		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
+	else if (IsA(node, NullIfExpr))
+		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
+	else if (IsA(node, ScalarArrayOpExpr))
+		set_sa_opfuncid((ScalarArrayOpExpr *) node);
 	return expression_tree_mutator(node,
-								   join_references_mutator,
+								   fix_join_expr_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.
+ * fix_upper_expr
+ *		Modifies an expression tree so that all Var nodes reference outputs
+ *		of a subplan.  Also performs opcode lookup.
+ *
+ * This is used to fix up target and qual expressions of non-join 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'
@@ -1273,26 +1234,28 @@ join_references_mutator(Node *node,
  * 'node': the tree to be fixed (a target item or qual)
  * 'subplan_itlist': indexed target list for subplan
  * 'subvarno': varno to be assigned to all Vars
+ * 'rtoffset': how much to increment varnoold by
  *
  * 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 Node *
-replace_vars_with_subplan_refs(Node *node,
-							   indexed_tlist *subplan_itlist,
-							   Index subvarno)
+fix_upper_expr(Node *node,
+			   indexed_tlist *subplan_itlist,
+			   Index subvarno,
+			   int rtoffset)
 {
-	replace_vars_with_subplan_refs_context context;
+	fix_upper_expr_context context;
 
 	context.subplan_itlist = subplan_itlist;
 	context.subvarno = subvarno;
-	return replace_vars_with_subplan_refs_mutator(node, &context);
+	context.rtoffset = rtoffset;
+	return fix_upper_expr_mutator(node, &context);
 }
 
 static Node *
-replace_vars_with_subplan_refs_mutator(Node *node,
-							 replace_vars_with_subplan_refs_context *context)
+fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 {
 	Var		   *newvar;
 
@@ -1304,7 +1267,8 @@ replace_vars_with_subplan_refs_mutator(Node *node,
 
 		newvar = search_indexed_tlist_for_var(var,
 											  context->subplan_itlist,
-											  context->subvarno);
+											  context->subvarno,
+											  context->rtoffset);
 		if (!newvar)
 			elog(ERROR, "variable not found in subplan target list");
 		return (Node *) newvar;
@@ -1318,8 +1282,20 @@ replace_vars_with_subplan_refs_mutator(Node *node,
 		if (newvar)
 			return (Node *) newvar;
 	}
+	/*
+	 * Since we update opcode info in-place, this part could possibly
+	 * scribble on the planner's input data structures, but it's OK.
+	 */
+	if (IsA(node, OpExpr))
+		set_opfuncid((OpExpr *) node);
+	else if (IsA(node, DistinctExpr))
+		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
+	else if (IsA(node, NullIfExpr))
+		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
+	else if (IsA(node, ScalarArrayOpExpr))
+		set_sa_opfuncid((ScalarArrayOpExpr *) node);
 	return expression_tree_mutator(node,
-								   replace_vars_with_subplan_refs_mutator,
+								   fix_upper_expr_mutator,
 								   (void *) context);
 }
 
@@ -1335,12 +1311,15 @@ replace_vars_with_subplan_refs_mutator(Node *node,
  * adjusted RETURNING list, result-table Vars will still have their
  * original varno, but Vars for other rels will have varno OUTER.
  *
- * We also must apply fix_expr_references to the list.
+ * We also must perform opcode lookup.
  *
  * 'rlist': the RETURNING targetlist to be fixed
  * 'topplan': the top Plan node for the query (not yet passed through
  *		set_plan_references)
- * 'resultRelation': RT index of the query's result relation
+ * 'resultRelation': RT index of the associated result relation
+ *
+ * Note: we assume that result relations will have rtoffset zero, that is,
+ * they are not coming from a subplan.
  */
 List *
 set_returning_clause_references(List *rlist,
@@ -1350,20 +1329,19 @@ set_returning_clause_references(List *rlist,
 	indexed_tlist *itlist;
 
 	/*
-	 * We can perform the desired Var fixup by abusing the join_references
+	 * We can perform the desired Var fixup by abusing the fix_join_expr
 	 * machinery that normally handles inner indexscan fixup.  We search the
 	 * top plan's targetlist for Vars of non-result relations, and use
-	 * join_references to convert RETURNING Vars into references to those
+	 * fix_join_expr to convert RETURNING Vars into references to those
 	 * tlist entries, while leaving result-rel Vars as-is.
 	 */
 	itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
 
-	rlist = join_references(rlist,
-							itlist,
-							NULL,
-							resultRelation);
-
-	fix_expr_references(topplan, (Node *) rlist);
+	rlist = fix_join_expr(rlist,
+						  itlist,
+						  NULL,
+						  resultRelation,
+						  0);
 
 	pfree(itlist);
 
@@ -1399,10 +1377,10 @@ fix_opfuncids_walker(Node *node, void *context)
 		set_opfuncid((OpExpr *) node);
 	else if (IsA(node, DistinctExpr))
 		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
-	else if (IsA(node, ScalarArrayOpExpr))
-		set_sa_opfuncid((ScalarArrayOpExpr *) node);
 	else if (IsA(node, NullIfExpr))
 		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
+	else if (IsA(node, ScalarArrayOpExpr))
+		set_sa_opfuncid((ScalarArrayOpExpr *) node);
 	return expression_tree_walker(node, fix_opfuncids_walker, context);
 }
 
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index b62aeb853d85e0e4e460c1e8cb696ff3afe6a1cc..d19e9d298c01cbd632811cdb6d6f08e4a7ada497 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.120 2007/02/19 07:03:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.121 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,6 +46,7 @@ typedef struct process_sublinks_context
 
 typedef struct finalize_primnode_context
 {
+	PlannerInfo *root;
 	Bitmapset  *paramids;		/* Set of PARAM_EXEC paramids found */
 	Bitmapset  *outer_params;	/* Set of accessible outer paramids */
 } finalize_primnode_context;
@@ -57,12 +58,13 @@ static Node *convert_testexpr(PlannerInfo *root,
 							  List **righthandIds);
 static Node *convert_testexpr_mutator(Node *node,
 						 convert_testexpr_context *context);
-static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
+static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan);
 static bool hash_ok_operator(OpExpr *expr);
 static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
 static Node *process_sublinks_mutator(Node *node,
 									  process_sublinks_context *context);
-static Bitmapset *finalize_plan(Plan *plan, List *rtable,
+static Bitmapset *finalize_plan(PlannerInfo *root,
+			  Plan *plan,
 			  Bitmapset *outer_params,
 			  Bitmapset *valid_params);
 static bool finalize_primnode(Node *node, finalize_primnode_context *context);
@@ -203,6 +205,24 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
 	return retval;
 }
 
+/*
+ * Get the datatype of the first column of the plan's output.
+ *
+ * This is a hack to support exprType(), which doesn't have any way to get
+ * at the plan associated with a SubPlan node.  We really only need the value
+ * for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we set
+ * it always.
+ */
+static Oid
+get_first_col_type(Plan *plan)
+{
+	TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist);
+
+	Assert(IsA(tent, TargetEntry));
+	Assert(!tent->resjunk);
+	return exprType((Node *) tent->expr);
+}
+
 /*
  * Convert a SubLink (as created by the parser) into a SubPlan.
  *
@@ -219,10 +239,11 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
 static Node *
 make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 {
-	SubPlan    *node = makeNode(SubPlan);
 	Query	   *subquery = (Query *) (slink->subselect);
 	double		tuple_fraction;
+	SubPlan    *node;
 	Plan	   *plan;
+	PlannerInfo *subroot;
 	Bitmapset  *tmpset;
 	int			paramid;
 	Node	   *result;
@@ -266,22 +287,19 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 	/*
 	 * Generate the plan for the subquery.
 	 */
-	node->plan = plan = subquery_planner(root->glob, subquery,
-										 root->query_level + 1,
-										 tuple_fraction,
-										 NULL);
-
-	/* Assign quasi-unique ID to this SubPlan */
-	node->plan_id = root->glob->next_plan_id++;
-
-	node->rtable = subquery->rtable;
+	plan = subquery_planner(root->glob, subquery,
+							root->query_level + 1,
+							tuple_fraction,
+							&subroot);
 
 	/*
-	 * Initialize other fields of the SubPlan node.
+	 * Initialize the SubPlan node.  Note plan_id isn't set yet.
 	 */
+	node = makeNode(SubPlan);
 	node->subLinkType = slink->subLinkType;
 	node->testexpr = NULL;
 	node->paramIds = NIL;
+	node->firstColType = get_first_col_type(plan);
 	node->useHashTable = false;
 	/* At top level of a qual, can treat UNKNOWN the same as FALSE */
 	node->unknownEqFalse = isTopQual;
@@ -384,7 +402,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 		 * tuple.  But if it's an IN (= ANY) test, we might be able to use a
 		 * hashtable to avoid comparing all the tuples.
 		 */
-		if (subplan_is_hashable(slink, node))
+		if (subplan_is_hashable(slink, node, plan))
 			node->useHashTable = true;
 
 		/*
@@ -411,7 +429,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 					break;
 			}
 			if (use_material)
-				node->plan = plan = materialize_finished_plan(plan);
+				plan = materialize_finished_plan(plan);
 		}
 
 		/*
@@ -435,6 +453,15 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 		result = (Node *) node;
 	}
 
+	/*
+	 * Add the subplan and its rtable to the global lists.
+	 */
+	root->glob->subplans = lappend(root->glob->subplans,
+								   plan);
+	root->glob->subrtables = lappend(root->glob->subrtables,
+									 subroot->parse->rtable);
+	node->plan_id = list_length(root->glob->subplans);
+
 	return result;
 }
 
@@ -539,7 +566,7 @@ convert_testexpr_mutator(Node *node,
  * on its plan and parParam fields, however.
  */
 static bool
-subplan_is_hashable(SubLink *slink, SubPlan *node)
+subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan)
 {
 	double		subquery_size;
 	ListCell   *l;
@@ -574,8 +601,8 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
 	 * actually be stored as MinimalTuples; this provides some fudge factor
 	 * for hashtable overhead.)
 	 */
-	subquery_size = node->plan->plan_rows *
-		(MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));
+	subquery_size = plan->plan_rows *
+		(MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));
 	if (subquery_size > work_mem * 1024L)
 		return false;
 
@@ -964,7 +991,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
 	/*
 	 * Now recurse through plan tree.
 	 */
-	(void) finalize_plan(plan, root->parse->rtable, outer_params, valid_params);
+	(void) finalize_plan(root, plan, outer_params, valid_params);
 
 	bms_free(outer_params);
 	bms_free(valid_params);
@@ -988,16 +1015,16 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
 	initplan_cost = 0;
 	foreach(l, plan->initPlan)
 	{
-		SubPlan    *initplan = (SubPlan *) lfirst(l);
+		SubPlan    *initsubplan = (SubPlan *) lfirst(l);
+		Plan	   *initplan = planner_subplan_get_plan(root, initsubplan);
 		ListCell   *l2;
 
-		initExtParam = bms_add_members(initExtParam,
-									   initplan->plan->extParam);
-		foreach(l2, initplan->setParam)
+		initExtParam = bms_add_members(initExtParam, initplan->extParam);
+		foreach(l2, initsubplan->setParam)
 		{
 			initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
 		}
-		initplan_cost += initplan->plan->total_cost;
+		initplan_cost += initplan->total_cost;
 	}
 	/* allParam must include all these params */
 	plan->allParam = bms_add_members(plan->allParam, initExtParam);
@@ -1019,7 +1046,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
  * This is just an internal notational convenience.
  */
 static Bitmapset *
-finalize_plan(Plan *plan, List *rtable,
+finalize_plan(PlannerInfo *root, Plan *plan,
 			  Bitmapset *outer_params, Bitmapset *valid_params)
 {
 	finalize_primnode_context context;
@@ -1027,6 +1054,7 @@ finalize_plan(Plan *plan, List *rtable,
 	if (plan == NULL)
 		return NULL;
 
+	context.root = root;
 	context.paramids = NULL;	/* initialize set to empty */
 	context.outer_params = outer_params;
 
@@ -1110,8 +1138,8 @@ finalize_plan(Plan *plan, List *rtable,
 				{
 					context.paramids =
 						bms_add_members(context.paramids,
-										finalize_plan((Plan *) lfirst(l),
-													  rtable,
+										finalize_plan(root,
+													  (Plan *) lfirst(l),
 													  outer_params,
 													  valid_params));
 				}
@@ -1126,8 +1154,8 @@ finalize_plan(Plan *plan, List *rtable,
 				{
 					context.paramids =
 						bms_add_members(context.paramids,
-										finalize_plan((Plan *) lfirst(l),
-													  rtable,
+										finalize_plan(root,
+													  (Plan *) lfirst(l),
 													  outer_params,
 													  valid_params));
 				}
@@ -1142,8 +1170,8 @@ finalize_plan(Plan *plan, List *rtable,
 				{
 					context.paramids =
 						bms_add_members(context.paramids,
-										finalize_plan((Plan *) lfirst(l),
-													  rtable,
+										finalize_plan(root,
+													  (Plan *) lfirst(l),
 													  outer_params,
 													  valid_params));
 				}
@@ -1193,14 +1221,14 @@ finalize_plan(Plan *plan, List *rtable,
 
 	/* Process left and right child plans, if any */
 	context.paramids = bms_add_members(context.paramids,
-									   finalize_plan(plan->lefttree,
-													 rtable,
+									   finalize_plan(root,
+													 plan->lefttree,
 													 outer_params,
 													 valid_params));
 
 	context.paramids = bms_add_members(context.paramids,
-									   finalize_plan(plan->righttree,
-													 rtable,
+									   finalize_plan(root,
+													 plan->righttree,
 													 outer_params,
 													 valid_params));
 
@@ -1252,10 +1280,11 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
 	if (is_subplan(node))
 	{
 		SubPlan    *subplan = (SubPlan *) node;
+		Plan	   *plan = planner_subplan_get_plan(context->root, subplan);
 
 		/* Add outer-level params needed by the subplan to paramids */
 		context->paramids = bms_join(context->paramids,
-									 bms_intersect(subplan->plan->extParam,
+									 bms_intersect(plan->extParam,
 												   context->outer_params));
 		/* fall through to recurse into subplan args */
 	}
@@ -1299,16 +1328,21 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
 	root->query_level--;
 	root->init_plans = saved_init_plans;
 
+	/*
+	 * Add the subplan and its rtable to the global lists.
+	 */
+	root->glob->subplans = lappend(root->glob->subplans,
+								   plan);
+	root->glob->subrtables = lappend(root->glob->subrtables,
+									 root->parse->rtable);
+
 	/*
 	 * Create a SubPlan node and add it to the outer list of InitPlans.
 	 */
 	node = makeNode(SubPlan);
 	node->subLinkType = EXPR_SUBLINK;
-	node->plan = plan;
-	/* Assign quasi-unique ID to this SubPlan */
-	node->plan_id = root->glob->next_plan_id++;
-
-	node->rtable = root->parse->rtable;
+	node->firstColType = get_first_col_type(plan);
+	node->plan_id = list_length(root->glob->subplans);
 
 	root->init_plans = lappend(root->init_plans, node);
 
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 2f06e1bf5322dfe3a03c14d310c9dbc03d964590..f09ddb1d23d1111205017deea44617f66fef7be0 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.138 2007/02/19 07:03:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.139 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,6 +169,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		RangeTblRef *rtr = (RangeTblRef *) setOp;
 		RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable);
 		Query	   *subquery = rte->subquery;
+		PlannerInfo *subroot;
 		Plan	   *subplan,
 				   *plan;
 
@@ -180,7 +181,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		subplan = subquery_planner(root->glob, subquery,
 								   root->query_level + 1,
 								   tuple_fraction,
-								   NULL);
+								   &subroot);
 
 		/*
 		 * Add a SubqueryScan with the caller-requested targetlist
@@ -193,7 +194,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 												   refnames_tlist),
 							  NIL,
 							  rtr->rtindex,
-							  subplan);
+							  subplan,
+							  subroot->parse->rtable);
 
 		/*
 		 * We don't bother to determine the subquery's output ordering since
@@ -223,7 +225,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		 * output columns.
 		 *
 		 * XXX you don't really want to know about this: setrefs.c will apply
-		 * replace_vars_with_subplan_refs() to the Result node's tlist. This
+		 * fix_upper_expr() to the Result node's tlist. This
 		 * would fail if the Vars generated by generate_setop_tlist() were not
 		 * exactly equal() to the corresponding tlist entries of the subplan.
 		 * However, since the subplan was generated by generate_union_plan()
@@ -235,7 +237,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 			!tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
 		{
 			plan = (Plan *)
-				make_result(generate_setop_tlist(colTypes, flag,
+				make_result(root,
+							generate_setop_tlist(colTypes, flag,
 												 0,
 												 false,
 												 plan->targetlist,
@@ -1216,28 +1219,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
 	Assert(!IsA(node, SubLink));
 	Assert(!IsA(node, Query));
 
-	/*
-	 * BUT: although we don't need to recurse into subplans, we do need to
-	 * make sure that they are copied, not just referenced as
-	 * expression_tree_mutator will do by default.	Otherwise we'll have the
-	 * same subplan node referenced from each arm of the finished APPEND plan,
-	 * which will cause trouble in the executor.  This is a kluge that should
-	 * go away when we redesign querytrees.
-	 */
-	if (is_subplan(node))
-	{
-		SubPlan    *subplan;
-
-		/* Copy the node and process subplan args */
-		node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
-									   (void *) context);
-		/* Make sure we have separate copies of subplan and its rtable */
-		subplan = (SubPlan *) node;
-		subplan->plan = copyObject(subplan->plan);
-		subplan->rtable = copyObject(subplan->rtable);
-		return node;
-	}
-
 	return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
 								   (void *) context);
 }
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f90d55001979b65c15f8fdb222473cc349e83a1e..852b0f533e75d4251e128bb5aae0a3eb3e05b631 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.235 2007/02/19 07:03:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.236 2007/02/22 22:00:24 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2989,7 +2989,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
 			 */
 			if (contain_subplans(param))
 				goto fail;
-			cost_qual_eval(&eval_cost, list_make1(param));
+			cost_qual_eval(&eval_cost, list_make1(param), NULL);
 			if (eval_cost.startup + eval_cost.per_tuple >
 				10 * cpu_operator_cost)
 				goto fail;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 8cc6c81746f3db605c323198a344483d1c2c2e69..6dfd2f61149e71c2ef87ec965b64a1b850e04aea 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.85 2007/01/20 20:45:40 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.86 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,6 +82,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	rel->pages = 0;
 	rel->tuples = 0;
 	rel->subplan = NULL;
+	rel->subrtable = NIL;
 	rel->baserestrictinfo = NIL;
 	rel->baserestrictcost.startup = 0;
 	rel->baserestrictcost.per_tuple = 0;
@@ -333,6 +334,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->pages = 0;
 	joinrel->tuples = 0;
 	joinrel->subplan = NULL;
+	joinrel->subrtable = NIL;
 	joinrel->baserestrictinfo = NIL;
 	joinrel->baserestrictcost.startup = 0;
 	joinrel->baserestrictcost.per_tuple = 0;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8f34748f9987372ab03f8e52116d3bf428fc0592..5791215a95f29589e5ab9375498d0a6e3f182c61 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.211 2007/02/11 22:18:15 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.212 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1769,12 +1769,7 @@ exprType(Node *expr)
 					subplan->subLinkType == ARRAY_SUBLINK)
 				{
 					/* get the type of the subselect's first target column */
-					TargetEntry *tent;
-
-					tent = (TargetEntry *) linitial(subplan->plan->targetlist);
-					Assert(IsA(tent, TargetEntry));
-					Assert(!tent->resjunk);
-					type = exprType((Node *) tent->expr);
+					type = subplan->firstColType;
 					if (subplan->subLinkType == ARRAY_SUBLINK)
 					{
 						type = get_array_type(type);
@@ -1782,7 +1777,7 @@ exprType(Node *expr)
 							ereport(ERROR,
 									(errcode(ERRCODE_UNDEFINED_OBJECT),
 									 errmsg("could not find array type for data type %s",
-							format_type_be(exprType((Node *) tent->expr)))));
+											format_type_be(subplan->firstColType))));
 					}
 				}
 				else
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c6b8e8481c498d822de7700ca293713910021716..f25a8d8f70de62575cec804b5bedd90b5efdbb6b 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.226 2007/02/19 07:03:31 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.227 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4964,7 +4964,7 @@ genericcostestimate(PlannerInfo *root,
 	 * more expensive than it's worth, though, considering all the other
 	 * inaccuracies here ...
 	 */
-	cost_qual_eval(&index_qual_cost, indexQuals);
+	cost_qual_eval(&index_qual_cost, indexQuals, root);
 	qual_op_cost = cpu_operator_cost * list_length(indexQuals);
 	qual_arg_cost = index_qual_cost.startup +
 		index_qual_cost.per_tuple - qual_op_cost;
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 38260b5ecd6c211dcd9b18ed590f688b0d04e1ee..7dfcabf5ac28227979700e7794a3baa71587a6bb 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.138 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,7 +130,6 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
 			ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
-extern void ExecCheckRTPerms(List *rangeTable);
 extern void ExecEndPlan(PlanState *planstate, EState *estate);
 extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b576f4610e3e75e7b7e84de5bb6b52b676db18ee..d0d47a8fd2b1ffce3a43753d14165fcff5d0002f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.169 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -302,7 +302,7 @@ typedef struct EState
 	ScanDirection es_direction; /* current scan direction */
 	Snapshot	es_snapshot;	/* time qual to use */
 	Snapshot	es_crosscheck_snapshot; /* crosscheck time qual for RI */
-	List	   *es_range_table; /* List of RangeTableEntrys */
+	List	   *es_range_table; /* List of RangeTblEntry */
 
 	/* Info about target table for insert/update/delete queries: */
 	ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 537981462b23e7b51585f63ab97e9116e8c37106..5a7445f69c0522db705059fdca13beade304492a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.92 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,6 +48,8 @@ typedef struct PlannedStmt
 
 	IntoClause *into;			/* target for SELECT INTO / CREATE TABLE AS */
 
+	List	   *subplans;		/* Plan trees for SubPlan expressions */
+
 	/*
 	 * If the query has a returningList then the planner will store a list of
 	 * processed targetlists (one per result relation) here.  We must have a
@@ -65,6 +67,10 @@ typedef struct PlannedStmt
 	int			nParamExec;		/* number of PARAM_EXEC Params used */
 } PlannedStmt;
 
+/* macro for fetching the Plan associated with a SubPlan node */
+#define exec_subplan_get_plan(plannedstmt, subplan) \
+	((Plan *) list_nth((plannedstmt)->subplans, (subplan)->plan_id - 1))
+
 
 /* ----------------
  *		Plan node
@@ -313,12 +319,17 @@ typedef struct TidScan
  * the generic lefttree field as you might expect.	This is because we do
  * not want plan-tree-traversal routines to recurse into the subplan without
  * knowing that they are changing Query contexts.
+ *
+ * Note: subrtable is used just to carry the subquery rangetable from
+ * createplan.c to setrefs.c; it should always be NIL by the time the
+ * executor sees the plan.
  * ----------------
  */
 typedef struct SubqueryScan
 {
 	Scan		scan;
 	Plan	   *subplan;
+	List	   *subrtable;		/* temporary workspace for planner */
 } SubqueryScan;
 
 /* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 185673f729a85f90750eff29f7e30ef2c720181b..7efb6ec77cfaa970fa1062b1d976d6ed97e85bac 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.127 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -428,8 +428,10 @@ typedef struct SubLink
  * SubPlan - executable expression node for a subplan (sub-SELECT)
  *
  * The planner replaces SubLink nodes in expression trees with SubPlan
- * nodes after it has finished planning the subquery.  SubPlan contains
- * a sub-plantree and rtable instead of a sub-Query.
+ * nodes after it has finished planning the subquery.  SubPlan references
+ * a sub-plantree stored in the subplans list of the toplevel PlannedStmt.
+ * (We avoid a direct link to make it easier to copy expression trees
+ * without causing multiple processing of the subplan.)
  *
  * In an ordinary subplan, testexpr points to an executable expression
  * (OpExpr, an AND/OR tree of OpExprs, or RowCompareExpr) for the combining
@@ -463,12 +465,10 @@ typedef struct SubPlan
 	/* The combining operators, transformed to an executable expression: */
 	Node	   *testexpr;		/* OpExpr or RowCompareExpr expression tree */
 	List	   *paramIds;		/* IDs of Params embedded in the above */
-	/* The subselect, transformed to a Plan: */
-	struct Plan *plan;			/* subselect plan itself */
-	int			plan_id;		/* kluge because we haven't equal-funcs for
-								 * plan nodes... we compare this instead of
-								 * subselect plan */
-	List	   *rtable;			/* range table for subselect */
+	/* Identification of the Plan tree to use: */
+	int			plan_id;		/* Index (from 1) in PlannedStmt.subplans */
+	/* Extra data saved for the convenience of exprType(): */
+	Oid			firstColType;	/* Type of first column of subplan result */
 	/* Information about execution strategy: */
 	bool		useHashTable;	/* TRUE to store subselect output in a hash
 								 * table (implies we are doing "IN") */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index fdb0cb0a5aeacfe10492011af4f97375c02b8eaf..76ad5abe1f5df70cf2c5f0e5e37fd7e6d4649b06 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.26 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.27 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,8 +31,5 @@ extern void print_expr(Node *expr, List *rtable);
 extern void print_pathkeys(List *pathkeys, List *rtable);
 extern void print_tl(List *tlist, List *rtable);
 extern void print_slot(TupleTableSlot *slot);
-extern void print_plan_recursive(Plan *p, Query *parsetree,
-					 int indentLevel, char *label);
-extern void print_plan(Plan *p, Query *parsetree);
 
 #endif   /* PRINT_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 59ec830f3ff6a23d1c065d7715a773d38bdb4037..19bcb51ea08c2bf8566f4033ca5a0a447ab52ff8 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.138 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,9 +64,17 @@ typedef struct PlannerGlobal
 
 	List	   *paramlist;		/* to keep track of cross-level Params */
 
-	int			next_plan_id;	/* hack for distinguishing SubPlans */
+	List	   *subplans;		/* Plans for SubPlan nodes */
+
+	List	   *subrtables;		/* Rangetables for SubPlan nodes */
+
+	List	   *finalrtable;	/* "flat" rangetable for executor */
 } PlannerGlobal;
 
+/* macro for fetching the Plan associated with a SubPlan node */
+#define planner_subplan_get_plan(root, subplan) \
+	((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1))
+
 
 /*----------
  * PlannerInfo
@@ -228,6 +236,7 @@ typedef struct PlannerInfo
  *		pages - number of disk pages in relation (zero if not a table)
  *		tuples - number of tuples in relation (not considering restrictions)
  *		subplan - plan for subquery (NULL if it's not a subquery)
+ *		subrtable - rangetable for subquery (NIL if it's not a subquery)
  *
  *		Note: for a subquery, tuples and subplan are not set immediately
  *		upon creation of the RelOptInfo object; they are filled in when
@@ -310,6 +319,7 @@ typedef struct RelOptInfo
 	BlockNumber pages;
 	double		tuples;
 	struct Plan *subplan;		/* if subquery */
+	List	   *subrtable;		/* if subquery */
 
 	/* used by various scans and joins: */
 	List	   *baserestrictinfo;		/* RestrictInfo structures (if base
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index fde65ed874a0d1cb6e4244d7478daac101d3e003..ef92d685eb4a433ef75bd77e5a7fbd9448bacef3 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.84 2007/01/22 01:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.85 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,8 +88,8 @@ extern void cost_group(Path *path, PlannerInfo *root,
 extern void cost_nestloop(NestPath *path, PlannerInfo *root);
 extern void cost_mergejoin(MergePath *path, PlannerInfo *root);
 extern void cost_hashjoin(HashPath *path, PlannerInfo *root);
-extern void cost_qual_eval(QualCost *cost, List *quals);
-extern void cost_qual_eval_node(QualCost *cost, Node *qual);
+extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
+extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
 extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
 						   RelOptInfo *outer_rel,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 6bf34e162a7c479f45cf951ee15d27a77b92425d..b5ad4b3c3b503a5ae93db14f8a613477963e1505 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.99 2007/01/22 01:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.100 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
  */
 extern Plan *create_plan(PlannerInfo *root, Path *best_path);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
-				  Index scanrelid, Plan *subplan);
+				  Index scanrelid, Plan *subplan, List *subrtable);
 extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
 extern Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
 						List *pathkeys);
@@ -60,7 +60,8 @@ extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
 		   int64 offset_est, int64 count_est);
 extern SetOp *make_setop(SetOpCmd cmd, Plan *lefttree,
 		   List *distinctList, AttrNumber flagColIdx);
-extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+extern Result *make_result(PlannerInfo *root, List *tlist,
+						   Node *resconstantqual, Plan *subplan);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
@@ -91,7 +92,9 @@ extern RestrictInfo *build_implied_join_equality(Oid opno,
 /*
  * prototypes for plan/setrefs.c
  */
-extern Plan *set_plan_references(Plan *plan, List *rtable);
+extern Plan *set_plan_references(PlannerGlobal *glob,
+								 Plan *plan,
+								 List *rtable);
 extern List *set_returning_clause_references(List *rlist,
 								Plan *topplan,
 								Index resultRelation);