diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f0a491550243ef0491e763ca6c3409b7a8b60d1e..2dce7bab18f28507d275154f829f2c9052e13d1a 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.408 2008/10/07 19:27:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.409 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1592,6 +1592,22 @@ _copyFlattenedSubLink(FlattenedSubLink *from)
 	return newnode;
 }
 
+/*
+ * _copyPlaceHolderVar
+ */
+static PlaceHolderVar *
+_copyPlaceHolderVar(PlaceHolderVar *from)
+{
+	PlaceHolderVar *newnode = makeNode(PlaceHolderVar);
+
+	COPY_NODE_FIELD(phexpr);
+	COPY_BITMAPSET_FIELD(phrels);
+	COPY_SCALAR_FIELD(phid);
+	COPY_SCALAR_FIELD(phlevelsup);
+
+	return newnode;
+}
+
 /*
  * _copySpecialJoinInfo
  */
@@ -1631,6 +1647,23 @@ _copyAppendRelInfo(AppendRelInfo *from)
 	return newnode;
 }
 
+/*
+ * _copyPlaceHolderInfo
+ */
+static PlaceHolderInfo *
+_copyPlaceHolderInfo(PlaceHolderInfo *from)
+{
+	PlaceHolderInfo *newnode = makeNode(PlaceHolderInfo);
+
+	COPY_SCALAR_FIELD(phid);
+	COPY_NODE_FIELD(ph_var);
+	COPY_BITMAPSET_FIELD(ph_eval_at);
+	COPY_BITMAPSET_FIELD(ph_needed);
+	COPY_SCALAR_FIELD(ph_width);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *					parsenodes.h copy functions
  * ****************************************************************
@@ -3438,12 +3471,18 @@ copyObject(void *from)
 		case T_FlattenedSubLink:
 			retval = _copyFlattenedSubLink(from);
 			break;
+		case T_PlaceHolderVar:
+			retval = _copyPlaceHolderVar(from);
+			break;
 		case T_SpecialJoinInfo:
 			retval = _copySpecialJoinInfo(from);
 			break;
 		case T_AppendRelInfo:
 			retval = _copyAppendRelInfo(from);
 			break;
+		case T_PlaceHolderInfo:
+			retval = _copyPlaceHolderInfo(from);
+			break;
 
 			/*
 			 * VALUE NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 96eec16ebc1f985be5fd4538e62622b97fcd03c0..e0b8bdcecb1230ad8e75ca870ef10a5df6d1d10f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.333 2008/10/06 17:39:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.334 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -763,6 +763,27 @@ _equalFlattenedSubLink(FlattenedSubLink *a, FlattenedSubLink *b)
 	return true;
 }
 
+static bool
+_equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b)
+{
+	/*
+	 * We intentionally do not compare phexpr.  Two PlaceHolderVars with the
+	 * same ID and levelsup should be considered equal even if the contained
+	 * expressions have managed to mutate to different states.  One way in
+	 * which that can happen is that initplan sublinks would get replaced by
+	 * differently-numbered Params when sublink folding is done.  (The end
+	 * result of such a situation would be some unreferenced initplans, which
+	 * is annoying but not really a problem.)
+	 *
+	 * COMPARE_NODE_FIELD(phexpr);
+	 */
+	COMPARE_BITMAPSET_FIELD(phrels);
+	COMPARE_SCALAR_FIELD(phid);
+	COMPARE_SCALAR_FIELD(phlevelsup);
+
+	return true;
+}
+
 static bool
 _equalSpecialJoinInfo(SpecialJoinInfo *a, SpecialJoinInfo *b)
 {
@@ -792,6 +813,18 @@ _equalAppendRelInfo(AppendRelInfo *a, AppendRelInfo *b)
 	return true;
 }
 
+static bool
+_equalPlaceHolderInfo(PlaceHolderInfo *a, PlaceHolderInfo *b)
+{
+	COMPARE_SCALAR_FIELD(phid);
+	COMPARE_NODE_FIELD(ph_var);
+	COMPARE_BITMAPSET_FIELD(ph_eval_at);
+	COMPARE_BITMAPSET_FIELD(ph_needed);
+	COMPARE_SCALAR_FIELD(ph_width);
+
+	return true;
+}
+
 
 /*
  * Stuff from parsenodes.h
@@ -2289,12 +2322,19 @@ equal(void *a, void *b)
 		case T_FlattenedSubLink:
 			retval = _equalFlattenedSubLink(a, b);
 			break;
+		case T_PlaceHolderVar:
+			retval = _equalPlaceHolderVar(a, b);
+			break;
 		case T_SpecialJoinInfo:
 			retval = _equalSpecialJoinInfo(a, b);
 			break;
 		case T_AppendRelInfo:
 			retval = _equalAppendRelInfo(a, b);
 			break;
+		case T_PlaceHolderInfo:
+			retval = _equalPlaceHolderInfo(a, b);
+			break;
+
 		case T_List:
 		case T_IntList:
 		case T_OidList:
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index baf98b67634fb05b5bcfcc3b6fb25e02b210ca38..7236360347c1477332b36fa28e2794c55e86a52a 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.34 2008/10/06 17:39:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.35 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -220,6 +220,9 @@ exprType(Node *expr)
 		case T_CurrentOfExpr:
 			type = BOOLOID;
 			break;
+		case T_PlaceHolderVar:
+			type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr);
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			type = InvalidOid;	/* keep compiler quiet */
@@ -420,6 +423,8 @@ exprTypmod(Node *expr)
 			return ((CoerceToDomainValue *) expr)->typeMod;
 		case T_SetToDefault:
 			return ((SetToDefault *) expr)->typeMod;
+		case T_PlaceHolderVar:
+			return exprTypmod((Node *) ((PlaceHolderVar *) expr)->phexpr);
 		default:
 			break;
 	}
@@ -876,6 +881,10 @@ exprLocation(Node *expr)
 		case T_CommonTableExpr:
 			loc = ((CommonTableExpr *) expr)->location;
 			break;
+		case T_PlaceHolderVar:
+			/* just use argument's location */
+			loc = exprLocation((Node *) ((PlaceHolderVar *) expr)->phexpr);
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -1272,11 +1281,12 @@ expression_tree_walker(Node *node,
 			{
 				FlattenedSubLink *fslink = (FlattenedSubLink *) node;
 
-				if (expression_tree_walker((Node *) fslink->quals,
-										   walker, context))
+				if (walker(fslink->quals, context))
 					return true;
 			}
 			break;
+		case T_PlaceHolderVar:
+			return walker(((PlaceHolderVar *) node)->phexpr, context);
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -1286,6 +1296,8 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_PlaceHolderInfo:
+			return walker(((PlaceHolderInfo *) node)->ph_var, context);
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -1918,6 +1930,17 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_PlaceHolderVar:
+			{
+				PlaceHolderVar *phv = (PlaceHolderVar *) node;
+				PlaceHolderVar *newnode;
+
+				FLATCOPY(newnode, phv, PlaceHolderVar);
+				MUTATE(newnode->phexpr, phv->phexpr, Expr *);
+				/* Assume we need not copy the relids bitmapset */
+				return (Node *) newnode;
+			}
+			break;
 		case T_AppendRelInfo:
 			{
 				AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -1928,6 +1951,17 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_PlaceHolderInfo:
+			{
+				PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+				PlaceHolderInfo *newnode;
+
+				FLATCOPY(newnode, phinfo, PlaceHolderInfo);
+				MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *);
+				/* Assume we need not copy the relids bitmapsets */
+				return (Node *) newnode;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 75a04ce2fa14e6bb59844b92685dc961f47c05a3..d1130760f9e94b03d08c5487e9f64ac940656d9c 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.342 2008/10/07 19:27:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.343 2008/10/21 20:42:52 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1412,6 +1412,8 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
 	WRITE_NODE_FIELD(finalrtable);
 	WRITE_NODE_FIELD(relationOids);
 	WRITE_NODE_FIELD(invalItems);
+	WRITE_UINT_FIELD(lastPHId);
+	WRITE_BOOL_FIELD(transientPlan);
 }
 
 static void
@@ -1435,6 +1437,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
 	WRITE_NODE_FIELD(full_join_clauses);
 	WRITE_NODE_FIELD(join_info_list);
 	WRITE_NODE_FIELD(append_rel_list);
+	WRITE_NODE_FIELD(placeholder_list);
 	WRITE_NODE_FIELD(query_pathkeys);
 	WRITE_NODE_FIELD(group_pathkeys);
 	WRITE_NODE_FIELD(distinct_pathkeys);
@@ -1590,6 +1593,17 @@ _outFlattenedSubLink(StringInfo str, FlattenedSubLink *node)
 	WRITE_NODE_FIELD(quals);
 }
 
+static void
+_outPlaceHolderVar(StringInfo str, PlaceHolderVar *node)
+{
+	WRITE_NODE_TYPE("PLACEHOLDERVAR");
+
+	WRITE_NODE_FIELD(phexpr);
+	WRITE_BITMAPSET_FIELD(phrels);
+	WRITE_UINT_FIELD(phid);
+	WRITE_UINT_FIELD(phlevelsup);
+}
+
 static void
 _outSpecialJoinInfo(StringInfo str, SpecialJoinInfo *node)
 {
@@ -1619,6 +1633,18 @@ _outAppendRelInfo(StringInfo str, AppendRelInfo *node)
 	WRITE_OID_FIELD(parent_reloid);
 }
 
+static void
+_outPlaceHolderInfo(StringInfo str, PlaceHolderInfo *node)
+{
+	WRITE_NODE_TYPE("PLACEHOLDERINFO");
+
+	WRITE_UINT_FIELD(phid);
+	WRITE_NODE_FIELD(ph_var);
+	WRITE_BITMAPSET_FIELD(ph_eval_at);
+	WRITE_BITMAPSET_FIELD(ph_needed);
+	WRITE_INT_FIELD(ph_width);
+}
+
 static void
 _outPlannerParamItem(StringInfo str, PlannerParamItem *node)
 {
@@ -2539,12 +2565,18 @@ _outNode(StringInfo str, void *obj)
 			case T_FlattenedSubLink:
 				_outFlattenedSubLink(str, obj);
 				break;
+			case T_PlaceHolderVar:
+				_outPlaceHolderVar(str, obj);
+				break;
 			case T_SpecialJoinInfo:
 				_outSpecialJoinInfo(str, obj);
 				break;
 			case T_AppendRelInfo:
 				_outAppendRelInfo(str, obj);
 				break;
+			case T_PlaceHolderInfo:
+				_outPlaceHolderInfo(str, obj);
+				break;
 			case T_PlannerParamItem:
 				_outPlannerParamItem(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index a942249fd09ab3cc257fb2f6f93bbfcc0912e87d..7d6a3b8d6b58a1c053fb376516f68d4d31d3799a 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.174 2008/10/04 21:56:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.175 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -423,6 +423,10 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				Var		   *parentvar = (Var *) lfirst(parentvars);
 				Var		   *childvar = (Var *) lfirst(childvars);
 
+				/*
+				 * Accumulate per-column estimates too.  Whole-row Vars and
+				 * PlaceHolderVars can be ignored here.
+				 */
 				if (IsA(parentvar, Var) &&
 					IsA(childvar, Var))
 				{
@@ -1105,12 +1109,25 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
 	 * Examine all Vars used in clause; since it's a restriction clause, all
 	 * such Vars must refer to subselect output columns.
 	 */
-	vars = pull_var_clause(qual, false);
+	vars = pull_var_clause(qual, true);
 	foreach(vl, vars)
 	{
 		Var		   *var = (Var *) lfirst(vl);
 		TargetEntry *tle;
 
+		/*
+		 * XXX Punt if we find any PlaceHolderVars in the restriction clause.
+		 * It's not clear whether a PHV could safely be pushed down, and even
+		 * less clear whether such a situation could arise in any cases of
+		 * practical interest anyway.  So for the moment, just refuse to push
+		 * down.
+		 */
+		if (!IsA(var, Var))
+		{
+			safe = false;
+			break;
+		}
+
 		Assert(var->varno == rti);
 
 		/* Check point 2 */
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fa580cf14e6796383e7dd586fe81cc914dc1d095..ba33da829a957553da31d1fc3a6e41f79766dfc9 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.199 2008/10/17 20:27:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.200 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,6 +69,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
+#include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
@@ -2610,55 +2611,66 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 {
 	Oid			reloid = planner_rt_fetch(rel->relid, root)->relid;
 	int32		tuple_width = 0;
-	ListCell   *tllist;
+	ListCell   *lc;
 
-	foreach(tllist, rel->reltargetlist)
+	foreach(lc, rel->reltargetlist)
 	{
-		Var		   *var = (Var *) lfirst(tllist);
-		int			ndx;
-		int32		item_width;
+		Node	   *node = (Node *) lfirst(lc);
 
-		/* For now, punt on whole-row child Vars */
-		if (!IsA(var, Var))
+		if (IsA(node, Var))
 		{
-			tuple_width += 32;	/* arbitrary */
-			continue;
-		}
-
-		Assert(var->varno == rel->relid);
-		Assert(var->varattno >= rel->min_attr);
-		Assert(var->varattno <= rel->max_attr);
+			Var		   *var = (Var *) node;
+			int			ndx;
+			int32		item_width;
 
-		ndx = var->varattno - rel->min_attr;
+			Assert(var->varno == rel->relid);
+			Assert(var->varattno >= rel->min_attr);
+			Assert(var->varattno <= rel->max_attr);
 
-		/*
-		 * The width probably hasn't been cached yet, but may as well check
-		 */
-		if (rel->attr_widths[ndx] > 0)
-		{
-			tuple_width += rel->attr_widths[ndx];
-			continue;
-		}
+			ndx = var->varattno - rel->min_attr;
 
-		if (reloid != InvalidOid)
-		{
-			item_width = get_attavgwidth(reloid, var->varattno);
-			if (item_width > 0)
+			/*
+			 * The width probably hasn't been cached yet, but may as well check
+			 */
+			if (rel->attr_widths[ndx] > 0)
 			{
-				rel->attr_widths[ndx] = item_width;
-				tuple_width += item_width;
+				tuple_width += rel->attr_widths[ndx];
 				continue;
 			}
+
+			/* Try to get column width from statistics */
+			if (reloid != InvalidOid)
+			{
+				item_width = get_attavgwidth(reloid, var->varattno);
+				if (item_width > 0)
+				{
+					rel->attr_widths[ndx] = item_width;
+					tuple_width += item_width;
+					continue;
+				}
+			}
+
+			/*
+			 * Not a plain relation, or can't find statistics for it. Estimate
+			 * using just the type info.
+			 */
+			item_width = get_typavgwidth(var->vartype, var->vartypmod);
+			Assert(item_width > 0);
+			rel->attr_widths[ndx] = item_width;
+			tuple_width += item_width;
 		}
+		else if (IsA(node, PlaceHolderVar))
+		{
+			PlaceHolderVar *phv = (PlaceHolderVar *) node;
+			PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
 
-		/*
-		 * Not a plain relation, or can't find statistics for it. Estimate
-		 * using just the type info.
-		 */
-		item_width = get_typavgwidth(var->vartype, var->vartypmod);
-		Assert(item_width > 0);
-		rel->attr_widths[ndx] = item_width;
-		tuple_width += item_width;
+			tuple_width += phinfo->ph_width;
+		}
+		else
+		{
+			/* For now, punt on whole-row child Vars */
+			tuple_width += 32;	/* arbitrary */
+		}
 	}
 	Assert(tuple_width >= 0);
 	rel->width = tuple_width;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0847b95574a58972f3157949f4eff58e865bf6b0..c0351008750aab52e65f177b2f5623346cd30cca 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.12 2008/08/25 22:42:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.13 2008/10/21 20:42:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -687,7 +687,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 	foreach(lc, ec->ec_members)
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
-		List	   *vars = pull_var_clause((Node *) cur_em->em_expr, false);
+		List	   *vars = pull_var_clause((Node *) cur_em->em_expr, true);
 
 		add_vars_to_targetlist(root, vars, ec->ec_relids);
 		list_free(vars);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index f812c93979495dfe80a69dcf5c2d5df0b7984943..7bddf33e5c87a84bdde2d6d155586c0a9d5f13f9 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.250 2008/10/07 19:27:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.251 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@
 
 static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
 static List *build_relation_tlist(RelOptInfo *rel);
-static bool use_physical_tlist(RelOptInfo *rel);
+static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
 static void disuse_physical_tlist(Plan *plan, Path *path);
 static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
@@ -212,7 +212,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 	 * planner.c may replace the tlist we generate here, forcing projection to
 	 * occur.)
 	 */
-	if (use_physical_tlist(rel))
+	if (use_physical_tlist(root, rel))
 	{
 		tlist = build_physical_tlist(root, rel);
 		/* if fail because of dropped cols, use regular method */
@@ -325,9 +325,9 @@ build_relation_tlist(RelOptInfo *rel)
 	foreach(v, rel->reltargetlist)
 	{
 		/* Do we really need to copy here?	Not sure */
-		Var		   *var = (Var *) copyObject(lfirst(v));
+		Node   *node = (Node *) copyObject(lfirst(v));
 
-		tlist = lappend(tlist, makeTargetEntry((Expr *) var,
+		tlist = lappend(tlist, makeTargetEntry((Expr *) node,
 											   resno,
 											   NULL,
 											   false));
@@ -342,9 +342,10 @@ build_relation_tlist(RelOptInfo *rel)
  *		rather than only those Vars actually referenced.
  */
 static bool
-use_physical_tlist(RelOptInfo *rel)
+use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
 {
 	int			i;
+	ListCell   *lc;
 
 	/*
 	 * We can do this for real relation scans, subquery scans, function scans,
@@ -365,9 +366,9 @@ use_physical_tlist(RelOptInfo *rel)
 		return false;
 
 	/*
-	 * Can't do it if any system columns or whole-row Vars are requested,
-	 * either.	(This could possibly be fixed but would take some fragile
-	 * assumptions in setrefs.c, I think.)
+	 * Can't do it if any system columns or whole-row Vars are requested.
+	 * (This could possibly be fixed but would take some fragile assumptions
+	 * in setrefs.c, I think.)
 	 */
 	for (i = rel->min_attr; i <= 0; i++)
 	{
@@ -375,6 +376,19 @@ use_physical_tlist(RelOptInfo *rel)
 			return false;
 	}
 
+	/*
+	 * Can't do it if the rel is required to emit any placeholder expressions,
+	 * either.
+	 */
+	foreach(lc, root->placeholder_list)
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+		if (bms_nonempty_difference(phinfo->ph_needed, rel->relids) &&
+			bms_is_subset(phinfo->ph_eval_at, rel->relids))
+			return false;
+	}
+
 	return true;
 }
 
@@ -2925,7 +2939,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
 					if (em->em_is_const || em->em_is_child)
 						continue;
 					sortexpr = em->em_expr;
-					exprvars = pull_var_clause((Node *) sortexpr, false);
+					exprvars = pull_var_clause((Node *) sortexpr, true);
 					foreach(k, exprvars)
 					{
 						if (!tlist_member_ignore_relabel(lfirst(k), tlist))
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 49c2cdf2f55ce8e15e0225b7d65b2a6d96405abd..1208262b8e4b83487669900846be9a241479fae9 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.142 2008/08/17 01:19:59 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.143 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
@@ -131,7 +132,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
 void
 build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
 {
-	List	   *tlist_vars = pull_var_clause((Node *) final_tlist, false);
+	List	   *tlist_vars = pull_var_clause((Node *) final_tlist, true);
 
 	if (tlist_vars != NIL)
 	{
@@ -146,6 +147,10 @@ build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
  *	  relation's targetlist if not already present, and mark the variable
  *	  as being needed for the indicated join (or for final output if
  *	  where_needed includes "relation 0").
+ *
+ *	  The list may also contain PlaceHolderVars.  These don't necessarily
+ *	  have a single owning relation; we keep their attr_needed info in
+ *	  root->placeholder_list instead.
  */
 void
 add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed)
@@ -156,20 +161,36 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed)
 
 	foreach(temp, vars)
 	{
-		Var		   *var = (Var *) lfirst(temp);
-		RelOptInfo *rel = find_base_rel(root, var->varno);
-		int			attrno = var->varattno;
+		Node	   *node = (Node *) lfirst(temp);
+
+		if (IsA(node, Var))
+		{
+			Var		   *var = (Var *) node;
+			RelOptInfo *rel = find_base_rel(root, var->varno);
+			int			attno = var->varattno;
 
-		Assert(attrno >= rel->min_attr && attrno <= rel->max_attr);
-		attrno -= rel->min_attr;
-		if (bms_is_empty(rel->attr_needed[attrno]))
+			Assert(attno >= rel->min_attr && attno <= rel->max_attr);
+			attno -= rel->min_attr;
+			if (rel->attr_needed[attno] == NULL)
+			{
+				/* Variable not yet requested, so add to reltargetlist */
+				/* XXX is copyObject necessary here? */
+				rel->reltargetlist = lappend(rel->reltargetlist,
+											 copyObject(var));
+			}
+			rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],
+													  where_needed);
+		}
+		else if (IsA(node, PlaceHolderVar))
 		{
-			/* Variable not yet requested, so add to reltargetlist */
-			/* XXX is copyObject necessary here? */
-			rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var));
+			PlaceHolderVar *phv = (PlaceHolderVar *) node;
+			PlaceHolderInfo *phinfo = find_placeholder_info(root, phv);
+
+			phinfo->ph_needed = bms_add_members(phinfo->ph_needed,
+												where_needed);
 		}
-		rel->attr_needed[attrno] = bms_add_members(rel->attr_needed[attrno],
-												   where_needed);
+		else
+			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 	}
 }
 
@@ -934,7 +955,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	 */
 	if (bms_membership(relids) == BMS_MULTIPLE)
 	{
-		List	   *vars = pull_var_clause(clause, false);
+		List	   *vars = pull_var_clause(clause, true);
 
 		add_vars_to_targetlist(root, vars, relids);
 		list_free(vars);
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 7dcdaf250f9b2ca16a1db527fb3e5ca13be94ae0..171724983dc223e70255f92467f4bcc09857d4d6 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.110 2008/08/14 18:47:59 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.111 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
 #include "utils/selfuncs.h"
@@ -132,7 +133,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	 * for "simple" rels.
 	 *
 	 * NOTE: append_rel_list was set up by subquery_planner, so do not touch
-	 * here; eq_classes may contain data already, too.
+	 * here; ditto placeholder_list; eq_classes may contain data already, too.
 	 */
 	root->simple_rel_array_size = list_length(parse->rtable) + 1;
 	root->simple_rel_array = (RelOptInfo **)
@@ -204,12 +205,6 @@ query_planner(PlannerInfo *root, List *tlist,
 	 * added to appropriate lists belonging to the mentioned relations.  We
 	 * also build EquivalenceClasses for provably equivalent expressions, and
 	 * form a target joinlist for make_one_rel() to work from.
-	 *
-	 * Note: all subplan nodes will have "flat" (var-only) tlists. This
-	 * implies that all expression evaluations are done at the root of the
-	 * plan tree. Once upon a time there was code to try to push expensive
-	 * function calls down to lower plan nodes, but that's dead code and has
-	 * been for a long time...
 	 */
 	build_base_rel_tlists(root, tlist);
 
@@ -240,6 +235,13 @@ query_planner(PlannerInfo *root, List *tlist,
 	root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
 	root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys);
 
+	/*
+	 * Examine any "placeholder" expressions generated during subquery pullup.
+	 * Make sure that we know what level to evaluate them at, and that the
+	 * Vars they need are marked as needed.
+	 */
+	fix_placeholder_eval_levels(root);
+
 	/*
 	 * Ready to do the primary planning.
 	 */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0a704dc65452a3d1908653f9a4fae96fada69d26..05de001a8775ab1084722c09167fb56fe56f35e1 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.244 2008/10/04 21:56:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.245 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,7 @@ planner_hook_type planner_hook = NULL;
 #define EXPRKIND_RTFUNC		2
 #define EXPRKIND_VALUES		3
 #define EXPRKIND_LIMIT		4
-#define EXPRKIND_APPINFO	5
+#define EXPRKIND_AUXINFO	5
 
 
 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
@@ -141,6 +141,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	glob->finalrtable = NIL;
 	glob->relationOids = NIL;
 	glob->invalItems = NIL;
+	glob->lastPHId = 0;
 	glob->transientPlan = false;
 
 	/* Determine what fraction of the plan is likely to be scanned */
@@ -273,6 +274,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->cte_plan_ids = NIL;
 	root->eq_classes = NIL;
 	root->append_rel_list = NIL;
+	root->placeholder_list = NIL;
 
 	root->hasRecursion = hasRecursion;
 	if (hasRecursion)
@@ -378,7 +380,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 
 	root->append_rel_list = (List *)
 		preprocess_expression(root, (Node *) root->append_rel_list,
-							  EXPRKIND_APPINFO);
+							  EXPRKIND_AUXINFO);
+	root->placeholder_list = (List *)
+		preprocess_expression(root, (Node *) root->placeholder_list,
+							  EXPRKIND_AUXINFO);
 
 	/* Also need to preprocess expressions for function and values RTEs */
 	foreach(l, parse->rtable)
@@ -656,7 +661,12 @@ inheritance_planner(PlannerInfo *root)
 		subroot.parse = (Query *)
 			adjust_appendrel_attrs((Node *) parse,
 								   appinfo);
+		subroot.returningLists = NIL;
 		subroot.init_plans = NIL;
+		/* We needn't modify the child's append_rel_list */
+		subroot.placeholder_list = (List *)
+			adjust_appendrel_attrs((Node *) root->placeholder_list,
+								   appinfo);
 		/* There shouldn't be any OJ info to translate, as yet */
 		Assert(subroot.join_info_list == NIL);
 
@@ -2046,7 +2056,7 @@ make_subplanTargetList(PlannerInfo *root,
 	 * Vars; they will be replaced by Params later on).
 	 */
 	sub_tlist = flatten_tlist(tlist);
-	extravars = pull_var_clause(parse->havingQual, false);
+	extravars = pull_var_clause(parse->havingQual, true);
 	sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
 	list_free(extravars);
 	*need_tlist_eval = false;	/* only eval if not flat tlist */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6d7ec283b347bb04e35566130f066f8c96f34f6c..9bec109f6f572fc793b91cf93f4bf26f35e9c2d1 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.145 2008/10/04 21:56:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.146 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,8 @@ typedef struct
 {
 	List	   *tlist;			/* underlying target list */
 	int			num_vars;		/* number of plain Var tlist entries */
-	bool		has_non_vars;	/* are there non-plain-Var entries? */
+	bool		has_ph_vars;	/* are there PlaceHolderVar entries? */
+	bool		has_non_vars;	/* are there other entries? */
 	/* array of num_vars entries: */
 	tlist_vinfo vars[1];		/* VARIABLE LENGTH ARRAY */
 } indexed_tlist;				/* VARIABLE LENGTH STRUCT */
@@ -741,15 +742,16 @@ fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset)
 	context.glob = glob;
 	context.rtoffset = rtoffset;
 
-	if (rtoffset != 0)
+	if (rtoffset != 0 || glob->lastPHId != 0)
 	{
 		return fix_scan_expr_mutator(node, &context);
 	}
 	else
 	{
 		/*
-		 * If rtoffset == 0, we don't need to change any Vars, which makes
-		 * it OK to just scribble on the input node tree instead of copying
+		 * If rtoffset == 0, we don't need to change any Vars, and if there
+		 * are no placeholders anywhere we won't need to remove them.  Then
+		 * it's OK to just scribble on the input node tree instead of copying
 		 * (since the only change, filling in any unset opfuncid fields,
 		 * is harmless).  This saves just enough cycles to be noticeable on
 		 * trivial queries.
@@ -790,6 +792,13 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
 		cexpr->cvarno += context->rtoffset;
 		return (Node *) cexpr;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		/* At scan level, we should always just evaluate the contained expr */
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+	}
 	fix_expr_common(context->glob, node);
 	return expression_tree_mutator(node, fix_scan_expr_mutator,
 								   (void *) context);
@@ -800,6 +809,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
 {
 	if (node == NULL)
 		return false;
+	Assert(!IsA(node, PlaceHolderVar));
 	fix_expr_common(context->glob, node);
 	return expression_tree_walker(node, fix_scan_expr_walker,
 								  (void *) context);
@@ -1199,6 +1209,7 @@ build_tlist_index(List *tlist)
 			   list_length(tlist) * sizeof(tlist_vinfo));
 
 	itlist->tlist = tlist;
+	itlist->has_ph_vars = false;
 	itlist->has_non_vars = false;
 
 	/* Find the Vars and fill in the index array */
@@ -1216,6 +1227,8 @@ build_tlist_index(List *tlist)
 			vinfo->resno = tle->resno;
 			vinfo++;
 		}
+		else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+			itlist->has_ph_vars = true;
 		else
 			itlist->has_non_vars = true;
 	}
@@ -1229,7 +1242,9 @@ build_tlist_index(List *tlist)
  * build_tlist_index_other_vars --- build a restricted tlist index
  *
  * This is like build_tlist_index, but we only index tlist entries that
- * are Vars and belong to some rel other than the one specified.
+ * are Vars belonging to some rel other than the one specified.  We will set
+ * has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
+ * (so nothing other than Vars and PlaceHolderVars can be matched).
  */
 static indexed_tlist *
 build_tlist_index_other_vars(List *tlist, Index ignore_rel)
@@ -1244,6 +1259,7 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
 			   list_length(tlist) * sizeof(tlist_vinfo));
 
 	itlist->tlist = tlist;
+	itlist->has_ph_vars = false;
 	itlist->has_non_vars = false;
 
 	/* Find the desired Vars and fill in the index array */
@@ -1264,6 +1280,8 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
 				vinfo++;
 			}
 		}
+		else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
+			itlist->has_ph_vars = true;
 	}
 
 	itlist->num_vars = (vinfo - itlist->vars);
@@ -1314,7 +1332,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
  * If a match is found, return a Var constructed to reference the tlist item.
  * If no match, return NULL.
  *
- * NOTE: it is a waste of time to call this if !itlist->has_non_vars
+ * NOTE: it is a waste of time to call this unless itlist->has_ph_vars or
+ * itlist->has_non_vars
  */
 static Var *
 search_indexed_tlist_for_non_var(Node *node,
@@ -1429,6 +1448,31 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		/* No referent found for Var */
 		elog(ERROR, "variable not found in subplan target lists");
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
+		if (context->outer_itlist->has_ph_vars)
+		{
+			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+													  context->outer_itlist,
+													  OUTER);
+			if (newvar)
+				return (Node *) newvar;
+		}
+		if (context->inner_itlist && context->inner_itlist->has_ph_vars)
+		{
+			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+													  context->inner_itlist,
+													  INNER);
+			if (newvar)
+				return (Node *) newvar;
+		}
+
+		/* If not supplied by input plans, evaluate the contained expr */
+		return fix_join_expr_mutator((Node *) phv->phexpr, context);
+	}
 	/* Try matching more complex expressions too, if tlists have any */
 	if (context->outer_itlist->has_non_vars)
 	{
@@ -1512,6 +1556,22 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 			elog(ERROR, "variable not found in subplan target list");
 		return (Node *) newvar;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
+		if (context->subplan_itlist->has_ph_vars)
+		{
+			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+													  context->subplan_itlist,
+													  OUTER);
+			if (newvar)
+				return (Node *) newvar;
+		}
+		/* If not supplied by input plan, evaluate the contained expr */
+		return fix_upper_expr_mutator((Node *) phv->phexpr, context);
+	}
 	/* Try matching more complex expressions too, if tlist has any */
 	if (context->subplan_itlist->has_non_vars)
 	{
@@ -1564,6 +1624,13 @@ set_returning_clause_references(PlannerGlobal *glob,
 	 * top plan's targetlist for Vars of non-result relations, and use
 	 * fix_join_expr to convert RETURNING Vars into references to those tlist
 	 * entries, while leaving result-rel Vars as-is.
+	 *
+	 * PlaceHolderVars will also be sought in the targetlist, but no
+	 * more-complex expressions will be.  Note that it is not possible for
+	 * a PlaceHolderVar to refer to the result relation, since the result
+	 * is never below an outer join.  If that case could happen, we'd have
+	 * to be prepared to pick apart the PlaceHolderVar and evaluate its
+	 * contained expression instead.
 	 */
 	itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
 
@@ -1721,6 +1788,7 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context)
 {
 	if (node == NULL)
 		return false;
+	Assert(!IsA(node, PlaceHolderVar));
 	/* Extract function dependencies and check for regclass Consts */
 	fix_expr_common(context, node);
 	if (IsA(node, Query))
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 00d84d56823c33cec74c8bc1e98efead6e48a5e6..b80427f041e5a42e35e5357dccb4c39827e78ec4 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.141 2008/10/04 21:56:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.142 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1040,6 +1040,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 
 	/*
 	 * And finally, build the FlattenedSubLink node.
+	 *
+	 * Note: at this point left_varnos may well contain join relids, since
+	 * the testexpr hasn't been run through flatten_join_alias_vars.  This
+	 * will get fixed when flatten_join_alias_vars is run.
 	 */
 	fslink = makeNode(FlattenedSubLink);
 	fslink->jointype = JOIN_SEMI;
@@ -1189,6 +1193,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 
 	/*
 	 * And finally, build the FlattenedSubLink node.
+	 *
+	 * Note: at this point left_varnos and subselect_varnos may well contain
+	 * join relids.  This will get fixed when flatten_join_alias_vars is run.
 	 */
 	fslink = makeNode(FlattenedSubLink);
 	fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 1c73c57dadf9d5527291d7bb712a54be52fd1086..45de509619874b518b8b6c624d752ab1b48efb81 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.56 2008/10/09 19:27:40 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.57 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/placeholder.h"
 #include "optimizer/prep.h"
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
@@ -60,7 +61,8 @@ static bool is_simple_subquery(Query *subquery);
 static bool is_simple_union_all(Query *subquery);
 static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
 							List *colTypes);
-static bool has_nullable_targetlist(Query *subquery);
+static List *insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+											int varno, bool wrap_non_vars);
 static bool is_safe_append_member(Query *subquery);
 static void resolvenew_in_jointree(Node *jtnode, int varno,
 					   RangeTblEntry *rte, List *subtlist);
@@ -71,8 +73,8 @@ static void reduce_outer_joins_pass2(Node *jtnode,
 						 Relids nonnullable_rels,
 						 List *nonnullable_vars,
 						 List *forced_null_vars);
-static void fix_flattened_sublink_relids(Node *node,
-										 int varno, Relids subrelids);
+static void substitute_multiple_relids(Node *node,
+									   int varno, Relids subrelids);
 static void fix_append_rel_relids(List *append_rel_list, int varno,
 					  Relids subrelids);
 static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
@@ -406,11 +408,13 @@ inline_set_returning_functions(PlannerInfo *root)
  *		converted into "append relations".
  *
  * below_outer_join is true if this jointree node is within the nullable
- * side of an outer join.  This restricts what we can do.
+ * side of an outer join.  This forces use of the PlaceHolderVar mechanism
+ * for non-nullable targetlist items.
  *
  * append_rel_member is true if we are looking at a member subquery of
- * an append relation.	This puts some different restrictions on what
- * we can do.
+ * an append relation.	This forces use of the PlaceHolderVar mechanism
+ * for all non-Var targetlist items, and puts some additional restrictions
+ * on what can be pulled up.
  *
  * A tricky aspect of this code is that if we pull up a subquery we have
  * to replace Vars that reference the subquery's outputs throughout the
@@ -434,24 +438,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 
 		/*
 		 * Is this a subquery RTE, and if so, is the subquery simple enough to
-		 * pull up?  (If not, do nothing at this node.)
-		 *
-		 * If we are inside an outer join, only pull up subqueries whose
-		 * targetlists are nullable --- otherwise substituting their tlist
-		 * entries for upper Var references would do the wrong thing (the
-		 * results wouldn't become NULL when they're supposed to).
-		 *
-		 * XXX This could be improved by generating pseudo-variables for such
-		 * expressions; we'd have to figure out how to get the pseudo-
-		 * variables evaluated at the right place in the modified plan tree.
-		 * Fix it someday.
+		 * pull up?
 		 *
 		 * If we are looking at an append-relation member, we can't pull it up
 		 * unless is_safe_append_member says so.
 		 */
 		if (rte->rtekind == RTE_SUBQUERY &&
 			is_simple_subquery(rte->subquery) &&
-			(!below_outer_join || has_nullable_targetlist(rte->subquery)) &&
 			(!append_rel_member || is_safe_append_member(rte->subquery)))
 			return pull_up_simple_subquery(root, jtnode, rte,
 										   below_outer_join,
@@ -459,12 +452,9 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 
 		/*
 		 * Alternatively, is it a simple UNION ALL subquery?  If so, flatten
-		 * into an "append relation".  We can do this regardless of
-		 * nullability considerations since this transformation does not
-		 * result in propagating non-Var expressions into upper levels of the
-		 * query.
+		 * into an "append relation".
 		 *
-		 * It's also safe to do this regardless of whether this query is
+		 * It's safe to do this regardless of whether this query is
 		 * itself an appendrel member.	(If you're thinking we should try to
 		 * flatten the two levels of appendrel together, you're right; but we
 		 * handle that in set_append_rel_pathlist, not here.)
@@ -472,6 +462,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 		if (rte->rtekind == RTE_SUBQUERY &&
 			is_simple_union_all(rte->subquery))
 			return pull_up_simple_union_all(root, jtnode, rte);
+
+		/* Otherwise, do nothing at this node. */
 	}
 	else if (IsA(jtnode, FromExpr))
 	{
@@ -573,6 +565,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->cte_plan_ids = NIL;
 	subroot->eq_classes = NIL;
 	subroot->append_rel_list = NIL;
+	subroot->placeholder_list = NIL;
 	subroot->hasRecursion = false;
 	subroot->wt_param_id = -1;
 	subroot->non_recursive_plan = NULL;
@@ -614,7 +607,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 * pull_up_subqueries.
 	 */
 	if (is_simple_subquery(subquery) &&
-		(!below_outer_join || has_nullable_targetlist(subquery)) &&
 		(!append_rel_member || is_safe_append_member(subquery)))
 	{
 		/* good to go */
@@ -635,11 +627,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	/*
 	 * Adjust level-0 varnos in subquery so that we can append its rangetable
 	 * to upper query's.  We have to fix the subquery's append_rel_list
-	 * as well.
+	 * and placeholder_list as well.
 	 */
 	rtoffset = list_length(parse->rtable);
 	OffsetVarNodes((Node *) subquery, rtoffset, 0);
 	OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
+	OffsetVarNodes((Node *) subroot->placeholder_list, rtoffset, 0);
 
 	/*
 	 * Upper-level vars in subquery are now one level closer to their parent
@@ -647,6 +640,20 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 */
 	IncrementVarSublevelsUp((Node *) subquery, -1, 1);
 	IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
+	IncrementVarSublevelsUp((Node *) subroot->placeholder_list, -1, 1);
+
+	/*
+	 * The subquery's targetlist items are now in the appropriate form to
+	 * insert into the top query, but if we are under an outer join then
+	 * non-nullable items have to be turned into PlaceHolderVars.  If we
+	 * are dealing with an appendrel member then anything that's not a
+	 * simple Var has to be turned into a PlaceHolderVar.
+	 */
+	if (below_outer_join || append_rel_member)
+		subtlist = insert_targetlist_placeholders(root, subquery->targetList,
+												  varno, append_rel_member);
+	else
+		subtlist = subquery->targetList;
 
 	/*
 	 * Replace all of the top query's references to the subquery's outputs
@@ -654,7 +661,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 * replace any of the jointree structure. (This'd be a lot cleaner if we
 	 * could use query_tree_mutator.)
 	 */
-	subtlist = subquery->targetList;
 	parse->targetList = (List *)
 		ResolveNew((Node *) parse->targetList,
 				   varno, 0, rte,
@@ -700,29 +706,41 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
 
 	/*
-	 * We also have to fix the relid sets of any FlattenedSubLink nodes in
-	 * the parent query.  (This could perhaps be done by ResolveNew, but it
-	 * would clutter that routine's API unreasonably.)
+	 * We also have to fix the relid sets of any FlattenedSubLink,
+	 * PlaceHolderVar, and PlaceHolderInfo nodes in the parent query.
+	 * (This could perhaps be done by ResolveNew, but it would clutter that
+	 * routine's API unreasonably.)  Note in particular that any placeholder
+	 * nodes just created by insert_targetlist_placeholders() wiil be adjusted.
 	 *
 	 * Likewise, relids appearing in AppendRelInfo nodes have to be fixed (but
 	 * we took care of their translated_vars lists above).	We already checked
 	 * that this won't require introducing multiple subrelids into the
 	 * single-slot AppendRelInfo structs.
 	 */
-	if (parse->hasSubLinks || root->append_rel_list)
+	if (parse->hasSubLinks || root->placeholder_list || root->append_rel_list)
 	{
 		Relids		subrelids;
 
 		subrelids = get_relids_in_jointree((Node *) subquery->jointree, false);
-		fix_flattened_sublink_relids((Node *) parse, varno, subrelids);
-		fix_append_rel_relids(root->append_rel_list, varno, subrelids);
+		substitute_multiple_relids((Node *) parse,
+								   varno, subrelids);
+		substitute_multiple_relids((Node *) root->placeholder_list,
+								   varno, subrelids);
+		fix_append_rel_relids(root->append_rel_list,
+							  varno, subrelids);
 	}
 
 	/*
-	 * And now add subquery's AppendRelInfos to our list.
+	 * And now add subquery's AppendRelInfos and PlaceHolderInfos to our lists.
+	 * Note that any placeholders pulled up from the subquery will appear
+	 * after any we just created; this preserves the property that placeholders
+	 * can only refer to other placeholders that appear later in the list
+	 * (needed by fix_placeholder_eval_levels).
 	 */
 	root->append_rel_list = list_concat(root->append_rel_list,
 										subroot->append_rel_list);
+	root->placeholder_list = list_concat(root->placeholder_list,
+										 subroot->placeholder_list);
 
 	/*
 	 * We don't have to do the equivalent bookkeeping for outer-join info,
@@ -950,7 +968,9 @@ is_simple_subquery(Query *subquery)
 	 * Don't pull up a subquery that has any volatile functions in its
 	 * targetlist.	Otherwise we might introduce multiple evaluations of these
 	 * functions, if they get copied to multiple places in the upper query,
-	 * leading to surprising results.
+	 * leading to surprising results.  (Note: the PlaceHolderVar mechanism
+	 * doesn't quite guarantee single evaluation; else we could pull up anyway
+	 * and just wrap such items in PlaceHolderVars ...)
 	 */
 	if (contain_volatile_functions((Node *) subquery->targetList))
 		return false;
@@ -959,8 +979,12 @@ is_simple_subquery(Query *subquery)
 	 * Hack: don't try to pull up a subquery with an empty jointree.
 	 * query_planner() will correctly generate a Result plan for a jointree
 	 * that's totally empty, but I don't think the right things happen if an
-	 * empty FromExpr appears lower down in a jointree. Not worth working hard
-	 * on this, just to collapse SubqueryScan/Result into Result...
+	 * empty FromExpr appears lower down in a jointree.  It would pose a
+	 * problem for the PlaceHolderVar mechanism too, since we'd have no
+	 * way to identify where to evaluate a PHV coming out of the subquery.
+	 * Not worth working hard on this, just to collapse SubqueryScan/Result
+	 * into Result; especially since the SubqueryScan can often be optimized
+	 * away by setrefs.c anyway.
 	 */
 	if (subquery->jointree->fromlist == NIL)
 		return false;
@@ -1043,40 +1067,59 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes)
 }
 
 /*
- * has_nullable_targetlist
- *	  Check a subquery in the range table to see if all the non-junk
- *	  targetlist items are simple variables or strict functions of simple
- *	  variables (and, hence, will correctly go to NULL when examined above
- *	  the point of an outer join).
+ * insert_targetlist_placeholders
+ *	  Insert PlaceHolderVar nodes into any non-junk targetlist items that are
+ *	  not simple variables or strict functions of simple variables (and hence
+ *	  might not correctly go to NULL when examined above the point of an outer
+ *	  join).  We assume we can modify the tlist items in-place.
  *
- * NOTE: it would be correct (and useful) to ignore output columns that aren't
- * actually referenced by the enclosing query ... but we do not have that
- * information available at this point.
+ * varno is the upper-query relid of the subquery; this is used as the
+ * syntactic location of the PlaceHolderVars.
+ * If wrap_non_vars is true then *only* simple Var references escape being
+ * wrapped with PlaceHolderVars.
  */
-static bool
-has_nullable_targetlist(Query *subquery)
+static List *
+insert_targetlist_placeholders(PlannerInfo *root, List *tlist,
+							   int varno, bool wrap_non_vars)
 {
-	ListCell   *l;
+	ListCell   *lc;
 
-	foreach(l, subquery->targetList)
+	foreach(lc, tlist)
 	{
-		TargetEntry *tle = (TargetEntry *) lfirst(l);
+		TargetEntry *tle = (TargetEntry *) lfirst(lc);
 
 		/* ignore resjunk columns */
 		if (tle->resjunk)
 			continue;
 
-		/* Must contain a Var of current level */
-		if (!contain_vars_of_level((Node *) tle->expr, 0))
-			return false;
+		/*
+		 * Simple Vars always escape being wrapped.  This is common enough
+		 * to deserve a fast path even if we aren't doing wrap_non_vars.
+		 */
+		if (tle->expr && IsA(tle->expr, Var) &&
+			((Var *) tle->expr)->varlevelsup == 0)
+			continue;
 
-		/* Must not contain any non-strict constructs */
-		if (contain_nonstrict_functions((Node *) tle->expr))
-			return false;
+		if (!wrap_non_vars)
+		{
+			/*
+			 * If it contains a Var of current level, and does not contain
+			 * any non-strict constructs, then it's certainly nullable and we
+			 * don't need to insert a PlaceHolderVar.  (Note: in future maybe
+			 * we should insert PlaceHolderVars anyway, when a tlist item is
+			 * expensive to evaluate?
+			 */
+			if (contain_vars_of_level((Node *) tle->expr, 0) &&
+				!contain_nonstrict_functions((Node *) tle->expr))
+				continue;
+		}
 
-		/* This one's OK, keep scanning */
+		/* Else wrap it in a PlaceHolderVar */
+		tle->expr = (Expr *) make_placeholder_expr(root,
+												   tle->expr,
+												   bms_make_singleton(varno));
 	}
-	return true;
+	return tlist;
 }
 
 /*
@@ -1088,7 +1131,6 @@ static bool
 is_safe_append_member(Query *subquery)
 {
 	FromExpr   *jtnode;
-	ListCell   *l;
 
 	/*
 	 * It's only safe to pull up the child if its jointree contains exactly
@@ -1113,24 +1155,6 @@ is_safe_append_member(Query *subquery)
 	if (!IsA(jtnode, RangeTblRef))
 		return false;
 
-	/*
-	 * XXX For the moment we also have to insist that the subquery's tlist
-	 * includes only simple Vars.  This is pretty annoying, but fixing it
-	 * seems to require nontrivial changes --- mainly because joinrel tlists
-	 * are presently assumed to contain only Vars.	Perhaps a pseudo-variable
-	 * mechanism similar to the one speculated about in pull_up_subqueries'
-	 * comments would help?  FIXME someday.
-	 */
-	foreach(l, subquery->targetList)
-	{
-		TargetEntry *tle = (TargetEntry *) lfirst(l);
-
-		if (tle->resjunk)
-			continue;
-		if (!(tle->expr && IsA(tle->expr, Var)))
-			return false;
-	}
-
 	return true;
 }
 
@@ -1579,28 +1603,29 @@ reduce_outer_joins_pass2(Node *jtnode,
 }
 
 /*
- * fix_flattened_sublink_relids - adjust FlattenedSubLink nodes after
- * pulling up a subquery
+ * substitute_multiple_relids - adjust node relid sets after pulling up
+ * a subquery
  *
- * Find any FlattenedSubLink nodes in the given tree that reference the
- * pulled-up relid, and change them to reference the replacement relid(s).
- * We do not need to recurse into subqueries, since no subquery of the
- * current top query could contain such a reference.
+ * Find any FlattenedSubLink, PlaceHolderVar, or PlaceHolderInfo nodes in the
+ * given tree that reference the pulled-up relid, and change them to reference
+ * the replacement relid(s).  We do not need to recurse into subqueries, since
+ * no subquery of the current top query could (yet) contain such a reference.
  *
  * NOTE: although this has the form of a walker, we cheat and modify the
  * nodes in-place.  This should be OK since the tree was copied by ResolveNew
- * earlier.
+ * earlier.  Avoid scribbling on the original values of the bitmapsets, though,
+ * because expression_tree_mutator doesn't copy those.
  */
 
 typedef struct
 {
 	int			varno;
 	Relids		subrelids;
-} fix_flattened_sublink_relids_context;
+} substitute_multiple_relids_context;
 
 static bool
-fix_flattened_sublink_relids_walker(Node *node,
-							   fix_flattened_sublink_relids_context *context)
+substitute_multiple_relids_walker(Node *node,
+								  substitute_multiple_relids_context *context)
 {
 	if (node == NULL)
 		return false;
@@ -1610,28 +1635,61 @@ fix_flattened_sublink_relids_walker(Node *node,
 
 		if (bms_is_member(context->varno, fslink->lefthand))
 		{
+			fslink->lefthand = bms_union(fslink->lefthand,
+										 context->subrelids);
 			fslink->lefthand = bms_del_member(fslink->lefthand,
 											  context->varno);
-			fslink->lefthand = bms_add_members(fslink->lefthand,
-											   context->subrelids);
 		}
 		if (bms_is_member(context->varno, fslink->righthand))
 		{
+			fslink->righthand = bms_union(fslink->righthand,
+										  context->subrelids);
 			fslink->righthand = bms_del_member(fslink->righthand,
 											   context->varno);
-			fslink->righthand = bms_add_members(fslink->righthand,
-												context->subrelids);
 		}
 		/* fall through to examine children */
 	}
-	return expression_tree_walker(node, fix_flattened_sublink_relids_walker,
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		if (bms_is_member(context->varno, phv->phrels))
+		{
+			phv->phrels = bms_union(phv->phrels,
+									context->subrelids);
+			phv->phrels = bms_del_member(phv->phrels,
+										 context->varno);
+		}
+		/* fall through to examine children */
+	}
+	if (IsA(node, PlaceHolderInfo))
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+		if (bms_is_member(context->varno, phinfo->ph_eval_at))
+		{
+			phinfo->ph_eval_at = bms_union(phinfo->ph_eval_at,
+										   context->subrelids);
+			phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at,
+												context->varno);
+		}
+		if (bms_is_member(context->varno, phinfo->ph_needed))
+		{
+			phinfo->ph_needed = bms_union(phinfo->ph_needed,
+										  context->subrelids);
+			phinfo->ph_needed = bms_del_member(phinfo->ph_needed,
+											   context->varno);
+		}
+		/* fall through to examine children */
+	}
+	return expression_tree_walker(node, substitute_multiple_relids_walker,
 								  (void *) context);
 }
 
 static void
-fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids)
+substitute_multiple_relids(Node *node, int varno, Relids subrelids)
 {
-	fix_flattened_sublink_relids_context context;
+	substitute_multiple_relids_context context;
 
 	context.varno = varno;
 	context.subrelids = subrelids;
@@ -1640,7 +1698,7 @@ fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids)
 	 * Must be prepared to start with a Query or a bare expression tree.
 	 */
 	query_or_expression_tree_walker(node,
-									fix_flattened_sublink_relids_walker,
+									substitute_multiple_relids_walker,
 									(void *) &context,
 									0);
 }
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 08b70c8ee580902d686d97d708d5fae47feb3dde..45aa0e9ca5bf1fc5dc82c8fa85eec8deb22b9140 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.91 2008/08/28 23:09:46 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.92 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -168,13 +168,14 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
 		List	   *vars;
 		ListCell   *l;
 
-		vars = pull_var_clause((Node *) parse->returningList, false);
+		vars = pull_var_clause((Node *) parse->returningList, true);
 		foreach(l, vars)
 		{
 			Var		   *var = (Var *) lfirst(l);
 			TargetEntry *tle;
 
-			if (var->varno == result_relation)
+			if (IsA(var, Var) &&
+				var->varno == result_relation)
 				continue;		/* don't need it */
 
 			if (tlist_member((Node *) var, tlist))
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 2e22d09b4adfc1b9c6f695e69478895fea22308f..0efd150f6bf096730a50587294bd3766958fe12c 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.158 2008/10/07 19:27:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.159 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1584,7 +1584,39 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
 											 context->child_relid);
 		return (Node *) fslink;
 	}
-	/* Shouldn't need to handle SpecialJoinInfo or AppendRelInfo here */
+	if (IsA(node, PlaceHolderVar))
+	{
+		/* Copy the PlaceHolderVar node with correct mutation of subnodes */
+		PlaceHolderVar *phv;
+
+		phv = (PlaceHolderVar *) expression_tree_mutator(node,
+											  adjust_appendrel_attrs_mutator,
+														 (void *) context);
+		/* now fix PlaceHolderVar's relid sets */
+		if (phv->phlevelsup == 0)
+			phv->phrels = adjust_relid_set(phv->phrels,
+										   context->parent_relid,
+										   context->child_relid);
+		return (Node *) phv;
+	}
+	if (IsA(node, PlaceHolderInfo))
+	{
+		/* Copy the PlaceHolderInfo node with correct mutation of subnodes */
+		PlaceHolderInfo *phinfo;
+
+		phinfo = (PlaceHolderInfo *) expression_tree_mutator(node,
+											  adjust_appendrel_attrs_mutator,
+															 (void *) context);
+		/* now fix PlaceHolderInfo's relid sets */
+		phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at,
+											  context->parent_relid,
+											  context->child_relid);
+		phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed,
+											 context->parent_relid,
+											 context->child_relid);
+		return (Node *) phinfo;
+	}
+	/* Shouldn't need to handle other planner auxiliary nodes here */
 	Assert(!IsA(node, SpecialJoinInfo));
 	Assert(!IsA(node, AppendRelInfo));
 
diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile
index 0ea1dc32cc1f182648d0bb0abe40250bb02e2bac..d13b18c53f4413f20b1e2b4403f680086db68981 100644
--- a/src/backend/optimizer/util/Makefile
+++ b/src/backend/optimizer/util/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for optimizer/util
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.18 2008/02/19 10:30:07 petere Exp $
+#    $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.19 2008/10/21 20:42:53 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/util
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = clauses.o joininfo.o pathnode.o plancat.o predtest.o \
+OBJS = clauses.o joininfo.o pathnode.o placeholder.o plancat.o predtest.o \
        relnode.o restrictinfo.o tlist.o var.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 14b9313a9aadeb55dbce4f81aad9abbb9a90a0f4..c826ecb2ad20f8194f293ef2ccde9482d05f1a18 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.269 2008/10/09 19:27:40 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.270 2008/10/21 20:42:53 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1188,6 +1188,12 @@ find_nonnullable_rels_walker(Node *node, bool top_level)
 			result = find_nonnullable_rels_walker((Node *) expr->quals,
 												  top_level);
 	}
+	else if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		result = find_nonnullable_rels_walker((Node *) phv->phexpr, top_level);
+	}
 	return result;
 }
 
@@ -1393,6 +1399,12 @@ find_nonnullable_vars_walker(Node *node, bool top_level)
 			result = find_nonnullable_vars_walker((Node *) expr->quals,
 												  top_level);
 	}
+	else if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		result = find_nonnullable_vars_walker((Node *) phv->phexpr, top_level);
+	}
 	return result;
 }
 
@@ -1921,6 +1933,7 @@ eval_const_expressions(PlannerInfo *root, Node *node)
  *	  constant.  This effectively means that we plan using the first supplied
  *	  value of the Param.
  * 2. Fold stable, as well as immutable, functions to constants.
+ * 3. Reduce PlaceHolderVar nodes to their contained expressions.
  *--------------------
  */
 Node *
@@ -2823,6 +2836,20 @@ eval_const_expressions_mutator(Node *node,
 		newfslink->quals = quals;
 		return (Node *) newfslink;
 	}
+	if (IsA(node, PlaceHolderVar) && context->estimate)
+	{
+		/*
+		 * In estimation mode, just strip the PlaceHolderVar node altogether;
+		 * this amounts to estimating that the contained value won't be forced
+		 * to null by an outer join.  In regular mode we just use the default
+		 * behavior (ie, simplify the expression but leave the PlaceHolderVar
+		 * node intact).
+		 */
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		return eval_const_expressions_mutator((Node *) phv->phexpr,
+											  context);
+	}
 
 	/*
 	 * For any node type not handled above, we recurse using
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
new file mode 100644
index 0000000000000000000000000000000000000000..aef212b3604ae589664e5c7373e28e1d5603ced1
--- /dev/null
+++ b/src/backend/optimizer/util/placeholder.c
@@ -0,0 +1,226 @@
+/*-------------------------------------------------------------------------
+ *
+ * placeholder.c
+ *	  PlaceHolderVar and PlaceHolderInfo manipulation routines
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/placeholder.c,v 1.1 2008/10/21 20:42:53 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/nodeFuncs.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/placeholder.h"
+#include "optimizer/planmain.h"
+#include "optimizer/var.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * make_placeholder_expr
+ *		Make a PlaceHolderVar (and corresponding PlaceHolderInfo)
+ *		for the given expression.
+ *
+ * phrels is the syntactic location (as a set of baserels) to attribute
+ * to the expression.
+ */
+PlaceHolderVar *
+make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels)
+{
+	PlaceHolderVar *phv = makeNode(PlaceHolderVar);
+	PlaceHolderInfo *phinfo = makeNode(PlaceHolderInfo);
+
+	phv->phexpr = expr;
+	phv->phrels = phrels;
+	phv->phid = ++(root->glob->lastPHId);
+	phv->phlevelsup = 0;
+
+	phinfo->phid = phv->phid;
+	phinfo->ph_var = copyObject(phv);
+	phinfo->ph_eval_at = pull_varnos((Node *) phv);
+	/* ph_eval_at may change later, see fix_placeholder_eval_levels */
+	phinfo->ph_needed = NULL;		/* initially it's unused */
+	/* for the moment, estimate width using just the datatype info */
+	phinfo->ph_width = get_typavgwidth(exprType((Node *) expr),
+									   exprTypmod((Node *) expr));
+
+	root->placeholder_list = lappend(root->placeholder_list, phinfo);
+
+	return phv;
+}
+
+/*
+ * find_placeholder_info
+ *		Fetch the PlaceHolderInfo for the given PHV; error if not found
+ */
+PlaceHolderInfo *
+find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
+{
+	ListCell   *lc;
+
+	/* if this ever isn't true, we'd need to be able to look in parent lists */
+	Assert(phv->phlevelsup == 0);
+
+	foreach(lc, root->placeholder_list)
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+		if (phinfo->phid == phv->phid)
+			return phinfo;
+	}
+	elog(ERROR, "could not find PlaceHolderInfo with id %u", phv->phid);
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * fix_placeholder_eval_levels
+ *		Adjust the target evaluation levels for placeholders
+ *
+ * The initial eval_at level set by make_placeholder_expr was the set of
+ * rels used in the placeholder's expression (or the whole subselect if
+ * the expr is variable-free).  If the subselect contains any outer joins
+ * that can null any of those rels, we must delay evaluation to above those
+ * joins.
+ *
+ * In future we might want to put additional policy/heuristics here to
+ * try to determine an optimal evaluation level.  The current rules will
+ * result in evaluation at the lowest possible level.
+ */
+void
+fix_placeholder_eval_levels(PlannerInfo *root)
+{
+	ListCell   *lc1;
+
+	foreach(lc1, root->placeholder_list)
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1);
+		Relids		syn_level = phinfo->ph_var->phrels;
+		Relids		eval_at = phinfo->ph_eval_at;
+		BMS_Membership eval_membership;
+		bool		found_some;
+		ListCell   *lc2;
+
+		/*
+		 * Ignore unreferenced placeholders.  Note: if a placeholder is
+		 * referenced only by some other placeholder's expr, we will do
+		 * the right things because the referencing placeholder must appear
+		 * earlier in the list.
+		 */
+		if (bms_is_empty(phinfo->ph_needed))
+			continue;
+
+		/*
+		 * Check for delays due to lower outer joins.  This is the same logic
+		 * as in check_outerjoin_delay in initsplan.c, except that we don't
+		 * want to modify the delay_upper_joins flags; that was all handled
+		 * already during distribute_qual_to_rels.
+		 */
+		do
+		{
+			found_some = false;
+			foreach(lc2, root->join_info_list)
+			{
+				SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc2);
+
+				/* disregard joins not within the expr's sub-select */
+				if (!bms_is_subset(sjinfo->syn_lefthand, syn_level) ||
+					!bms_is_subset(sjinfo->syn_righthand, syn_level))
+					continue;
+
+				/* do we reference any nullable rels of this OJ? */
+				if (bms_overlap(eval_at, sjinfo->min_righthand) ||
+					(sjinfo->jointype == JOIN_FULL &&
+					 bms_overlap(eval_at, sjinfo->min_lefthand)))
+				{
+					/* yes; have we included all its rels in eval_at? */
+					if (!bms_is_subset(sjinfo->min_lefthand, eval_at) ||
+						!bms_is_subset(sjinfo->min_righthand, eval_at))
+					{
+						/* no, so add them in */
+						eval_at = bms_add_members(eval_at,
+												  sjinfo->min_lefthand);
+						eval_at = bms_add_members(eval_at,
+												  sjinfo->min_righthand);
+						/* we'll need another iteration */
+						found_some = true;
+					}
+				}
+			}
+		} while (found_some);
+
+		phinfo->ph_eval_at = eval_at;
+
+		/*
+		 * Now that we know where to evaluate the placeholder, make sure that
+		 * any vars or placeholders it uses will be available at that join
+		 * level.  (Note that this has to be done within this loop to make
+		 * sure we don't skip over such placeholders when we get to them.)
+		 */
+		eval_membership = bms_membership(eval_at);
+		if (eval_membership == BMS_MULTIPLE)
+		{
+			List	   *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
+											   true);
+
+			add_vars_to_targetlist(root, vars, eval_at);
+			list_free(vars);
+		}
+
+		/*
+		 * Also, if the placeholder can be computed at a base rel and is
+		 * needed above it, add it to that rel's targetlist.  (This is
+		 * essentially the same logic as in add_placeholders_to_joinrel, but
+		 * we can't do that part until joinrels are formed.)
+		 */
+		if (eval_membership == BMS_SINGLETON)
+		{
+			int			varno = bms_singleton_member(eval_at);
+			RelOptInfo *rel = find_base_rel(root, varno);
+
+			if (bms_nonempty_difference(phinfo->ph_needed, rel->relids))
+				rel->reltargetlist = lappend(rel->reltargetlist,
+											 copyObject(phinfo->ph_var));
+		}
+	}
+}
+
+/*
+ * add_placeholders_to_joinrel
+ *		Add any required PlaceHolderVars to a join rel's targetlist.
+ *
+ * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
+ * this join level and (b) the PHV can be computed at or below this level.
+ * At this time we do not need to distinguish whether the PHV will be
+ * computed here or copied up from below.
+ */
+void
+add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
+{
+	Relids		relids = joinrel->relids;
+	ListCell   *lc;
+
+	foreach(lc, root->placeholder_list)
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
+
+		/* Is it still needed above this joinrel? */
+		if (bms_nonempty_difference(phinfo->ph_needed, relids))
+		{
+			/* Is it computable here? */
+			if (bms_is_subset(phinfo->ph_eval_at, relids))
+			{
+				/* Yup, add it to the output */
+				joinrel->reltargetlist = lappend(joinrel->reltargetlist,
+												 phinfo->ph_var);
+				joinrel->width += phinfo->ph_width;
+			}
+		}
+	}
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 2fb6cd2efe646fa2da8f27879d59c8dfd88759f5..ed177fa929d02ff5d74e8e3a03e206e04cb33b58 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.91 2008/10/04 21:56:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.92 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
 #include "parser/parsetree.h"
@@ -354,6 +355,7 @@ build_join_rel(PlannerInfo *root,
 	 */
 	build_joinrel_tlist(root, joinrel, outer_rel);
 	build_joinrel_tlist(root, joinrel, inner_rel);
+	add_placeholders_to_joinrel(root, joinrel);
 
 	/*
 	 * Construct restrict and join clause lists for the new joinrel. (The
@@ -403,7 +405,8 @@ build_join_rel(PlannerInfo *root,
 
 /*
  * build_joinrel_tlist
- *	  Builds a join relation's target list.
+ *	  Builds a join relation's target list from an input relation.
+ *	  (This is invoked twice to handle the two input relations.)
  *
  * The join's targetlist includes all Vars of its member relations that
  * will still be needed above the join.  This subroutine adds all such
@@ -421,16 +424,23 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 
 	foreach(vars, input_rel->reltargetlist)
 	{
-		Var		   *origvar = (Var *) lfirst(vars);
+		Node	   *origvar = (Node *) lfirst(vars);
 		Var		   *var;
 		RelOptInfo *baserel;
 		int			ndx;
 
+		/*
+		 * Ignore PlaceHolderVars in the input tlists; we'll make our
+		 * own decisions about whether to copy them.
+		 */
+		if (IsA(origvar, PlaceHolderVar))
+			continue;
+
 		/*
 		 * We can't run into any child RowExprs here, but we could find a
 		 * whole-row Var with a ConvertRowtypeExpr atop it.
 		 */
-		var = origvar;
+		var = (Var *) origvar;
 		while (!IsA(var, Var))
 		{
 			if (IsA(var, ConvertRowtypeExpr))
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 2fd16afb5f965d62fcb3cb41743125355f933dfd..968f4ae367ac58caf059d73950d6fc9a0454ccc0 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.82 2008/08/25 22:42:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.83 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ tlist_member_ignore_relabel(Node *node, List *targetlist)
 List *
 flatten_tlist(List *tlist)
 {
-	List	   *vlist = pull_var_clause((Node *) tlist, false);
+	List	   *vlist = pull_var_clause((Node *) tlist, true);
 	List	   *new_tlist;
 
 	new_tlist = add_to_flat_tlist(NIL, vlist);
@@ -104,7 +104,7 @@ flatten_tlist(List *tlist)
  *		Add more vars to a flattened tlist (if they're not already in it)
  *
  * 'tlist' is the flattened tlist
- * 'vars' is a list of var nodes
+ * 'vars' is a list of Var and/or PlaceHolderVar nodes
  *
  * Returns the extended tlist.
  */
@@ -116,9 +116,9 @@ add_to_flat_tlist(List *tlist, List *vars)
 
 	foreach(v, vars)
 	{
-		Var		   *var = (Var *) lfirst(v);
+		Node	   *var = (Node *) lfirst(v);
 
-		if (!tlist_member((Node *) var, tlist))
+		if (!tlist_member(var, tlist))
 		{
 			TargetEntry *tle;
 
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 2e230913965064815c351e9d2c89a693134cd0bf..31749e46c0536e09b4cabca13a166eae6354a035 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -3,12 +3,18 @@
  * var.c
  *	  Var node manipulation routines
  *
+ * Note: for most purposes, PlaceHolderVar is considered a Var too,
+ * even if its contained expression is variable-free.  Also, CurrentOfExpr
+ * is treated as a Var for purposes of determining whether an expression
+ * contains variables.
+ *
+ *
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.80 2008/10/06 17:39:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.81 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,7 +56,7 @@ typedef struct
 typedef struct
 {
 	List	   *varlist;
-	bool		includeUpperVars;
+	bool		includePlaceHolderVars;
 } pull_var_clause_context;
 
 typedef struct
@@ -128,6 +134,26 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
 			context->varnos = bms_add_member(context->varnos, cexpr->cvarno);
 		return false;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		/*
+		 * Normally, we can just take the varnos in the contained expression.
+		 * But if it is variable-free, use the PHV's syntactic relids.
+		 */
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+		pull_varnos_context subcontext;
+
+		subcontext.varnos = NULL;
+		subcontext.sublevels_up = context->sublevels_up;
+		(void) pull_varnos_walker((Node *) phv->phexpr, &subcontext);
+
+		if (bms_is_empty(subcontext.varnos) &&
+			phv->phlevelsup == context->sublevels_up)
+			context->varnos = bms_add_members(context->varnos, phv->phrels);
+		else
+			context->varnos = bms_join(context->varnos, subcontext.varnos);
+		return false;
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into RTE subquery or not-yet-planned sublink subquery */
@@ -215,6 +241,12 @@ contain_var_clause_walker(Node *node, void *context)
 	}
 	if (IsA(node, CurrentOfExpr))
 		return true;
+	if (IsA(node, PlaceHolderVar))
+	{
+		if (((PlaceHolderVar *) node)->phlevelsup == 0)
+			return true;		/* abort the tree traversal and return true */
+		/* else fall through to check the contained expr */
+	}
 	return expression_tree_walker(node, contain_var_clause_walker, context);
 }
 
@@ -256,6 +288,12 @@ contain_vars_of_level_walker(Node *node, int *sublevels_up)
 			return true;
 		return false;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		if (((PlaceHolderVar *) node)->phlevelsup == *sublevels_up)
+			return true;		/* abort the tree traversal and return true */
+		/* else fall through to check the contained expr */
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -329,6 +367,7 @@ locate_var_of_level_walker(Node *node,
 		/* since CurrentOfExpr doesn't carry location, nothing we can do */
 		return false;
 	}
+	/* No extra code needed for PlaceHolderVar; just look in contained expr */
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -398,6 +437,7 @@ locate_var_of_relation_walker(Node *node,
 		/* since CurrentOfExpr doesn't carry location, nothing we can do */
 		return false;
 	}
+	/* No extra code needed for PlaceHolderVar; just look in contained expr */
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -527,6 +567,30 @@ find_minimum_var_level_walker(Node *node,
 			}
 		}
 	}
+	/* Likewise, make sure PlaceHolderVar is treated correctly */
+	if (IsA(node, PlaceHolderVar))
+	{
+		int			phlevelsup = ((PlaceHolderVar *) node)->phlevelsup;
+
+		/* convert levelsup to frame of reference of original query */
+		phlevelsup -= context->sublevels_up;
+		/* ignore local vars of subqueries */
+		if (phlevelsup >= 0)
+		{
+			if (context->min_varlevel < 0 ||
+				context->min_varlevel > phlevelsup)
+			{
+				context->min_varlevel = phlevelsup;
+
+				/*
+				 * As soon as we find a local variable, we can abort the tree
+				 * traversal, since min_varlevel is then certainly 0.
+				 */
+				if (phlevelsup == 0)
+					return true;
+			}
+		}
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -548,25 +612,30 @@ find_minimum_var_level_walker(Node *node,
 
 /*
  * pull_var_clause
- *	  Recursively pulls all var nodes from an expression clause.
+ *	  Recursively pulls all Var nodes from an expression clause.
+ *
+ *	  PlaceHolderVars are included too, if includePlaceHolderVars is true.
+ *	  If it isn't true, an error is thrown if any are found.
+ *	  Note that Vars within a PHV's expression are *not* included.
+ *
+ *	  CurrentOfExpr nodes are *not* included.
  *
- *	  Upper-level vars (with varlevelsup > 0) are included only
- *	  if includeUpperVars is true.	Most callers probably want
- *	  to ignore upper-level vars.
+ *	  Upper-level vars (with varlevelsup > 0) are not included.
+ *	  (These probably represent errors too, but we don't complain.)
  *
- *	  Returns list of varnodes found.  Note the varnodes themselves are not
+ *	  Returns list of nodes found.  Note the nodes themselves are not
  *	  copied, only referenced.
  *
  * Does not examine subqueries, therefore must only be used after reduction
  * of sublinks to subplans!
  */
 List *
-pull_var_clause(Node *node, bool includeUpperVars)
+pull_var_clause(Node *node, bool includePlaceHolderVars)
 {
 	pull_var_clause_context context;
 
 	context.varlist = NIL;
-	context.includeUpperVars = includeUpperVars;
+	context.includePlaceHolderVars = includePlaceHolderVars;
 
 	pull_var_clause_walker(node, &context);
 	return context.varlist;
@@ -579,10 +648,19 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
 		return false;
 	if (IsA(node, Var))
 	{
-		if (((Var *) node)->varlevelsup == 0 || context->includeUpperVars)
+		if (((Var *) node)->varlevelsup == 0)
 			context->varlist = lappend(context->varlist, node);
 		return false;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		if (!context->includePlaceHolderVars)
+			elog(ERROR, "PlaceHolderVar found where not expected");
+		if (((PlaceHolderVar *) node)->phlevelsup == 0)
+			context->varlist = lappend(context->varlist, node);
+		/* we do NOT descend into the contained expression */
+		return false;
+	}
 	return expression_tree_walker(node, pull_var_clause_walker,
 								  (void *) context);
 }
@@ -597,6 +675,9 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
  *	  is necessary since we will not scan the JOIN as a base relation, which
  *	  is the only way that the executor can directly handle whole-row Vars.
  *
+ * This also adjusts relid sets found in some expression node types to
+ * substitute the contained base rels for any join relid.
+ *
  * NOTE: this is used on not-yet-planned expressions.  We do not expect it
  * to be applied directly to a Query node.
  */
@@ -703,6 +784,40 @@ flatten_join_alias_vars_mutator(Node *node,
 		}
 		return (Node *) fslink;
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		/* Copy the PlaceHolderVar node with correct mutation of subnodes */
+		PlaceHolderVar *phv;
+
+		phv = (PlaceHolderVar *) expression_tree_mutator(node,
+											 flatten_join_alias_vars_mutator,
+														 (void *) context);
+		/* now fix PlaceHolderVar's relid sets */
+		if (phv->phlevelsup == context->sublevels_up)
+		{
+			phv->phrels = alias_relid_set(context->root,
+										  phv->phrels);
+		}
+		return (Node *) phv;
+	}
+	if (IsA(node, PlaceHolderInfo))
+	{
+		/* Copy the PlaceHolderInfo node with correct mutation of subnodes */
+		PlaceHolderInfo *phinfo;
+
+		phinfo = (PlaceHolderInfo *) expression_tree_mutator(node,
+											 flatten_join_alias_vars_mutator,
+															 (void *) context);
+		/* now fix PlaceHolderInfo's relid sets */
+		if (context->sublevels_up == 0)
+		{
+			phinfo->ph_eval_at = alias_relid_set(context->root,
+												 phinfo->ph_eval_at);
+			phinfo->ph_needed = alias_relid_set(context->root,
+												phinfo->ph_needed);
+		}
+		return (Node *) phinfo;
+	}
 
 	if (IsA(node, Query))
 	{
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 2a9645bf423c1ddb64eb9db19cab4c97c2457728..8ab486bfc2dc04bee9f7e56bb0b2adf572cd0fcc 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.115 2008/10/06 17:39:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.116 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -276,6 +276,17 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
 		}
 		/* fall through to examine children */
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		if (phv->phlevelsup == context->sublevels_up)
+		{
+			phv->phrels = offset_relid_set(phv->phrels,
+										   context->offset);
+		}
+		/* fall through to examine children */
+	}
 	if (IsA(node, AppendRelInfo))
 	{
 		AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -287,6 +298,19 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
 		}
 		/* fall through to examine children */
 	}
+	if (IsA(node, PlaceHolderInfo))
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+		if (context->sublevels_up == 0)
+		{
+			phinfo->ph_eval_at = offset_relid_set(phinfo->ph_eval_at,
+												  context->offset);
+			phinfo->ph_needed = offset_relid_set(phinfo->ph_needed,
+												 context->offset);
+		}
+		/* fall through to examine children */
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -440,6 +464,18 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 		}
 		/* fall through to examine children */
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		if (phv->phlevelsup == context->sublevels_up)
+		{
+			phv->phrels = adjust_relid_set(phv->phrels,
+										   context->rt_index,
+										   context->new_index);
+		}
+		/* fall through to examine children */
+	}
 	if (IsA(node, AppendRelInfo))
 	{
 		AppendRelInfo *appinfo = (AppendRelInfo *) node;
@@ -453,6 +489,21 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 		}
 		/* fall through to examine children */
 	}
+	if (IsA(node, PlaceHolderInfo))
+	{
+		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
+
+		if (context->sublevels_up == 0)
+		{
+			phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at,
+												  context->rt_index,
+												  context->new_index);
+			phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed,
+												 context->rt_index,
+												 context->new_index);
+		}
+		/* fall through to examine children */
+	}
 	if (IsA(node, Query))
 	{
 		/* Recurse into subselects */
@@ -585,6 +636,14 @@ IncrementVarSublevelsUp_walker(Node *node,
 			agg->agglevelsup += context->delta_sublevels_up;
 		/* fall through to recurse into argument */
 	}
+	if (IsA(node, PlaceHolderVar))
+	{
+		PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+		if (phv->phlevelsup >= context->min_sublevels_up)
+			phv->phlevelsup += context->delta_sublevels_up;
+		/* fall through to recurse into argument */
+	}
 	if (IsA(node, RangeTblEntry))
 	{
 		RangeTblEntry *rte = (RangeTblEntry *) node;
@@ -708,8 +767,10 @@ rangeTableEntry_used_walker(Node *node,
 	}
 	/* Shouldn't need to handle planner auxiliary nodes here */
 	Assert(!IsA(node, FlattenedSubLink));
+	Assert(!IsA(node, PlaceHolderVar));
 	Assert(!IsA(node, SpecialJoinInfo));
 	Assert(!IsA(node, AppendRelInfo));
+	Assert(!IsA(node, PlaceHolderInfo));
 
 	if (IsA(node, Query))
 	{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fc9853a43a938591d30841eb8fb664b06b66aa81..b659013bdec3fa61d4eb2a585431aa67ac0f8e67 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.255 2008/09/28 20:42:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.256 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2951,7 +2951,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows)
 		/*
 		 * Else pull out the component Vars
 		 */
-		varshere = pull_var_clause(groupexpr, false);
+		varshere = pull_var_clause(groupexpr, true);
 
 		/*
 		 * If we find any variable-free GROUP BY item, then either it is a
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 4aa5ce4345886a029c7c77be09868a99a88de102..ddd9934cf1ed70b20bb9879ceb264cf03675f202 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.213 2008/10/04 21:56:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.214 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -213,8 +213,10 @@ typedef enum NodeTag
 	T_RestrictInfo,
 	T_InnerIndexscanInfo,
 	T_FlattenedSubLink,
+	T_PlaceHolderVar,
 	T_SpecialJoinInfo,
 	T_AppendRelInfo,
+	T_PlaceHolderInfo,
 	T_PlannerParamItem,
 
 	/*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 9934daa64a83f9875e3f60662b7d95516a3ed7eb..b5eb00a7a67ec7d5ca599aae1de7b8d434d05ba7 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.161 2008/10/17 20:23:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.162 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,6 +76,8 @@ typedef struct PlannerGlobal
 
 	List	   *invalItems;		/* other dependencies, as PlanInvalItems */
 
+	Index		lastPHId;		/* highest PlaceHolderVar ID assigned */
+
 	bool		transientPlan;	/* redo plan when TransactionXmin changes? */
 } PlannerGlobal;
 
@@ -163,6 +165,8 @@ typedef struct PlannerInfo
 
 	List	   *append_rel_list;	/* list of AppendRelInfos */
 
+	List	   *placeholder_list;	/* list of PlaceHolderInfos */
+
 	List	   *query_pathkeys; /* desired pathkeys for query_planner(), and
 								 * actual pathkeys afterwards */
 
@@ -243,11 +247,12 @@ typedef struct PlannerInfo
  *			   clauses have been applied (ie, output rows of a plan for it)
  *		width - avg. number of bytes per tuple in the relation after the
  *				appropriate projections have been done (ie, output width)
- *		reltargetlist - List of Var nodes for the attributes we need to
- *						output from this relation (in no particular order,
- *						but all rels of an appendrel set must use same order)
+ *		reltargetlist - List of Var and PlaceHolderVar nodes for the values
+ *						we need to output from this relation.
+ *						List is in no particular order, but all rels of an
+ *						appendrel set must use corresponding orders.
  *						NOTE: in a child relation, may contain RowExpr or
- *						ConvertRowtypeExpr representing a whole-row Var
+ *						ConvertRowtypeExpr representing a whole-row Var.
  *		pathlist - List of Path nodes, one for each potentially useful
  *				   method of generating the relation
  *		cheapest_startup_path - the pathlist member with lowest startup cost
@@ -1089,6 +1094,29 @@ typedef struct FlattenedSubLink
 	Expr	   *quals;			/* join quals (in explicit-AND format) */
 } FlattenedSubLink;
 
+/*
+ * Placeholder node for an expression to be evaluated below the top level
+ * of a plan tree.  This is used during planning to represent the contained
+ * expression.  At the end of the planning process it is replaced by either
+ * the contained expression or a Var referring to a lower-level evaluation of
+ * the contained expression.  Typically the evaluation occurs below an outer
+ * join, and Var references above the outer join might thereby yield NULL
+ * instead of the expression value.
+ *
+ * Although the planner treats this as an expression node type, it is not
+ * recognized by the parser or executor, so we declare it here rather than
+ * in primnodes.h.
+ */
+
+typedef struct PlaceHolderVar
+{
+	Expr		xpr;
+	Expr	   *phexpr;			/* the represented expression */
+	Relids		phrels;			/* base relids syntactically within expr src */
+	Index		phid;			/* ID for PHV (unique within planner run) */
+	Index		phlevelsup;		/* > 0 if PHV belongs to outer query */
+} PlaceHolderVar;
+
 /*
  * "Special join" info.
  *
@@ -1250,6 +1278,31 @@ typedef struct AppendRelInfo
 	Oid			parent_reloid;	/* OID of parent relation */
 } AppendRelInfo;
 
+/*
+ * For each distinct placeholder expression generated during planning, we
+ * store a PlaceHolderInfo node in the PlannerInfo node's placeholder_list.
+ * This stores info that is needed centrally rather than in each copy of the
+ * PlaceHolderVar.  The phid fields identify which PlaceHolderInfo goes with
+ * each PlaceHolderVar.  Note that phid is unique throughout a planner run,
+ * not just within a query level --- this is so that we need not reassign ID's
+ * when pulling a subquery into its parent.
+ *
+ * The idea is to evaluate the expression at (only) the ph_eval_at join level,
+ * then allow it to bubble up like a Var until the ph_needed join level.
+ * ph_needed has the same definition as attr_needed for a regular Var.
+ */
+
+typedef struct PlaceHolderInfo
+{
+	NodeTag		type;
+
+	Index		phid;			/* ID for PH (unique within planner run) */
+	PlaceHolderVar *ph_var;		/* copy of PlaceHolderVar tree */
+	Relids		ph_eval_at;		/* lowest level we can evaluate value at */
+	Relids		ph_needed;		/* highest level the value is needed at */
+	int32		ph_width;		/* estimated attribute width */
+} PlaceHolderInfo;
+
 /*
  * glob->paramlist keeps track of the PARAM_EXEC slots that we have decided
  * we need for the query.  At runtime these slots are used to pass values
diff --git a/src/include/optimizer/placeholder.h b/src/include/optimizer/placeholder.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f39aedcfed15adbdf109156467d4a957a2c1942
--- /dev/null
+++ b/src/include/optimizer/placeholder.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * placeholder.h
+ *	  prototypes for optimizer/util/placeholder.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/optimizer/placeholder.h,v 1.1 2008/10/21 20:42:53 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLACEHOLDER_H
+#define PLACEHOLDER_H
+
+#include "nodes/relation.h"
+
+
+extern PlaceHolderVar *make_placeholder_expr(PlannerInfo *root, Expr *expr,
+											 Relids phrels);
+extern PlaceHolderInfo *find_placeholder_info(PlannerInfo *root,
+											  PlaceHolderVar *phv);
+extern void fix_placeholder_eval_levels(PlannerInfo *root);
+extern void add_placeholders_to_joinrel(PlannerInfo *root,
+										RelOptInfo *joinrel);
+
+#endif   /* PLACEHOLDER_H */
diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h
index 4a8f84e5532fdd89ddad52f3295f6296ac767719..c478259bcdb058bd2f8b4d3c1552f4408a28af2e 100644
--- a/src/include/optimizer/var.h
+++ b/src/include/optimizer/var.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.38 2008/09/01 20:42:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.39 2008/10/21 20:42:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,7 @@ extern bool contain_vars_of_level(Node *node, int levelsup);
 extern int	locate_var_of_level(Node *node, int levelsup);
 extern int	locate_var_of_relation(Node *node, int relid, int levelsup);
 extern int	find_minimum_var_level(Node *node);
-extern List *pull_var_clause(Node *node, bool includeUpperVars);
+extern List *pull_var_clause(Node *node, bool includePlaceHolderVars);
 extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node);
 
 #endif   /* VAR_H */