diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6a97b01ec3c43cf37b789ad1a214ef0cf20ea51c..835b1ef6ade2af64a0baf51536167eeb6a75a356 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.158 2007/02/22 23:44:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.159 2007/02/23 21:59:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,11 +49,9 @@ static void explain_outNode(StringInfo str,
 				Plan *outer_plan,
 				int indent, ExplainState *es);
 static void show_scan_qual(List *qual, const char *qlabel,
-			   int scanrelid, Plan *outer_plan,
+			   int scanrelid, Plan *outer_plan, Plan *inner_plan,
 			   StringInfo str, int indent, ExplainState *es);
-static void show_upper_qual(List *qual, const char *qlabel,
-				const char *outer_name, Plan *outer_plan,
-				const char *inner_name, Plan *inner_plan,
+static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
 				StringInfo str, int indent, ExplainState *es);
 static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
 			   const char *qlabel,
@@ -725,19 +723,19 @@ explain_outNode(StringInfo str,
 			show_scan_qual(((IndexScan *) plan)->indexqualorig,
 						   "Index Cond",
 						   ((Scan *) plan)->scanrelid,
-						   outer_plan,
+						   outer_plan, NULL,
 						   str, indent, es);
 			show_scan_qual(plan->qual,
 						   "Filter",
 						   ((Scan *) plan)->scanrelid,
-						   outer_plan,
+						   outer_plan, NULL,
 						   str, indent, es);
 			break;
 		case T_BitmapIndexScan:
 			show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
 						   "Index Cond",
 						   ((Scan *) plan)->scanrelid,
-						   outer_plan,
+						   outer_plan, NULL,
 						   str, indent, es);
 			break;
 		case T_BitmapHeapScan:
@@ -745,17 +743,24 @@ explain_outNode(StringInfo str,
 			show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
 						   "Recheck Cond",
 						   ((Scan *) plan)->scanrelid,
-						   outer_plan,
+						   outer_plan, NULL,
 						   str, indent, es);
 			/* FALL THRU */
 		case T_SeqScan:
-		case T_SubqueryScan:
 		case T_FunctionScan:
 		case T_ValuesScan:
+			show_scan_qual(plan->qual,
+						   "Filter",
+						   ((Scan *) plan)->scanrelid,
+						   outer_plan, NULL,
+						   str, indent, es);
+			break;
+		case T_SubqueryScan:
 			show_scan_qual(plan->qual,
 						   "Filter",
 						   ((Scan *) plan)->scanrelid,
 						   outer_plan,
+						   ((SubqueryScan *) plan)->subplan,
 						   str, indent, es);
 			break;
 		case T_TidScan:
@@ -771,67 +776,49 @@ explain_outNode(StringInfo str,
 				show_scan_qual(tidquals,
 							   "TID Cond",
 							   ((Scan *) plan)->scanrelid,
-							   outer_plan,
+							   outer_plan, NULL,
 							   str, indent, es);
 				show_scan_qual(plan->qual,
 							   "Filter",
 							   ((Scan *) plan)->scanrelid,
-							   outer_plan,
+							   outer_plan, NULL,
 							   str, indent, es);
 			}
 			break;
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
-							"Join Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Join Filter", plan,
 							str, indent, es);
 			show_upper_qual(plan->qual,
-							"Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Filter", plan,
 							str, indent, es);
 			break;
 		case T_MergeJoin:
 			show_upper_qual(((MergeJoin *) plan)->mergeclauses,
-							"Merge Cond",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Merge Cond", plan,
 							str, indent, es);
 			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
-							"Join Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Join Filter", plan,
 							str, indent, es);
 			show_upper_qual(plan->qual,
-							"Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Filter", plan,
 							str, indent, es);
 			break;
 		case T_HashJoin:
 			show_upper_qual(((HashJoin *) plan)->hashclauses,
-							"Hash Cond",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Hash Cond", plan,
 							str, indent, es);
 			show_upper_qual(((HashJoin *) plan)->join.joinqual,
-							"Join Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Join Filter", plan,
 							str, indent, es);
 			show_upper_qual(plan->qual,
-							"Filter",
-							"outer", outerPlan(plan),
-							"inner", innerPlan(plan),
+							"Filter", plan,
 							str, indent, es);
 			break;
 		case T_Agg:
 		case T_Group:
 			show_upper_qual(plan->qual,
-							"Filter",
-							"subplan", outerPlan(plan),
-							"", NULL,
+							"Filter", plan,
 							str, indent, es);
 			break;
 		case T_Sort:
@@ -843,14 +830,10 @@ explain_outNode(StringInfo str,
 			break;
 		case T_Result:
 			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
-							"One-Time Filter",
-							"subplan", outerPlan(plan),
-							"", NULL,
+							"One-Time Filter", plan,
 							str, indent, es);
 			show_upper_qual(plan->qual,
-							"Filter",
-							"subplan", outerPlan(plan),
-							"", NULL,
+							"Filter", plan,
 							str, indent, es);
 			break;
 		default:
@@ -1032,14 +1015,18 @@ explain_outNode(StringInfo str,
 
 /*
  * Show a qualifier expression for a scan plan node
+ *
+ * Note: outer_plan is the referent for any OUTER vars in the scan qual;
+ * this would be the outer side of a nestloop plan.  inner_plan should be
+ * NULL except for a SubqueryScan plan node, where it should be the subplan.
  */
 static void
 show_scan_qual(List *qual, const char *qlabel,
-			   int scanrelid, Plan *outer_plan,
+			   int scanrelid, Plan *outer_plan, Plan *inner_plan,
 			   StringInfo str, int indent, ExplainState *es)
 {
-	Node	   *outercontext;
 	List	   *context;
+	bool		useprefix;
 	Node	   *node;
 	char	   *exprstr;
 	int			i;
@@ -1051,31 +1038,14 @@ show_scan_qual(List *qual, const char *qlabel,
 	/* Convert AND list to explicit AND */
 	node = (Node *) make_ands_explicit(qual);
 
-	/*
-	 * If we have an outer plan that is referenced by the qual, add it to the
-	 * deparse context.  If not, don't (so that we don't force prefixes
-	 * unnecessarily).
-	 */
-	if (outer_plan)
-	{
-		Relids		varnos = pull_varnos(node);
-
-		if (bms_is_member(OUTER, varnos))
-			outercontext = deparse_context_for_subplan("outer",
-													   (Node *) outer_plan);
-		else
-			outercontext = NULL;
-		bms_free(varnos);
-	}
-	else
-		outercontext = NULL;
-
-	context = deparse_context_for_plan(OUTER, outercontext,
-									   0, NULL,
+	/* Set up deparsing context */
+	context = deparse_context_for_plan((Node *) outer_plan,
+									   (Node *) inner_plan,
 									   es->rtable);
+	useprefix = (outer_plan != NULL || inner_plan != NULL);
 
 	/* Deparse the expression */
-	exprstr = deparse_expression(node, context, (outercontext != NULL), false);
+	exprstr = deparse_expression(node, context, useprefix, false);
 
 	/* And add to str */
 	for (i = 0; i < indent; i++)
@@ -1087,16 +1057,11 @@ show_scan_qual(List *qual, const char *qlabel,
  * Show a qualifier expression for an upper-level plan node
  */
 static void
-show_upper_qual(List *qual, const char *qlabel,
-				const char *outer_name, Plan *outer_plan,
-				const char *inner_name, Plan *inner_plan,
+show_upper_qual(List *qual, const char *qlabel, Plan *plan,
 				StringInfo str, int indent, ExplainState *es)
 {
 	List	   *context;
-	Node	   *outercontext;
-	Node	   *innercontext;
-	int			outer_varno;
-	int			inner_varno;
+	bool		useprefix;
 	Node	   *node;
 	char	   *exprstr;
 	int			i;
@@ -1105,36 +1070,15 @@ show_upper_qual(List *qual, const char *qlabel,
 	if (qual == NIL)
 		return;
 
-	/* Generate deparse context */
-	if (outer_plan)
-	{
-		outercontext = deparse_context_for_subplan(outer_name,
-												   (Node *) outer_plan);
-		outer_varno = OUTER;
-	}
-	else
-	{
-		outercontext = NULL;
-		outer_varno = 0;
-	}
-	if (inner_plan)
-	{
-		innercontext = deparse_context_for_subplan(inner_name,
-												   (Node *) inner_plan);
-		inner_varno = INNER;
-	}
-	else
-	{
-		innercontext = NULL;
-		inner_varno = 0;
-	}
-	context = deparse_context_for_plan(outer_varno, outercontext,
-									   inner_varno, innercontext,
+	/* Set up deparsing context */
+	context = deparse_context_for_plan((Node *) outerPlan(plan),
+									   (Node *) innerPlan(plan),
 									   es->rtable);
+	useprefix = list_length(es->rtable) > 1;
 
 	/* Deparse the expression */
 	node = (Node *) make_ands_explicit(qual);
-	exprstr = deparse_expression(node, context, (inner_plan != NULL), false);
+	exprstr = deparse_expression(node, context, useprefix, false);
 
 	/* And add to str */
 	for (i = 0; i < indent; i++)
@@ -1154,7 +1098,6 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
 	bool		useprefix;
 	int			keyno;
 	char	   *exprstr;
-	Relids		varnos;
 	int			i;
 
 	if (nkeys <= 0)
@@ -1164,33 +1107,11 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
 		appendStringInfo(str, "  ");
 	appendStringInfo(str, "  %s: ", qlabel);
 
-	/*
-	 * In this routine we expect that the plan node's tlist has not been
-	 * processed by set_plan_references().	Normally, any Vars will contain
-	 * valid varnos referencing the actual rtable.	But we might instead be
-	 * looking at a dummy tlist generated by prepunion.c; if there are Vars
-	 * with zero varno, use the tlist itself to determine their names.
-	 */
-	varnos = pull_varnos((Node *) sortplan->targetlist);
-	if (bms_is_member(0, varnos))
-	{
-		Node	   *outercontext;
-
-		outercontext = deparse_context_for_subplan("sort",
-												   (Node *) sortplan);
-		context = deparse_context_for_plan(0, outercontext,
-										   0, NULL,
-										   es->rtable);
-		useprefix = false;
-	}
-	else
-	{
-		context = deparse_context_for_plan(0, NULL,
-										   0, NULL,
-										   es->rtable);
-		useprefix = list_length(es->rtable) > 1;
-	}
-	bms_free(varnos);
+	/* Set up deparsing context */
+	context = deparse_context_for_plan((Node *) outerPlan(sortplan),
+									   NULL,		/* Sort has no innerPlan */
+									   es->rtable);
+	useprefix = list_length(es->rtable) > 1;
 
 	for (keyno = 0; keyno < nkeys; keyno++)
 	{
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 5240a7283b2ff41befd4b5cf5f00c3044c3a0fd2..5c11d788e00247d7f3e673962975e1f0b0487a95 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.132 2007/02/22 23:44:25 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.133 2007/02/23 21:59:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,6 +73,7 @@ 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_upper_references(Plan *plan, int rtoffset);
+static void set_dummy_tlist_references(Plan *plan, int rtoffset);
 static indexed_tlist *build_tlist_index(List *tlist);
 static Var *search_indexed_tlist_for_var(Var *var,
 							 indexed_tlist *itlist,
@@ -315,8 +316,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
 			 * 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);
+			set_dummy_tlist_references(plan, rtoffset);
 			/*
 			 * Since these plan types don't check quals either, we should not
 			 * find any qual expression attached to them.
@@ -330,11 +330,12 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
 				/*
 				 * Like the plan types above, Limit doesn't evaluate its tlist
 				 * or quals.  It does have live expressions for limit/offset,
-				 * however.
+				 * however; and those cannot contain subplan variable refs,
+				 * so fix_scan_expr works for them.
 				 */
-				splan->plan.targetlist =
-					fix_scan_list(splan->plan.targetlist, rtoffset);
+				set_dummy_tlist_references(plan, rtoffset);
 				Assert(splan->plan.qual == NIL);
+
 				splan->limitOffset =
 					fix_scan_expr(splan->limitOffset, rtoffset);
 				splan->limitCount =
@@ -375,8 +376,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
 				 * Append, like Sort et al, doesn't actually evaluate its
 				 * targetlist or check quals.
 				 */
-				splan->plan.targetlist =
-					fix_scan_list(splan->plan.targetlist, rtoffset);
+				set_dummy_tlist_references(plan, rtoffset);
 				Assert(splan->plan.qual == NIL);
 				foreach(l, splan->appendplans)
 				{
@@ -892,10 +892,7 @@ set_upper_references(Plan *plan, int rtoffset)
 	List	   *output_targetlist;
 	ListCell   *l;
 
-	if (subplan != NULL)
-		subplan_itlist = build_tlist_index(subplan->targetlist);
-	else
-		subplan_itlist = build_tlist_index(NIL);
+	subplan_itlist = build_tlist_index(subplan->targetlist);
 
 	output_targetlist = NIL;
 	foreach(l, plan->targetlist)
@@ -920,6 +917,58 @@ set_upper_references(Plan *plan, int rtoffset)
 	pfree(subplan_itlist);
 }
 
+/*
+ * set_dummy_tlist_references
+ *	  Replace the targetlist of an upper-level plan node with a simple
+ *	  list of OUTER references to its child.
+ *
+ * This is used for plan types like Sort and Append that don't evaluate
+ * their targetlists.  Although the executor doesn't care at all what's in
+ * the tlist, EXPLAIN needs it to be realistic.
+ *
+ * Note: we could almost use set_upper_references() here, but it fails for
+ * Append for lack of a lefttree subplan.  Single-purpose code is faster
+ * anyway.
+ */
+static void
+set_dummy_tlist_references(Plan *plan, int rtoffset)
+{
+	List	   *output_targetlist;
+	ListCell   *l;
+
+	output_targetlist = NIL;
+	foreach(l, plan->targetlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(l);
+		Var		   *oldvar = (Var *) tle->expr;
+		Var		   *newvar;
+
+		newvar = makeVar(OUTER,
+						 tle->resno,
+						 exprType((Node *) oldvar),
+						 exprTypmod((Node *) oldvar),
+						 0);
+		if (IsA(oldvar, Var))
+		{
+			newvar->varnoold = oldvar->varno + rtoffset;
+			newvar->varoattno = oldvar->varattno;
+		}
+		else
+		{
+			newvar->varnoold = 0;	/* wasn't ever a plain Var */
+			newvar->varoattno = 0;
+		}
+
+		tle = flatCopyTargetEntry(tle);
+		tle->expr = (Expr *) newvar;
+		output_targetlist = lappend(output_targetlist, tle);
+	}
+	plan->targetlist = output_targetlist;
+
+	/* We don't touch plan->qual here */
+}
+
+
 /*
  * build_tlist_index --- build an index data structure for a child tlist
  *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 2f61b4a9fc57faec2d246b67c4f9bfd8573d5dfe..765b3c65c3060f03ed93b04dc7b2a20741f2c1a7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.250 2007/02/22 23:44:25 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.251 2007/02/23 21:59:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,17 +90,14 @@ typedef struct
  *
  * The rangetable is the list of actual RTEs from the query tree.
  *
- * For deparsing plan trees, we allow two special RTE entries that are not
- * part of the rtable list (partly because they don't have consecutively
- * allocated varnos).
+ * For deparsing plan trees, we provide for outer and inner subplan nodes.
+ * The tlists of these nodes are used to resolve OUTER and INNER varnos.
  */
 typedef struct
 {
 	List	   *rtable;			/* List of RangeTblEntry nodes */
-	int			outer_varno;	/* varno for outer_rte */
-	RangeTblEntry *outer_rte;	/* special RangeTblEntry, or NULL */
-	int			inner_varno;	/* varno for inner_rte */
-	RangeTblEntry *inner_rte;	/* special RangeTblEntry, or NULL */
+	Plan	   *outer_plan;		/* OUTER subplan, or NULL if none */
+	Plan	   *inner_plan;		/* INNER subplan, or NULL if none */
 } deparse_namespace;
 
 
@@ -158,8 +155,8 @@ static void get_setop_query(Node *setOp, Query *query,
 static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
 						 bool force_colno,
 						 deparse_context *context);
-static void get_names_for_var(Var *var, int levelsup, deparse_context *context,
-				  char **schemaname, char **refname, char **attname);
+static char *get_variable(Var *var, int levelsup, bool showstar,
+						  deparse_context *context);
 static RangeTblEntry *find_rte_by_refname(const char *refname,
 					deparse_context *context);
 static const char *get_simple_binary_op_name(OpExpr *expr);
@@ -1434,8 +1431,7 @@ deparse_context_for(const char *aliasname, Oid relid)
 
 	/* Build one-element rtable */
 	dpns->rtable = list_make1(rte);
-	dpns->outer_varno = dpns->inner_varno = 0;
-	dpns->outer_rte = dpns->inner_rte = NULL;
+	dpns->outer_plan = dpns->inner_plan = NULL;
 
 	/* Return a one-deep namespace stack */
 	return list_make1(dpns);
@@ -1444,17 +1440,21 @@ deparse_context_for(const char *aliasname, Oid relid)
 /*
  * deparse_context_for_plan		- Build deparse context for a plan node
  *
- * The plan node may contain references to one or two subplans or outer
- * join plan nodes.  For these, pass the varno used plus a context node
- * made with deparse_context_for_subplan.  (Pass 0/NULL for unused inputs.)
+ * When deparsing an expression in a Plan tree, we might have to resolve
+ * OUTER or INNER references.  Pass the plan nodes whose targetlists define
+ * such references, or NULL when none are expected.  (outer_plan and
+ * inner_plan really ought to be declared as "Plan *", but we use "Node *"
+ * to avoid having to include plannodes.h in builtins.h.)
+ *
+ * As a special case, when deparsing a SubqueryScan plan, pass the subplan
+ * as inner_plan (there won't be any regular innerPlan() in this case).
  *
  * The plan's rangetable list must also be passed.  We actually prefer to use
  * the rangetable to resolve simple Vars, but the subplan inputs are needed
  * for Vars that reference expressions computed in subplan target lists.
  */
 List *
-deparse_context_for_plan(int outer_varno, Node *outercontext,
-						 int inner_varno, Node *innercontext,
+deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
 						 List *rtable)
 {
 	deparse_namespace *dpns;
@@ -1462,47 +1462,13 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
 	dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
 
 	dpns->rtable = rtable;
-	dpns->outer_varno = outer_varno;
-	dpns->outer_rte = (RangeTblEntry *) outercontext;
-	dpns->inner_varno = inner_varno;
-	dpns->inner_rte = (RangeTblEntry *) innercontext;
+	dpns->outer_plan = (Plan *) outer_plan;
+	dpns->inner_plan = (Plan *) inner_plan;
 
 	/* Return a one-deep namespace stack */
 	return list_make1(dpns);
 }
 
-/*
- * deparse_context_for_subplan	- Build deparse context for a plan node
- *
- * Helper routine to build one of the inputs for deparse_context_for_plan.
- * Pass the name to be used to reference the subplan, plus the Plan node.
- * (subplan really ought to be declared as "Plan *", but we use "Node *"
- * to avoid having to include plannodes.h in builtins.h.)
- *
- * The returned node is actually a RangeTblEntry, but we declare it as just
- * Node to discourage callers from assuming anything.
- */
-Node *
-deparse_context_for_subplan(const char *name, Node *subplan)
-{
-	RangeTblEntry *rte = makeNode(RangeTblEntry);
-
-	/*
-	 * We create an RTE_SPECIAL RangeTblEntry, and store the subplan in its
-	 * funcexpr field.	RTE_SPECIAL nodes shouldn't appear in deparse contexts
-	 * otherwise.
-	 */
-	rte->rtekind = RTE_SPECIAL;
-	rte->relid = InvalidOid;
-	rte->funcexpr = subplan;
-	rte->alias = NULL;
-	rte->eref = makeAlias(name, NIL);
-	rte->inh = false;
-	rte->inFromCl = true;
-
-	return (Node *) rte;
-}
-
 /* ----------
  * make_ruledef			- reconstruct the CREATE RULE command
  *				  for a given pg_rewrite tuple
@@ -1645,8 +1611,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
 		context.prettyFlags = prettyFlags;
 		context.indentLevel = PRETTYINDENT_STD;
 		dpns.rtable = query->rtable;
-		dpns.outer_varno = dpns.inner_varno = 0;
-		dpns.outer_rte = dpns.inner_rte = NULL;
+		dpns.outer_plan = dpns.inner_plan = NULL;
 
 		get_rule_expr(qual, &context, false);
 	}
@@ -1789,8 +1754,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
 	context.indentLevel = startIndent;
 
 	dpns.rtable = query->rtable;
-	dpns.outer_varno = dpns.inner_varno = 0;
-	dpns.outer_rte = dpns.inner_rte = NULL;
+	dpns.outer_plan = dpns.inner_plan = NULL;
 
 	switch (query->commandType)
 	{
@@ -2129,38 +2093,7 @@ get_target_list(List *targetList, deparse_context *context,
 		 */
 		if (tle->expr && IsA(tle->expr, Var))
 		{
-			Var		   *var = (Var *) (tle->expr);
-			char	   *schemaname;
-			char	   *refname;
-
-			get_names_for_var(var, 0, context,
-							  &schemaname, &refname, &attname);
-			if (refname && (context->varprefix || attname == NULL))
-			{
-				if (schemaname)
-					appendStringInfo(buf, "%s.",
-									 quote_identifier(schemaname));
-
-				if (strcmp(refname, "*NEW*") == 0)
-					appendStringInfoString(buf, "new");
-				else if (strcmp(refname, "*OLD*") == 0)
-					appendStringInfoString(buf, "old");
-				else
-					appendStringInfoString(buf, quote_identifier(refname));
-
-				if (attname)
-					appendStringInfoChar(buf, '.');
-			}
-			if (attname)
-				appendStringInfoString(buf, quote_identifier(attname));
-			else
-			{
-				/*
-				 * In the whole-row Var case, refname is what the default AS
-				 * name would be.
-				 */
-				attname = refname;
-			}
+			attname = get_variable((Var *) tle->expr, 0, false, context);
 		}
 		else
 		{
@@ -2596,25 +2529,60 @@ get_utility_query_def(Query *query, deparse_context *context)
 
 
 /*
- * Get the RTE referenced by a (possibly nonlocal) Var.
+ * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
  *
- * The appropriate attribute number is stored into *attno
- * (do not assume that var->varattno is what to use).
+ * When expanding an OUTER or INNER reference, we must push new outer/inner
+ * subplans in case the referenced expression itself uses OUTER/INNER.  We
+ * modify the top stack entry in-place to avoid affecting levelsup issues
+ * (although in a Plan tree there really shouldn't be any).
+ *
+ * Caller must save and restore outer_plan and inner_plan around this.
+ */
+static void
+push_plan(deparse_namespace *dpns, Plan *subplan)
+{
+	/*
+	 * We special-case Append to pretend that the first child plan is the
+	 * OUTER referent; otherwise normal.
+	 */
+	if (IsA(subplan, Append))
+		dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
+	else
+		dpns->outer_plan = outerPlan(subplan);
+	/*
+	 * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
+	 * use OUTER because that could someday conflict with the normal meaning.)
+	 */
+	if (IsA(subplan, SubqueryScan))
+		dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
+	else
+		dpns->inner_plan = innerPlan(subplan);
+}
+
+
+/*
+ * Display a Var appropriately.
  *
  * In some cases (currently only when recursing into an unnamed join)
  * the Var's varlevelsup has to be interpreted with respect to a context
  * above the current one; levelsup indicates the offset.
+ *
+ * If showstar is TRUE, whole-row Vars are displayed as "foo.*";
+ * if FALSE, merely as "foo".
+ *
+ * Returns the attname of the Var, or NULL if not determinable.
  */
-static RangeTblEntry *
-get_rte_for_var(Var *var, int levelsup, deparse_context *context,
-				AttrNumber *attno)
+static char *
+get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
 {
+	StringInfo	buf = context->buf;
 	RangeTblEntry *rte;
+	AttrNumber	attnum;
 	int			netlevelsup;
 	deparse_namespace *dpns;
-
-	/* default assumption */
-	*attno = var->varattno;
+	char	   *schemaname;
+	char	   *refname;
+	char	   *attname;
 
 	/* Find appropriate nesting depth */
 	netlevelsup = var->varlevelsup + levelsup;
@@ -2628,59 +2596,81 @@ get_rte_for_var(Var *var, int levelsup, deparse_context *context,
 	 * Try to find the relevant RTE in this rtable.  In a plan tree, it's
 	 * likely that varno is OUTER or INNER, in which case we try to use
 	 * varnoold instead.  If the Var references an expression computed by a
-	 * subplan, varnoold will be 0, and we fall back to looking at the special
-	 * subplan RTEs.
+	 * subplan, varnoold will be 0, and we must dig down into the subplans.
 	 */
 	if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
+	{
 		rte = rt_fetch(var->varno, dpns->rtable);
+		attnum = var->varattno;
+	}
 	else if (var->varnoold >= 1 && var->varnoold <= list_length(dpns->rtable))
 	{
 		rte = rt_fetch(var->varnoold, dpns->rtable);
-		*attno = var->varoattno;
+		attnum = var->varoattno;
 	}
-	else if (var->varno == dpns->outer_varno)
-		rte = dpns->outer_rte;
-	else if (var->varno == dpns->inner_varno)
-		rte = dpns->inner_rte;
-	else
-		rte = NULL;
-	if (rte == NULL)
-		elog(ERROR, "bogus varno: %d", var->varno);
-	return rte;
-}
+	else if (var->varno == OUTER && dpns->outer_plan)
+	{
+		TargetEntry *tle;
+		Plan   *save_outer;
+		Plan   *save_inner;
 
+		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
+		if (!tle)
+			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
 
-/*
- * Get the schemaname, refname and attname for a (possibly nonlocal) Var.
- *
- * In some cases (currently only when recursing into an unnamed join)
- * the Var's varlevelsup has to be interpreted with respect to a context
- * above the current one; levelsup indicates the offset.
- *
- * schemaname is usually returned as NULL.	It will be non-null only if
- * use of the unqualified refname would find the wrong RTE.
- *
- * refname will be returned as NULL if the Var references an unnamed join.
- * In this case the Var *must* be displayed without any qualification.
- *
- * attname will be returned as NULL if the Var represents a whole tuple
- * of the relation.  (Typically we'd want to display the Var as "foo.*",
- * but it's convenient to return NULL to make it easier for callers to
- * distinguish this case.)
- */
-static void
-get_names_for_var(Var *var, int levelsup, deparse_context *context,
-				  char **schemaname, char **refname, char **attname)
-{
-	RangeTblEntry *rte;
-	AttrNumber	attnum;
+		Assert(netlevelsup == 0);
+		save_outer = dpns->outer_plan;
+		save_inner = dpns->inner_plan;
+		push_plan(dpns, dpns->outer_plan);
+
+		/*
+		 * Force parentheses because our caller probably assumed a Var is a
+		 * simple expression.
+		 */
+		appendStringInfoChar(buf, '(');
+		get_rule_expr((Node *) tle->expr, context, true);
+		appendStringInfoChar(buf, ')');
+
+		dpns->outer_plan = save_outer;
+		dpns->inner_plan = save_inner;
+		return NULL;
+	}
+	else if (var->varno == INNER && dpns->inner_plan)
+	{
+		TargetEntry *tle;
+		Plan   *save_outer;
+		Plan   *save_inner;
+
+		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+		if (!tle)
+			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
 
-	/* Find appropriate RTE */
-	rte = get_rte_for_var(var, levelsup, context, &attnum);
+		Assert(netlevelsup == 0);
+		save_outer = dpns->outer_plan;
+		save_inner = dpns->inner_plan;
+		push_plan(dpns, dpns->inner_plan);
 
-	/* Emit results */
-	*schemaname = NULL;			/* default assumptions */
-	*refname = rte->eref->aliasname;
+		/*
+		 * Force parentheses because our caller probably assumed a Var is a
+		 * simple expression.
+		 */
+		appendStringInfoChar(buf, '(');
+		get_rule_expr((Node *) tle->expr, context, true);
+		appendStringInfoChar(buf, ')');
+
+		dpns->outer_plan = save_outer;
+		dpns->inner_plan = save_inner;
+		return NULL;
+	}
+	else
+	{
+		elog(ERROR, "bogus varno: %d", var->varno);
+		return NULL;					/* keep compiler quiet */
+	}
+
+	/* Identify names to use */
+	schemaname = NULL;			/* default assumptions */
+	refname = rte->eref->aliasname;
 
 	/* Exceptions occur only if the RTE is alias-less */
 	if (rte->alias == NULL)
@@ -2693,18 +2683,17 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
 			 * to specify the schemaname to avoid these errors.
 			 */
 			if (find_rte_by_refname(rte->eref->aliasname, context) != rte)
-				*schemaname =
-					get_namespace_name(get_rel_namespace(rte->relid));
+				schemaname = get_namespace_name(get_rel_namespace(rte->relid));
 		}
 		else if (rte->rtekind == RTE_JOIN)
 		{
 			/*
 			 * If it's an unnamed join, look at the expansion of the alias
 			 * variable.  If it's a simple reference to one of the input vars
-			 * then recursively find the name of that var, instead. (This
+			 * then recursively print the name of that var, instead. (This
 			 * allows correct decompiling of cases where there are identically
 			 * named columns on both sides of the join.) When it's not a
-			 * simple reference, we have to just return the unqualified
+			 * simple reference, we have to just print the unqualified
 			 * variable name (this can only happen with columns that were
 			 * merged by USING or NATURAL clauses).
 			 */
@@ -2715,63 +2704,55 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
 				aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
 				if (IsA(aliasvar, Var))
 				{
-					get_names_for_var(aliasvar,
-									  var->varlevelsup + levelsup, context,
-									  schemaname, refname, attname);
-					return;
+					return get_variable(aliasvar, var->varlevelsup + levelsup,
+										showstar, context);
 				}
 			}
 			/* Unnamed join has neither schemaname nor refname */
-			*refname = NULL;
-		}
-		else if (rte->rtekind == RTE_SPECIAL)
-		{
-			/*
-			 * This case occurs during EXPLAIN when we are looking at a
-			 * deparse context node set up by deparse_context_for_subplan().
-			 * If the subplan tlist provides a name, use it, but usually we'll
-			 * end up with "?columnN?".
-			 */
-			List	   *tlist = ((Plan *) rte->funcexpr)->targetlist;
-			TargetEntry *tle = get_tle_by_resno(tlist, attnum);
-
-			if (tle && tle->resname)
-			{
-				*attname = tle->resname;
-			}
-			else
-			{
-				char		buf[32];
-
-				snprintf(buf, sizeof(buf), "?column%d?", attnum);
-				*attname = pstrdup(buf);
-			}
-			return;
+			refname = NULL;
 		}
 	}
 
 	if (attnum == InvalidAttrNumber)
-		*attname = NULL;
+		attname = NULL;
 	else
-		*attname = get_rte_attribute_name(rte, attnum);
+		attname = get_rte_attribute_name(rte, attnum);
+
+	if (refname && (context->varprefix || attname == NULL))
+	{
+		if (schemaname)
+			appendStringInfo(buf, "%s.",
+							 quote_identifier(schemaname));
+
+		if (strcmp(refname, "*NEW*") == 0)
+			appendStringInfoString(buf, "new");
+		else if (strcmp(refname, "*OLD*") == 0)
+			appendStringInfoString(buf, "old");
+		else
+			appendStringInfoString(buf, quote_identifier(refname));
+
+		if (attname || showstar)
+			appendStringInfoChar(buf, '.');
+	}
+	if (attname)
+		appendStringInfoString(buf, quote_identifier(attname));
+	else if (showstar)
+		appendStringInfoChar(buf, '*');
+
+	return attname;
 }
 
 
 /*
- * Get the name of a field of a Var of type RECORD.
+ * Get the name of a field of an expression of composite type.
  *
+ * This is fairly straightforward except for the case of a Var of type RECORD.
  * Since no actual table or view column is allowed to have type RECORD, such
  * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.	We
  * drill down to find the ultimate defining expression and attempt to infer
  * the field name from it.	We ereport if we can't determine the name.
  *
  * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
- *
- * Note: this has essentially the same logic as the parser's
- * expandRecordVariable() function, but we are dealing with a different
- * representation of the input context, and we only need one field name not
- * a TupleDesc.  Also, we have a special case for RTE_SPECIAL so that we can
- * deal with displaying RECORD-returning functions in subplan targetlists.
  */
 static const char *
 get_name_for_var_field(Var *var, int fieldno,
@@ -2779,15 +2760,100 @@ get_name_for_var_field(Var *var, int fieldno,
 {
 	RangeTblEntry *rte;
 	AttrNumber	attnum;
+	int			netlevelsup;
+	deparse_namespace *dpns;
 	TupleDesc	tupleDesc;
 	Node	   *expr;
 
-	/* Check my caller didn't mess up */
-	Assert(IsA(var, Var));
-	Assert(var->vartype == RECORDOID);
+	/*
+	 * If it's a Var of type RECORD, we have to find what the Var refers to;
+	 * if not, we can use get_expr_result_type. If that fails, we try
+	 * lookup_rowtype_tupdesc, which will probably fail too, but will ereport
+	 * an acceptable message.
+	 */
+	if (!IsA(var, Var) ||
+		var->vartype != RECORDOID)
+	{
+		if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+			tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var),
+													exprTypmod((Node *) var));
+		Assert(tupleDesc);
+		/* Got the tupdesc, so we can extract the field name */
+		Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
+		return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
+	}
+
+	/* Find appropriate nesting depth */
+	netlevelsup = var->varlevelsup + levelsup;
+	if (netlevelsup >= list_length(context->namespaces))
+		elog(ERROR, "bogus varlevelsup: %d offset %d",
+			 var->varlevelsup, levelsup);
+	dpns = (deparse_namespace *) list_nth(context->namespaces,
+										  netlevelsup);
 
-	/* Find appropriate RTE */
-	rte = get_rte_for_var(var, levelsup, context, &attnum);
+	/*
+	 * Try to find the relevant RTE in this rtable.  In a plan tree, it's
+	 * likely that varno is OUTER or INNER, in which case we must dig down
+	 * into the subplans.  (We can't shortcut with varnoold here, because
+	 * it might reference a SUBQUERY RTE; we have to dig down to the
+	 * SubqueryScan plan level to cope with that.  See below.)
+	 */
+	if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
+	{
+		rte = rt_fetch(var->varno, dpns->rtable);
+		attnum = var->varattno;
+	}
+	else if (var->varno == OUTER && dpns->outer_plan)
+	{
+		TargetEntry *tle;
+		Plan   *save_outer;
+		Plan   *save_inner;
+		const char *result;
+
+		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
+		if (!tle)
+			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
+
+		Assert(netlevelsup == 0);
+		save_outer = dpns->outer_plan;
+		save_inner = dpns->inner_plan;
+		push_plan(dpns, dpns->outer_plan);
+
+		result = get_name_for_var_field((Var *) tle->expr, fieldno,
+										levelsup, context);
+
+		dpns->outer_plan = save_outer;
+		dpns->inner_plan = save_inner;
+		return result;
+	}
+	else if (var->varno == INNER && dpns->inner_plan)
+	{
+		TargetEntry *tle;
+		Plan   *save_outer;
+		Plan   *save_inner;
+		const char *result;
+
+		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+		if (!tle)
+			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+
+		Assert(netlevelsup == 0);
+		save_outer = dpns->outer_plan;
+		save_inner = dpns->inner_plan;
+		push_plan(dpns, dpns->inner_plan);
+
+		result = get_name_for_var_field((Var *) tle->expr, fieldno,
+										levelsup, context);
+
+		dpns->outer_plan = save_outer;
+		dpns->inner_plan = save_inner;
+		return result;
+	}
+	else
+	{
+		elog(ERROR, "bogus varno: %d", var->varno);
+		return NULL;					/* keep compiler quiet */
+	}
 
 	if (attnum == InvalidAttrNumber)
 	{
@@ -2795,11 +2861,19 @@ get_name_for_var_field(Var *var, int fieldno,
 		return get_rte_attribute_name(rte, fieldno);
 	}
 
+	/*
+	 * This part has essentially the same logic as the parser's
+	 * expandRecordVariable() function, but we are dealing with a different
+	 * representation of the input context, and we only need one field name not
+	 * a TupleDesc.  Also, we need a special case for deparsing Plan trees,
+	 * because the subquery field has been removed from SUBQUERY RTEs.
+	 */
 	expr = (Node *) var;		/* default if we can't drill down */
 
 	switch (rte->rtekind)
 	{
 		case RTE_RELATION:
+		case RTE_SPECIAL:
 		case RTE_VALUES:
 
 			/*
@@ -2810,38 +2884,77 @@ get_name_for_var_field(Var *var, int fieldno,
 			break;
 		case RTE_SUBQUERY:
 			{
-				/* Subselect-in-FROM: examine sub-select's output expr */
-				TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
-													attnum);
-
-				if (ste == NULL || ste->resjunk)
-					elog(ERROR, "subquery %s does not have attribute %d",
-						 rte->eref->aliasname, attnum);
-				expr = (Node *) ste->expr;
-				if (IsA(expr, Var))
+				if (rte->subquery)
 				{
-					/*
-					 * Recurse into the sub-select to see what its Var refers
-					 * to.	We have to build an additional level of namespace
-					 * to keep in step with varlevelsup in the subselect.
-					 */
-					deparse_namespace mydpns;
-					const char *result;
+					/* Subselect-in-FROM: examine sub-select's output expr */
+					TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
+														attnum);
+
+					if (ste == NULL || ste->resjunk)
+						elog(ERROR, "subquery %s does not have attribute %d",
+							 rte->eref->aliasname, attnum);
+					expr = (Node *) ste->expr;
+					if (IsA(expr, Var))
+					{
+						/*
+						 * Recurse into the sub-select to see what its Var
+						 * refers to. We have to build an additional level of
+						 * namespace to keep in step with varlevelsup in the
+						 * subselect.
+						 */
+						deparse_namespace mydpns;
+						const char *result;
 
-					mydpns.rtable = rte->subquery->rtable;
-					mydpns.outer_varno = mydpns.inner_varno = 0;
-					mydpns.outer_rte = mydpns.inner_rte = NULL;
+						mydpns.rtable = rte->subquery->rtable;
+						mydpns.outer_plan = mydpns.inner_plan = NULL;
 
-					context->namespaces = lcons(&mydpns, context->namespaces);
+						context->namespaces = lcons(&mydpns,
+													context->namespaces);
 
-					result = get_name_for_var_field((Var *) expr, fieldno,
-													0, context);
+						result = get_name_for_var_field((Var *) expr, fieldno,
+														0, context);
 
-					context->namespaces = list_delete_first(context->namespaces);
+						context->namespaces =
+							list_delete_first(context->namespaces);
 
+						return result;
+					}
+					/* else fall through to inspect the expression */
+				}
+				else
+				{
+					/*
+					 * We're deparsing a Plan tree so we don't have complete
+					 * RTE entries.  But the only place we'd see a Var
+					 * directly referencing a SUBQUERY RTE is in a SubqueryScan
+					 * plan node, and we can look into the child plan's tlist
+					 * instead.
+					 */
+					TargetEntry *tle;
+					Plan   *save_outer;
+					Plan   *save_inner;
+					const char *result;
+
+					if (!dpns->inner_plan)
+						elog(ERROR, "failed to find plan for subquery %s",
+							 rte->eref->aliasname);
+					tle = get_tle_by_resno(dpns->inner_plan->targetlist,
+										   attnum);
+					if (!tle)
+						elog(ERROR, "bogus varattno for subquery var: %d",
+							 attnum);
+					Assert(netlevelsup == 0);
+					save_outer = dpns->outer_plan;
+					save_inner = dpns->inner_plan;
+					push_plan(dpns, dpns->inner_plan);
+
+					result = get_name_for_var_field((Var *) tle->expr, fieldno,
+													levelsup, context);
+
+					dpns->outer_plan = save_outer;
+					dpns->inner_plan = save_inner;
 					return result;
 				}
-				/* else fall through to inspect the expression */
 			}
 			break;
 		case RTE_JOIN:
@@ -2861,40 +2974,6 @@ get_name_for_var_field(Var *var, int fieldno,
 			 * its result columns as RECORD, which is not allowed.
 			 */
 			break;
-		case RTE_SPECIAL:
-			{
-				/*
-				 * We are looking at a deparse context node set up by
-				 * deparse_context_for_subplan().  The Var must refer to an
-				 * expression computed by this subplan (or possibly one of its
-				 * inputs), rather than any simple attribute of an RTE entry.
-				 * Look into the subplan's target list to get the referenced
-				 * expression, digging down as far as needed to find something
-				 * that's not a Var, and then pass it to
-				 * get_expr_result_type().
-				 */
-				Plan	   *subplan = (Plan *) rte->funcexpr;
-
-				for (;;)
-				{
-					TargetEntry *ste;
-
-					ste = get_tle_by_resno(subplan->targetlist,
-										   ((Var *) expr)->varattno);
-					if (!ste || !ste->expr)
-						break;
-					expr = (Node *) ste->expr;
-					if (!IsA(expr, Var))
-						break;
-					if (((Var *) expr)->varno == INNER)
-						subplan = innerPlan(subplan);
-					else
-						subplan = outerPlan(subplan);
-					if (!subplan)
-						break;
-				}
-			}
-			break;
 	}
 
 	/*
@@ -2906,7 +2985,7 @@ get_name_for_var_field(Var *var, int fieldno,
 	if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
 		tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
 												exprTypmod(expr));
-
+	Assert(tupleDesc);
 	/* Got the tupdesc, so we can extract the field name */
 	Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
 	return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
@@ -2947,20 +3026,6 @@ find_rte_by_refname(const char *refname, deparse_context *context)
 				result = rte;
 			}
 		}
-		if (dpns->outer_rte &&
-			strcmp(dpns->outer_rte->eref->aliasname, refname) == 0)
-		{
-			if (result)
-				return NULL;	/* it's ambiguous */
-			result = dpns->outer_rte;
-		}
-		if (dpns->inner_rte &&
-			strcmp(dpns->inner_rte->eref->aliasname, refname) == 0)
-		{
-			if (result)
-				return NULL;	/* it's ambiguous */
-			result = dpns->inner_rte;
-		}
 		if (result)
 			break;
 	}
@@ -3291,39 +3356,11 @@ get_rule_expr(Node *node, deparse_context *context,
 	 * expression tree.  The only exception is that when the input is a List,
 	 * we emit the component items comma-separated with no surrounding
 	 * decoration; this is convenient for most callers.
-	 *
-	 * There might be some work left here to support additional node types.
 	 */
 	switch (nodeTag(node))
 	{
 		case T_Var:
-			{
-				Var		   *var = (Var *) node;
-				char	   *schemaname;
-				char	   *refname;
-				char	   *attname;
-
-				get_names_for_var(var, 0, context,
-								  &schemaname, &refname, &attname);
-				if (refname && (context->varprefix || attname == NULL))
-				{
-					if (schemaname)
-						appendStringInfo(buf, "%s.",
-										 quote_identifier(schemaname));
-
-					if (strcmp(refname, "*NEW*") == 0)
-						appendStringInfoString(buf, "new.");
-					else if (strcmp(refname, "*OLD*") == 0)
-						appendStringInfoString(buf, "old.");
-					else
-						appendStringInfo(buf, "%s.",
-										 quote_identifier(refname));
-				}
-				if (attname)
-					appendStringInfoString(buf, quote_identifier(attname));
-				else
-					appendStringInfoString(buf, "*");
-			}
+			(void) get_variable((Var *) node, 0, true, context);
 			break;
 
 		case T_Const:
@@ -3509,28 +3546,10 @@ get_rule_expr(Node *node, deparse_context *context,
 					appendStringInfoChar(buf, ')');
 
 				/*
-				 * If it's a Var of type RECORD, we have to find what the Var
-				 * refers to; otherwise we can use get_expr_result_type. If
-				 * that fails, we try lookup_rowtype_tupdesc, which will
-				 * probably fail too, but will ereport an acceptable message.
+				 * Get and print the field name.
 				 */
-				if (IsA(arg, Var) &&
-					((Var *) arg)->vartype == RECORDOID)
-					fieldname = get_name_for_var_field((Var *) arg, fno,
-													   0, context);
-				else
-				{
-					TupleDesc	tupdesc;
-
-					if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-						tupdesc = lookup_rowtype_tupdesc_copy(exprType(arg),
-															exprTypmod(arg));
-					Assert(tupdesc);
-					/* Got the tupdesc, so we can extract the field name */
-					Assert(fno >= 1 && fno <= tupdesc->natts);
-					fieldname = NameStr(tupdesc->attrs[fno - 1]->attname);
-				}
-
+				fieldname = get_name_for_var_field((Var *) arg, fno,
+												   0, context);
 				appendStringInfo(buf, ".%s", quote_identifier(fieldname));
 			}
 			break;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index db4357c17b7bf1e7c7de263aa8a1796c80208f5b..70c2142d8d422b9ace7f08ba1891ba69cb01bf4f 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.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/utils/builtins.h,v 1.288 2007/02/17 00:55:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.289 2007/02/23 21:59:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -537,10 +537,8 @@ extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS);
 extern char *deparse_expression(Node *expr, List *dpcontext,
 				   bool forceprefix, bool showimplicit);
 extern List *deparse_context_for(const char *aliasname, Oid relid);
-extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
-						 int inner_varno, Node *innercontext,
-						 List *rtable);
-extern Node *deparse_context_for_subplan(const char *name, Node *subplan);
+extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
+									  List *rtable);
 extern const char *quote_identifier(const char *ident);
 extern char *quote_qualified_identifier(const char *namespace,
 						   const char *ident);