Index: src/backend/executor/nodeBitmapIndexscan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeBitmapIndexscan.c,v
retrieving revision 1.33
diff -c -r1.33 nodeBitmapIndexscan.c
*** src/backend/executor/nodeBitmapIndexscan.c	2 Jan 2010 16:57:41 -0000	1.33
--- src/backend/executor/nodeBitmapIndexscan.c	12 Jul 2010 04:26:44 -0000
***************
*** 108,144 ****
  /* ----------------------------------------------------------------
   *		ExecBitmapIndexReScan(node)
   *
!  *		Recalculates the value of the scan keys whose value depends on
!  *		information known at runtime and rescans the indexed relation.
   * ----------------------------------------------------------------
   */
  void
  ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
  {
! 	ExprContext *econtext;
! 
! 	econtext = node->biss_RuntimeContext;		/* context for runtime keys */
  
  	if (econtext)
- 	{
- 		/*
- 		 * If we are being passed an outer tuple, save it for runtime key
- 		 * calc.
- 		 */
- 		if (exprCtxt != NULL)
- 			econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- 
- 		/*
- 		 * Reset the runtime-key context so we don't leak memory as each outer
- 		 * tuple is scanned.  Note this assumes that we will recalculate *all*
- 		 * runtime keys on each call.
- 		 */
  		ResetExprContext(econtext);
- 	}
  
  	/*
! 	 * If we are doing runtime key calculations (ie, the index keys depend on
! 	 * data from an outer scan), compute the new key values.
  	 *
  	 * Array keys are also treated as runtime keys; note that if we return
  	 * with biss_RuntimeKeysReady still false, then there is an empty array
--- 108,133 ----
  /* ----------------------------------------------------------------
   *		ExecBitmapIndexReScan(node)
   *
!  *		Recalculates the values of any scan keys whose value depends on
!  *		information known at runtime, then rescans the indexed relation.
   * ----------------------------------------------------------------
   */
  void
  ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
  {
! 	ExprContext *econtext = node->biss_RuntimeContext;
  
+ 	/*
+ 	 * Reset the runtime-key context so we don't leak memory as each outer
+ 	 * tuple is scanned.  Note this assumes that we will recalculate *all*
+ 	 * runtime keys on each call.
+ 	 */
  	if (econtext)
  		ResetExprContext(econtext);
  
  	/*
! 	 * If we are doing runtime key calculations (ie, any of the index key
! 	 * values weren't simple Consts), compute the new key values.
  	 *
  	 * Array keys are also treated as runtime keys; note that if we return
  	 * with biss_RuntimeKeysReady still false, then there is an empty array
Index: src/backend/executor/nodeIndexscan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v
retrieving revision 1.139
diff -c -r1.139 nodeIndexscan.c
*** src/backend/executor/nodeIndexscan.c	26 Feb 2010 02:00:42 -0000	1.139
--- src/backend/executor/nodeIndexscan.c	12 Jul 2010 04:26:44 -0000
***************
*** 151,158 ****
  /* ----------------------------------------------------------------
   *		ExecIndexReScan(node)
   *
!  *		Recalculates the value of the scan keys whose value depends on
!  *		information known at runtime and rescans the indexed relation.
   *		Updating the scan key was formerly done separately in
   *		ExecUpdateIndexScanKeys. Integrating it into ReScan makes
   *		rescans of indices and relations/general streams more uniform.
--- 151,159 ----
  /* ----------------------------------------------------------------
   *		ExecIndexReScan(node)
   *
!  *		Recalculates the values of any scan keys whose value depends on
!  *		information known at runtime, then rescans the indexed relation.
!  *
   *		Updating the scan key was formerly done separately in
   *		ExecUpdateIndexScanKeys. Integrating it into ReScan makes
   *		rescans of indices and relations/general streams more uniform.
***************
*** 161,202 ****
  void
  ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
  {
- 	ExprContext *econtext;
- 
- 	econtext = node->iss_RuntimeContext;		/* context for runtime keys */
- 
- 	if (econtext)
- 	{
- 		/*
- 		 * If we are being passed an outer tuple, save it for runtime key
- 		 * calc.  We also need to link it into the "regular" per-tuple
- 		 * econtext, so it can be used during indexqualorig evaluations.
- 		 */
- 		if (exprCtxt != NULL)
- 		{
- 			ExprContext *stdecontext;
- 
- 			econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- 			stdecontext = node->ss.ps.ps_ExprContext;
- 			stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- 		}
- 
- 		/*
- 		 * Reset the runtime-key context so we don't leak memory as each outer
- 		 * tuple is scanned.  Note this assumes that we will recalculate *all*
- 		 * runtime keys on each call.
- 		 */
- 		ResetExprContext(econtext);
- 	}
- 
  	/*
! 	 * If we are doing runtime key calculations (ie, the index keys depend on
! 	 * data from an outer scan), compute the new key values
  	 */
  	if (node->iss_NumRuntimeKeys != 0)
  		ExecIndexEvalRuntimeKeys(econtext,
  								 node->iss_RuntimeKeys,
  								 node->iss_NumRuntimeKeys);
  	node->iss_RuntimeKeysReady = true;
  
  	/* reset index scan */
--- 162,183 ----
  void
  ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
  {
  	/*
! 	 * If we are doing runtime key calculations (ie, any of the index key
! 	 * values weren't simple Consts), compute the new key values.  But first,
! 	 * reset the context so we don't leak memory as each outer tuple is
! 	 * scanned.  Note this assumes that we will recalculate *all* runtime keys
! 	 * on each call.
  	 */
  	if (node->iss_NumRuntimeKeys != 0)
+ 	{
+ 		ExprContext *econtext = node->iss_RuntimeContext;
+ 
+ 		ResetExprContext(econtext);
  		ExecIndexEvalRuntimeKeys(econtext,
  								 node->iss_RuntimeKeys,
  								 node->iss_NumRuntimeKeys);
+ 	}
  	node->iss_RuntimeKeysReady = true;
  
  	/* reset index scan */
***************
*** 229,239 ****
  
  		/*
  		 * For each run-time key, extract the run-time expression and evaluate
! 		 * it with respect to the current outer tuple.	We then stick the
! 		 * result into the proper scan key.
  		 *
  		 * Note: the result of the eval could be a pass-by-ref value that's
! 		 * stored in the outer scan's tuple, not in
  		 * econtext->ecxt_per_tuple_memory.  We assume that the outer tuple
  		 * will stay put throughout our scan.  If this is wrong, we could copy
  		 * the result into our context explicitly, but I think that's not
--- 210,220 ----
  
  		/*
  		 * For each run-time key, extract the run-time expression and evaluate
! 		 * it with respect to the current context.  We then stick the result
! 		 * into the proper scan key.
  		 *
  		 * Note: the result of the eval could be a pass-by-ref value that's
! 		 * stored in some outer scan's tuple, not in
  		 * econtext->ecxt_per_tuple_memory.  We assume that the outer tuple
  		 * will stay put throughout our scan.  If this is wrong, we could copy
  		 * the result into our context explicitly, but I think that's not
Index: src/backend/executor/nodeNestloop.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v
retrieving revision 1.55
diff -c -r1.55 nodeNestloop.c
*** src/backend/executor/nodeNestloop.c	2 Jan 2010 16:57:44 -0000	1.55
--- src/backend/executor/nodeNestloop.c	12 Jul 2010 04:26:44 -0000
***************
*** 59,64 ****
--- 59,65 ----
  TupleTableSlot *
  ExecNestLoop(NestLoopState *node)
  {
+ 	NestLoop   *nl;
  	PlanState  *innerPlan;
  	PlanState  *outerPlan;
  	TupleTableSlot *outerTupleSlot;
***************
*** 66,77 ****
--- 67,80 ----
  	List	   *joinqual;
  	List	   *otherqual;
  	ExprContext *econtext;
+ 	ListCell   *lc;
  
  	/*
  	 * get information from the node
  	 */
  	ENL1_printf("getting info from node");
  
+ 	nl = (NestLoop *) node->js.ps.plan;
  	joinqual = node->js.joinqual;
  	otherqual = node->js.ps.qual;
  	outerPlan = outerPlanState(node);
***************
*** 134,149 ****
  			node->nl_MatchedOuter = false;
  
  			/*
! 			 * now rescan the inner plan
  			 */
! 			ENL1_printf("rescanning inner plan");
  
  			/*
! 			 * The scan key of the inner plan might depend on the current
! 			 * outer tuple (e.g. in index scans), that's why we pass our expr
! 			 * context.
  			 */
! 			ExecReScan(innerPlan, econtext);
  		}
  
  		/*
--- 137,169 ----
  			node->nl_MatchedOuter = false;
  
  			/*
! 			 * fetch the values of any outer Vars that must be passed to
! 			 * the inner scan, and store them in the appropriate PARAM_EXEC
! 			 * slots.
  			 */
! 			foreach(lc, nl->nestParams)
! 			{
! 				NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
! 				int			paramno = nlp->paramno;
! 				ParamExecData *prm;
! 
! 				prm = &(econtext->ecxt_param_exec_vals[paramno]);
! 				/* Param value should be an OUTER var */
! 				Assert(nlp->paramval->varno == OUTER);
! 				Assert(nlp->paramval->varattno > 0);
! 				prm->value = slot_getattr(outerTupleSlot,
! 										  nlp->paramval->varattno,
! 										  &(prm->isnull));
! 				/* Flag parameter value as changed */
! 				innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
! 													 paramno);
! 			}
  
  			/*
! 			 * now rescan the inner plan
  			 */
! 			ENL1_printf("rescanning inner plan");
! 			ExecReScan(innerPlan, NULL);
  		}
  
  		/*
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.464
diff -c -r1.464 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	26 Feb 2010 02:00:43 -0000	1.464
--- src/backend/nodes/copyfuncs.c	12 Jul 2010 04:26:44 -0000
***************
*** 564,569 ****
--- 564,574 ----
  	 */
  	CopyJoinFields((Join *) from, (Join *) newnode);
  
+ 	/*
+ 	 * copy remainder of node
+ 	 */
+ 	COPY_NODE_FIELD(nestParams);
+ 
  	return newnode;
  }
  
***************
*** 845,850 ****
--- 850,869 ----
  }
  
  /*
+  * _copyNestLoopParam
+  */
+ static NestLoopParam *
+ _copyNestLoopParam(NestLoopParam *from)
+ {
+ 	NestLoopParam *newnode = makeNode(NestLoopParam);
+ 
+ 	COPY_SCALAR_FIELD(paramno);
+ 	COPY_NODE_FIELD(paramval);
+ 
+ 	return newnode;
+ }
+ 
+ /*
   * _copyPlanRowMark
   */
  static PlanRowMark *
***************
*** 3671,3676 ****
--- 3690,3698 ----
  		case T_Limit:
  			retval = _copyLimit(from);
  			break;
+ 		case T_NestLoopParam:
+ 			retval = _copyNestLoopParam(from);
+ 			break;
  		case T_PlanRowMark:
  			retval = _copyPlanRowMark(from);
  			break;
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.385
diff -c -r1.385 outfuncs.c
*** src/backend/nodes/outfuncs.c	30 Mar 2010 21:58:10 -0000	1.385
--- src/backend/nodes/outfuncs.c	12 Jul 2010 04:26:44 -0000
***************
*** 517,522 ****
--- 517,524 ----
  	WRITE_NODE_TYPE("NESTLOOP");
  
  	_outJoinPlanInfo(str, (Join *) node);
+ 
+ 	WRITE_NODE_FIELD(nestParams);
  }
  
  static void
***************
*** 749,754 ****
--- 751,765 ----
  }
  
  static void
+ _outNestLoopParam(StringInfo str, NestLoopParam *node)
+ {
+ 	WRITE_NODE_TYPE("NESTLOOPPARAM");
+ 
+ 	WRITE_INT_FIELD(paramno);
+ 	WRITE_NODE_FIELD(paramval);
+ }
+ 
+ static void
  _outPlanRowMark(StringInfo str, PlanRowMark *node)
  {
  	WRITE_NODE_TYPE("PLANROWMARK");
***************
*** 1565,1570 ****
--- 1576,1583 ----
  	WRITE_BOOL_FIELD(hasPseudoConstantQuals);
  	WRITE_BOOL_FIELD(hasRecursion);
  	WRITE_INT_FIELD(wt_param_id);
+ 	WRITE_BITMAPSET_FIELD(curOuterRels);
+ 	WRITE_NODE_FIELD(curOuterParams);
  }
  
  static void
***************
*** 2562,2567 ****
--- 2575,2583 ----
  			case T_Limit:
  				_outLimit(str, obj);
  				break;
+ 			case T_NestLoopParam:
+ 				_outNestLoopParam(str, obj);
+ 				break;
  			case T_PlanRowMark:
  				_outPlanRowMark(str, obj);
  				break;
Index: src/backend/optimizer/plan/createplan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v
retrieving revision 1.275
diff -c -r1.275 createplan.c
*** src/backend/optimizer/plan/createplan.c	25 May 2010 17:44:41 -0000	1.275
--- src/backend/optimizer/plan/createplan.c	12 Jul 2010 04:26:44 -0000
***************
*** 28,33 ****
--- 28,34 ----
  #include "optimizer/planmain.h"
  #include "optimizer/predtest.h"
  #include "optimizer/restrictinfo.h"
+ #include "optimizer/subselect.h"
  #include "optimizer/tlist.h"
  #include "optimizer/var.h"
  #include "parser/parse_clause.h"
***************
*** 35,40 ****
--- 36,42 ----
  #include "utils/lsyscache.h"
  
  
+ static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
  static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
  static List *build_relation_tlist(RelOptInfo *rel);
  static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
***************
*** 72,78 ****
  					  Plan *outer_plan, Plan *inner_plan);
  static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
  					 Plan *outer_plan, Plan *inner_plan);
! static List *fix_indexqual_references(List *indexquals, IndexPath *index_path);
  static List *get_switched_clauses(List *clauses, Relids outerrelids);
  static List *order_qual_clauses(PlannerInfo *root, List *clauses);
  static void copy_path_costsize(Plan *dest, Path *src);
--- 74,83 ----
  					  Plan *outer_plan, Plan *inner_plan);
  static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
  					 Plan *outer_plan, Plan *inner_plan);
! static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
! static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
! static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
! 						 List *indexquals);
  static List *get_switched_clauses(List *clauses, Relids outerrelids);
  static List *order_qual_clauses(PlannerInfo *root, List *clauses);
  static void copy_path_costsize(Plan *dest, Path *src);
***************
*** 103,109 ****
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
! 			  List *joinclauses, List *otherclauses,
  			  Plan *lefttree, Plan *righttree,
  			  JoinType jointype);
  static HashJoin *make_hashjoin(List *tlist,
--- 108,114 ----
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
! 			  List *joinclauses, List *otherclauses, List *nestParams,
  			  Plan *lefttree, Plan *righttree,
  			  JoinType jointype);
  static HashJoin *make_hashjoin(List *tlist,
***************
*** 133,140 ****
  
  /*
   * create_plan
!  *	  Creates the access plan for a query by tracing backwards through the
!  *	  desired chain of pathnodes, starting at the node 'best_path'.  For
   *	  every pathnode found, we create a corresponding plan node containing
   *	  appropriate id, target list, and qualification information.
   *
--- 138,145 ----
  
  /*
   * create_plan
!  *	  Creates the access plan for a query by recursively processing the
!  *	  desired tree of pathnodes, starting at the node 'best_path'.  For
   *	  every pathnode found, we create a corresponding plan node containing
   *	  appropriate id, target list, and qualification information.
   *
***************
*** 151,156 ****
--- 156,184 ----
  {
  	Plan	   *plan;
  
+ 	/* Initialize this module's private workspace in PlannerInfo */
+ 	root->curOuterRels = NULL;
+ 	root->curOuterParams = NIL;
+ 
+ 	/* Recursively process the path tree */
+ 	plan = create_plan_recurse(root, best_path);
+ 
+ 	/* Check we successfully assigned all NestLoopParams to plan nodes */
+ 	if (root->curOuterParams != NIL)
+ 		elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
+ 
+ 	return plan;
+ }
+ 
+ /*
+  * create_plan_recurse
+  *	  Recursive guts of create_plan().
+  */
+ static Plan *
+ create_plan_recurse(PlannerInfo *root, Path *best_path)
+ {
+ 	Plan	   *plan;
+ 
  	switch (best_path->pathtype)
  	{
  		case T_SeqScan:
***************
*** 477,485 ****
  	Plan	   *outer_plan;
  	Plan	   *inner_plan;
  	Plan	   *plan;
  
! 	outer_plan = create_plan(root, best_path->outerjoinpath);
! 	inner_plan = create_plan(root, best_path->innerjoinpath);
  
  	switch (best_path->path.pathtype)
  	{
--- 505,520 ----
  	Plan	   *outer_plan;
  	Plan	   *inner_plan;
  	Plan	   *plan;
+ 	Relids		saveOuterRels = root->curOuterRels;
  
! 	outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
! 
! 	/* For a nestloop, include outer relids in curOuterRels for inner side */
! 	if (best_path->path.pathtype == T_NestLoop)
! 		root->curOuterRels = bms_union(root->curOuterRels,
! 								   best_path->outerjoinpath->parent->relids);
! 
! 	inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
  
  	switch (best_path->path.pathtype)
  	{
***************
*** 496,501 ****
--- 531,540 ----
  												 inner_plan);
  			break;
  		case T_NestLoop:
+ 			/* Restore curOuterRels */
+ 			bms_free(root->curOuterRels);
+ 			root->curOuterRels = saveOuterRels;
+ 
  			plan = (Plan *) create_nestloop_plan(root,
  												 (NestPath *) best_path,
  												 outer_plan,
***************
*** 571,577 ****
  	{
  		Path	   *subpath = (Path *) lfirst(subpaths);
  
! 		subplans = lappend(subplans, create_plan(root, subpath));
  	}
  
  	plan = make_append(subplans, tlist);
--- 610,616 ----
  	{
  		Path	   *subpath = (Path *) lfirst(subpaths);
  
! 		subplans = lappend(subplans, create_plan_recurse(root, subpath));
  	}
  
  	plan = make_append(subplans, tlist);
***************
*** 616,622 ****
  	Material   *plan;
  	Plan	   *subplan;
  
! 	subplan = create_plan(root, best_path->subpath);
  
  	/* We don't want any excess columns in the materialized tuples */
  	disuse_physical_tlist(subplan, best_path->subpath);
--- 655,661 ----
  	Material   *plan;
  	Plan	   *subplan;
  
! 	subplan = create_plan_recurse(root, best_path->subpath);
  
  	/* We don't want any excess columns in the materialized tuples */
  	disuse_physical_tlist(subplan, best_path->subpath);
***************
*** 650,656 ****
  	int			groupColPos;
  	ListCell   *l;
  
! 	subplan = create_plan(root, best_path->subpath);
  
  	/* Done if we don't need to do any actual unique-ifying */
  	if (best_path->umethod == UNIQUE_PATH_NOOP)
--- 689,695 ----
  	int			groupColPos;
  	ListCell   *l;
  
! 	subplan = create_plan_recurse(root, best_path->subpath);
  
  	/* Done if we don't need to do any actual unique-ifying */
  	if (best_path->umethod == UNIQUE_PATH_NOOP)
***************
*** 904,910 ****
  	 * The executor needs a copy with the indexkey on the left of each clause
  	 * and with index attr numbers substituted for table ones.
  	 */
! 	fixed_indexquals = fix_indexqual_references(indexquals, best_path);
  
  	/*
  	 * If this is an innerjoin scan, the indexclauses will contain join
--- 943,949 ----
  	 * The executor needs a copy with the indexkey on the left of each clause
  	 * and with index attr numbers substituted for table ones.
  	 */
! 	fixed_indexquals = fix_indexqual_references(root, best_path, indexquals);
  
  	/*
  	 * If this is an innerjoin scan, the indexclauses will contain join
***************
*** 975,980 ****
--- 1014,1035 ----
  	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
  	qpqual = extract_actual_clauses(qpqual, false);
  
+ 	/*
+ 	 * We have to replace any outer-relation variables with nestloop params
+ 	 * in the indexqualorig and qpqual expressions.  A bit annoying to have to
+ 	 * do this separately from the processing in fix_indexqual_references ---
+ 	 * rethink this when generalizing the inner indexscan support.  But note
+ 	 * we can't really do this earlier because it'd break the comparisons to
+ 	 * predicates above ... (or would it?  Those wouldn't have outer refs)
+ 	 */
+ 	if (best_path->isjoininner)
+ 	{
+ 		stripped_indexquals = (List *)
+ 			replace_nestloop_params(root, (Node *) stripped_indexquals);
+ 		qpqual = (List *)
+ 			replace_nestloop_params(root, (Node *) qpqual);
+ 	}
+ 
  	/* Finally ready to build the plan node */
  	scan_plan = make_indexscan(tlist,
  							   qpqual,
***************
*** 1267,1272 ****
--- 1322,1338 ----
  				*indexqual = lappend(*indexqual, pred);
  			}
  		}
+ 		/*
+ 		 * Replace outer-relation variables with nestloop params, but only
+ 		 * after doing the above comparisons to index predicates.
+ 		 */
+ 		if (ipath->isjoininner)
+ 		{
+ 			*qual = (List *)
+ 				replace_nestloop_params(root, (Node *) *qual);
+ 			*indexqual = (List *)
+ 				replace_nestloop_params(root, (Node *) *indexqual);
+ 		}
  	}
  	else
  	{
***************
*** 1572,1582 ****
  					 Plan *outer_plan,
  					 Plan *inner_plan)
  {
  	List	   *tlist = build_relation_tlist(best_path->path.parent);
  	List	   *joinrestrictclauses = best_path->joinrestrictinfo;
  	List	   *joinclauses;
  	List	   *otherclauses;
! 	NestLoop   *join_plan;
  
  	/*
  	 * If the inner path is a nestloop inner indexscan, it might be using some
--- 1638,1653 ----
  					 Plan *outer_plan,
  					 Plan *inner_plan)
  {
+ 	NestLoop   *join_plan;
  	List	   *tlist = build_relation_tlist(best_path->path.parent);
  	List	   *joinrestrictclauses = best_path->joinrestrictinfo;
  	List	   *joinclauses;
  	List	   *otherclauses;
! 	Relids		outerrelids;
! 	List	   *nestParams;
! 	ListCell   *cell;
! 	ListCell   *prev;
! 	ListCell   *next;
  
  	/*
  	 * If the inner path is a nestloop inner indexscan, it might be using some
***************
*** 1605,1613 ****
--- 1676,1707 ----
  		otherclauses = NIL;
  	}
  
+ 	/*
+ 	 * Identify any nestloop parameters that should be supplied by this join
+ 	 * node, and move them from root->curOuterParams to the nestParams list.
+ 	 */
+ 	outerrelids = best_path->outerjoinpath->parent->relids;
+ 	nestParams = NIL;
+ 	prev = NULL;
+ 	for (cell = list_head(root->curOuterParams); cell; cell = next)
+ 	{
+ 		NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
+ 
+ 		next = lnext(cell);
+ 		if (bms_is_member(nlp->paramval->varno, outerrelids))
+ 		{
+ 			root->curOuterParams = list_delete_cell(root->curOuterParams,
+ 													cell, prev);
+ 			nestParams = lappend(nestParams, nlp);
+ 		}
+ 		else
+ 			prev = cell;
+ 	}
+ 
  	join_plan = make_nestloop(tlist,
  							  joinclauses,
  							  otherclauses,
+ 							  nestParams,
  							  outer_plan,
  							  inner_plan,
  							  best_path->jointype);
***************
*** 2016,2027 ****
   *****************************************************************************/
  
  /*
   * fix_indexqual_references
   *	  Adjust indexqual clauses to the form the executor's indexqual
   *	  machinery needs.
   *
!  * We have three tasks here:
   *	* Remove RestrictInfo nodes from the input clauses.
   *	* Index keys must be represented by Var nodes with varattno set to the
   *	  index's attribute number, not the attribute number in the original rel.
   *	* If the index key is on the right, commute the clause to put it on the
--- 2110,2181 ----
   *****************************************************************************/
  
  /*
+  * replace_nestloop_params
+  *	  Replace outer-relation Vars in the given expression with nestloop Params
+  *
+  * All Vars belonging to the relation(s) identified by root->curOuterRels
+  * are replaced by Params, and entries are added to root->curOuterParams if
+  * not already present.
+  */
+ static Node *
+ replace_nestloop_params(PlannerInfo *root, Node *expr)
+ {
+ 	/* No setup needed for tree walk, so away we go */
+ 	return replace_nestloop_params_mutator(expr, root);
+ }
+ 
+ static Node *
+ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
+ {
+ 	if (node == NULL)
+ 		return NULL;
+ 	if (IsA(node, Var))
+ 	{
+ 		Var	   *var = (Var *) node;
+ 		Param  *param;
+ 		NestLoopParam *nlp;
+ 		ListCell *lc;
+ 
+ 		/* Upper-level Vars should be long gone at this point */
+ 		Assert(var->varlevelsup == 0);
+ 		/* If not to be replaced, we can just return the Var unmodified */
+ 		if (!bms_is_member(var->varno, root->curOuterRels))
+ 			return node;
+ 		/* Create a Param representing the Var */
+ 		param = assign_nestloop_param(root, var);
+ 		/* Is this param already listed in root->curOuterParams? */
+ 		foreach(lc, root->curOuterParams)
+ 		{
+ 			nlp = (NestLoopParam *) lfirst(lc);
+ 			if (nlp->paramno == param->paramid)
+ 			{
+ 				Assert(equal(var, nlp->paramval));
+ 				/* Present, so we can just return the Param */
+ 				return (Node *) param;
+ 			}
+ 		}
+ 		/* No, so add it */
+ 		nlp = makeNode(NestLoopParam);
+ 		nlp->paramno = param->paramid;
+ 		nlp->paramval = var;
+ 		root->curOuterParams = lappend(root->curOuterParams, nlp);
+ 		/* And return the replacement Param */
+ 		return (Node *) param;
+ 	}
+ 	return expression_tree_mutator(node,
+ 								   replace_nestloop_params_mutator,
+ 								   (void *) root);
+ }
+ 
+ /*
   * fix_indexqual_references
   *	  Adjust indexqual clauses to the form the executor's indexqual
   *	  machinery needs.
   *
!  * We have four tasks here:
   *	* Remove RestrictInfo nodes from the input clauses.
+  *	* Replace any outer-relation Var nodes with nestloop Params.
+  *	  (XXX eventually, that responsibility should go elsewhere?)
   *	* Index keys must be represented by Var nodes with varattno set to the
   *	  index's attribute number, not the attribute number in the original rel.
   *	* If the index key is on the right, commute the clause to put it on the
***************
*** 2033,2039 ****
   * two separate copies of the subplan tree, or things will go awry).
   */
  static List *
! fix_indexqual_references(List *indexquals, IndexPath *index_path)
  {
  	IndexOptInfo *index = index_path->indexinfo;
  	List	   *fixed_indexquals;
--- 2187,2194 ----
   * two separate copies of the subplan tree, or things will go awry).
   */
  static List *
! fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
! 						 List *indexquals)
  {
  	IndexOptInfo *index = index_path->indexinfo;
  	List	   *fixed_indexquals;
***************
*** 2041,2066 ****
  
  	fixed_indexquals = NIL;
  
- 	/*
- 	 * For each qual clause, commute if needed to put the indexkey operand on
- 	 * the left, and then fix its varattno.  (We do not need to change the
- 	 * other side of the clause.)
- 	 */
  	foreach(l, indexquals)
  	{
  		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
! 		Expr	   *clause;
  
  		Assert(IsA(rinfo, RestrictInfo));
  
  		/*
! 		 * Make a copy that will become the fixed clause.
  		 *
! 		 * We used to try to do a shallow copy here, but that fails if there
! 		 * is a subplan in the arguments of the opclause.  So just do a full
! 		 * copy.
  		 */
! 		clause = (Expr *) copyObject((Node *) rinfo->clause);
  
  		if (IsA(clause, OpExpr))
  		{
--- 2196,2215 ----
  
  	fixed_indexquals = NIL;
  
  	foreach(l, indexquals)
  	{
  		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
! 		Node	   *clause;
  
  		Assert(IsA(rinfo, RestrictInfo));
  
  		/*
! 		 * Replace any outer-relation variables with nestloop params.
  		 *
! 		 * This also makes a copy of the clause, so it's safe to modify it
! 		 * in-place below.
  		 */
! 		clause = replace_nestloop_params(root, (Node *) rinfo->clause);
  
  		if (IsA(clause, OpExpr))
  		{
***************
*** 2765,2770 ****
--- 2914,2920 ----
  make_nestloop(List *tlist,
  			  List *joinclauses,
  			  List *otherclauses,
+ 			  List *nestParams,
  			  Plan *lefttree,
  			  Plan *righttree,
  			  JoinType jointype)
***************
*** 2779,2784 ****
--- 2929,2935 ----
  	plan->righttree = righttree;
  	node->join.jointype = jointype;
  	node->join.joinqual = joinclauses;
+ 	node->nestParams = nestParams;
  
  	return node;
  }
Index: src/backend/optimizer/plan/setrefs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v
retrieving revision 1.160
diff -c -r1.160 setrefs.c
*** src/backend/optimizer/plan/setrefs.c	26 Feb 2010 02:00:45 -0000	1.160
--- src/backend/optimizer/plan/setrefs.c	12 Jul 2010 04:26:44 -0000
***************
*** 89,96 ****
  static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
  static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
  static void set_join_references(PlannerGlobal *glob, Join *join, int rtoffset);
- static void set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan,
- 						  indexed_tlist *outer_itlist);
  static void set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset);
  static void set_dummy_tlist_references(Plan *plan, int rtoffset);
  static indexed_tlist *build_tlist_index(List *tlist);
--- 89,94 ----
***************
*** 878,889 ****
  		Assert(var->varlevelsup == 0);
  
  		/*
! 		 * We should not see any Vars marked INNER, but in a nestloop inner
! 		 * scan there could be OUTER Vars.	Leave them alone.
  		 */
  		Assert(var->varno != INNER);
! 		if (var->varno > 0 && var->varno != OUTER)
! 			var->varno += context->rtoffset;
  		if (var->varnoold > 0)
  			var->varnoold += context->rtoffset;
  		return (Node *) var;
--- 876,886 ----
  		Assert(var->varlevelsup == 0);
  
  		/*
! 		 * We should not see any Vars marked INNER or OUTER.
  		 */
  		Assert(var->varno != INNER);
! 		Assert(var->varno != OUTER);
! 		var->varno += context->rtoffset;
  		if (var->varnoold > 0)
  			var->varnoold += context->rtoffset;
  		return (Node *) var;
***************
*** 927,936 ****
   *	  values to the result domain number of either the corresponding outer
   *	  or inner join tuple item.  Also perform opcode lookup for these
   *	  expressions. and add regclass OIDs to glob->relationOids.
-  *
-  * In the case of a nestloop with inner indexscan, we will also need to
-  * apply the same transformation to any outer vars appearing in the
-  * quals of the child indexscan.  set_inner_join_references does that.
   */
  static void
  set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
--- 924,929 ----
***************
*** 966,973 ****
  	/* Now do join-type-specific stuff */
  	if (IsA(join, NestLoop))
  	{
! 		/* This processing is split out to handle possible recursion */
! 		set_inner_join_references(glob, inner_plan, outer_itlist);
  	}
  	else if (IsA(join, MergeJoin))
  	{
--- 959,976 ----
  	/* Now do join-type-specific stuff */
  	if (IsA(join, NestLoop))
  	{
! 		NestLoop   *nl = (NestLoop *) join;
! 		ListCell   *lc;
! 
! 		foreach(lc, nl->nestParams)
! 		{
! 			NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
! 
! 			nlp->paramval = (Var *) fix_upper_expr(glob,
! 												   (Node *) nlp->paramval,
! 												   outer_itlist,
! 												   rtoffset);
! 		}
  	}
  	else if (IsA(join, MergeJoin))
  	{
***************
*** 997,1189 ****
  }
  
  /*
-  * set_inner_join_references
-  *		Handle join references appearing in an inner indexscan's quals
-  *
-  * To handle bitmap-scan plan trees, we have to be able to recurse down
-  * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans
-  * require recursing through Append nodes.	This is split out as a separate
-  * function so that it can recurse.
-  *
-  * Note we do *not* apply any rtoffset for non-join Vars; this is because
-  * the quals will be processed again by fix_scan_expr when the set_plan_refs
-  * recursion reaches the inner indexscan, and so we'd have done it twice.
-  */
- static void
- set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan,
- 						  indexed_tlist *outer_itlist)
- {
- 	if (IsA(inner_plan, IndexScan))
- 	{
- 		/*
- 		 * An index is being used to reduce the number of tuples scanned in
- 		 * the inner relation.	If there are join clauses being used with the
- 		 * index, we must update their outer-rel var nodes to refer to the
- 		 * outer side of the join.
- 		 */
- 		IndexScan  *innerscan = (IndexScan *) inner_plan;
- 		List	   *indexqualorig = innerscan->indexqualorig;
- 
- 		/* No work needed if indexqual refers only to its own rel... */
- 		if (NumRelids((Node *) indexqualorig) > 1)
- 		{
- 			Index		innerrel = innerscan->scan.scanrelid;
- 
- 			/* only refs to outer vars get changed in the inner qual */
- 			innerscan->indexqualorig = fix_join_expr(glob,
- 													 indexqualorig,
- 													 outer_itlist,
- 													 NULL,
- 													 innerrel,
- 													 0);
- 			innerscan->indexqual = fix_join_expr(glob,
- 												 innerscan->indexqual,
- 												 outer_itlist,
- 												 NULL,
- 												 innerrel,
- 												 0);
- 
- 			/*
- 			 * We must fix the inner qpqual too, if it has join clauses (this
- 			 * could happen if special operators are involved: some indexquals
- 			 * may get rechecked as qpquals).
- 			 */
- 			if (NumRelids((Node *) inner_plan->qual) > 1)
- 				inner_plan->qual = fix_join_expr(glob,
- 												 inner_plan->qual,
- 												 outer_itlist,
- 												 NULL,
- 												 innerrel,
- 												 0);
- 		}
- 	}
- 	else if (IsA(inner_plan, BitmapIndexScan))
- 	{
- 		/*
- 		 * Same, but index is being used within a bitmap plan.
- 		 */
- 		BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
- 		List	   *indexqualorig = innerscan->indexqualorig;
- 
- 		/* No work needed if indexqual refers only to its own rel... */
- 		if (NumRelids((Node *) indexqualorig) > 1)
- 		{
- 			Index		innerrel = innerscan->scan.scanrelid;
- 
- 			/* only refs to outer vars get changed in the inner qual */
- 			innerscan->indexqualorig = fix_join_expr(glob,
- 													 indexqualorig,
- 													 outer_itlist,
- 													 NULL,
- 													 innerrel,
- 													 0);
- 			innerscan->indexqual = fix_join_expr(glob,
- 												 innerscan->indexqual,
- 												 outer_itlist,
- 												 NULL,
- 												 innerrel,
- 												 0);
- 			/* no need to fix inner qpqual */
- 			Assert(inner_plan->qual == NIL);
- 		}
- 	}
- 	else if (IsA(inner_plan, BitmapHeapScan))
- 	{
- 		/*
- 		 * The inner side is a bitmap scan plan.  Fix the top node, and
- 		 * recurse to get the lower nodes.
- 		 *
- 		 * Note: create_bitmap_scan_plan removes clauses from bitmapqualorig
- 		 * if they are duplicated in qpqual, so must test these independently.
- 		 */
- 		BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan;
- 		Index		innerrel = innerscan->scan.scanrelid;
- 		List	   *bitmapqualorig = innerscan->bitmapqualorig;
- 
- 		/* only refs to outer vars get changed in the inner qual */
- 		if (NumRelids((Node *) bitmapqualorig) > 1)
- 			innerscan->bitmapqualorig = fix_join_expr(glob,
- 													  bitmapqualorig,
- 													  outer_itlist,
- 													  NULL,
- 													  innerrel,
- 													  0);
- 
- 		/*
- 		 * We must fix the inner qpqual too, if it has join clauses (this
- 		 * could happen if special operators are involved: some indexquals may
- 		 * get rechecked as qpquals).
- 		 */
- 		if (NumRelids((Node *) inner_plan->qual) > 1)
- 			inner_plan->qual = fix_join_expr(glob,
- 											 inner_plan->qual,
- 											 outer_itlist,
- 											 NULL,
- 											 innerrel,
- 											 0);
- 
- 		/* Now recurse */
- 		set_inner_join_references(glob, inner_plan->lefttree, outer_itlist);
- 	}
- 	else if (IsA(inner_plan, BitmapAnd))
- 	{
- 		/* All we need do here is recurse */
- 		BitmapAnd  *innerscan = (BitmapAnd *) inner_plan;
- 		ListCell   *l;
- 
- 		foreach(l, innerscan->bitmapplans)
- 		{
- 			set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- 		}
- 	}
- 	else if (IsA(inner_plan, BitmapOr))
- 	{
- 		/* All we need do here is recurse */
- 		BitmapOr   *innerscan = (BitmapOr *) inner_plan;
- 		ListCell   *l;
- 
- 		foreach(l, innerscan->bitmapplans)
- 		{
- 			set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- 		}
- 	}
- 	else if (IsA(inner_plan, TidScan))
- 	{
- 		TidScan    *innerscan = (TidScan *) inner_plan;
- 		Index		innerrel = innerscan->scan.scanrelid;
- 
- 		innerscan->tidquals = fix_join_expr(glob,
- 											innerscan->tidquals,
- 											outer_itlist,
- 											NULL,
- 											innerrel,
- 											0);
- 	}
- 	else if (IsA(inner_plan, Append))
- 	{
- 		/*
- 		 * The inner side is an append plan.  Recurse to see if it contains
- 		 * indexscans that need to be fixed.
- 		 */
- 		Append	   *appendplan = (Append *) inner_plan;
- 		ListCell   *l;
- 
- 		foreach(l, appendplan->appendplans)
- 		{
- 			set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- 		}
- 	}
- 	else if (IsA(inner_plan, Result))
- 	{
- 		/* Recurse through a gating Result node (similar to Append case) */
- 		Result	   *result = (Result *) inner_plan;
- 
- 		if (result->plan.lefttree)
- 			set_inner_join_references(glob, result->plan.lefttree, outer_itlist);
- 	}
- }
- 
- /*
   * set_upper_references
   *	  Update the targetlist and quals of an upper-level plan node
   *	  to refer to the tuples returned by its lefttree subplan.
--- 1000,1005 ----
***************
*** 1532,1554 ****
   *
   * This is used in two different scenarios: a normal join clause, where
   * all the Vars in the clause *must* be replaced by OUTER or INNER references;
!  * and an indexscan being used on the inner side of a nestloop join.
!  * In the latter case we want to replace the outer-relation Vars by OUTER
!  * references, while Vars of the inner relation should be adjusted by rtoffset.
!  * (We also implement RETURNING clause fixup using this second scenario.)
   *
   * For a normal join, acceptable_rel should be zero so that any failure to
!  * match a Var will be reported as an error.  For the indexscan case,
!  * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID
!  * of the inner relation.
   *
   * 'clauses' is the targetlist or list of join clauses
   * 'outer_itlist' is the indexed target list of the outer join relation
   * 'inner_itlist' is the indexed target list of the inner join relation,
   *		or NULL
   * 'acceptable_rel' is either zero or the rangetable index of a relation
!  *		whose Vars may appear in the clause without provoking an error.
!  * 'rtoffset' is what to add to varno for Vars of acceptable_rel.
   *
   * Returns the new expression tree.  The original clause structure is
   * not modified.
--- 1348,1368 ----
   *
   * This is used in two different scenarios: a normal join clause, where
   * all the Vars in the clause *must* be replaced by OUTER or INNER references;
!  * and a RETURNING clause, which may contain both Vars of the target relation
!  * and Vars of other relations.  In the latter case we want to replace the
!  * other-relation Vars by OUTER references, while leaving target Vars alone.
   *
   * For a normal join, acceptable_rel should be zero so that any failure to
!  * match a Var will be reported as an error.  For the RETURNING case, pass
!  * inner_itlist = NULL and acceptable_rel = the ID of the target relation.
   *
   * 'clauses' is the targetlist or list of join clauses
   * 'outer_itlist' is the indexed target list of the outer join relation
   * 'inner_itlist' is the indexed target list of the inner join relation,
   *		or NULL
   * 'acceptable_rel' is either zero or the rangetable index of a relation
!  *		whose Vars may appear in the clause without provoking an error
!  * 'rtoffset': how much to increment varnoold by
   *
   * Returns the new expression tree.  The original clause structure is
   * not modified.
***************
*** 1603,1610 ****
  		if (var->varno == context->acceptable_rel)
  		{
  			var = copyVar(var);
! 			var->varno += context->rtoffset;
! 			var->varnoold += context->rtoffset;
  			return (Node *) var;
  		}
  
--- 1417,1424 ----
  		if (var->varno == context->acceptable_rel)
  		{
  			var = copyVar(var);
! 			if (var->varnoold > 0)
! 				var->varnoold += context->rtoffset;
  			return (Node *) var;
  		}
  
***************
*** 1783,1789 ****
  
  	/*
  	 * We can perform the desired Var fixup by abusing the fix_join_expr
! 	 * machinery that normally handles inner indexscan fixup.  We search the
  	 * top plan's targetlist for Vars of non-result relations, and use
  	 * fix_join_expr to convert RETURNING Vars into references to those tlist
  	 * entries, while leaving result-rel Vars as-is.
--- 1597,1603 ----
  
  	/*
  	 * We can perform the desired Var fixup by abusing the fix_join_expr
! 	 * machinery that formerly handled inner indexscan fixup.  We search the
  	 * 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.
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.162
diff -c -r1.162 subselect.c
*** src/backend/optimizer/plan/subselect.c	19 Apr 2010 00:55:25 -0000	1.162
--- src/backend/optimizer/plan/subselect.c	12 Jul 2010 04:26:44 -0000
***************
*** 83,112 ****
  
  
  /*
!  * Generate a Param node to replace the given Var,
!  * which is expected to have varlevelsup > 0 (ie, it is not local).
   */
! static Param *
! replace_outer_var(PlannerInfo *root, Var *var)
  {
- 	Param	   *retval;
  	ListCell   *ppl;
  	PlannerParamItem *pitem;
  	Index		abslevel;
  	int			i;
  
- 	Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
  	abslevel = root->query_level - var->varlevelsup;
  
! 	/*
! 	 * If there's already a paramlist entry for this same Var, just use it.
! 	 * NOTE: in sufficiently complex querytrees, it is possible for the same
! 	 * varno/abslevel to refer to different RTEs in different parts of the
! 	 * parsetree, so that different fields might end up sharing the same Param
! 	 * number.	As long as we check the vartype/typmod as well, I believe that
! 	 * this sort of aliasing will cause no trouble.  The correct field should
! 	 * get stored into the Param slot at execution in each part of the tree.
! 	 */
  	i = 0;
  	foreach(ppl, root->glob->paramlist)
  	{
--- 83,102 ----
  
  
  /*
!  * Select a PARAM_EXEC number to identify the given Var.
!  * If the Var already has a param slot, return that one.
   */
! static int
! assign_param_for_var(PlannerInfo *root, Var *var)
  {
  	ListCell   *ppl;
  	PlannerParamItem *pitem;
  	Index		abslevel;
  	int			i;
  
  	abslevel = root->query_level - var->varlevelsup;
  
! 	/* If there's already a paramlist entry for this same Var, just use it */
  	i = 0;
  	foreach(ppl, root->glob->paramlist)
  	{
***************
*** 119,142 ****
  				pvar->varattno == var->varattno &&
  				pvar->vartype == var->vartype &&
  				pvar->vartypmod == var->vartypmod)
! 				break;
  		}
  		i++;
  	}
  
! 	if (!ppl)
! 	{
! 		/* Nope, so make a new one */
! 		var = (Var *) copyObject(var);
! 		var->varlevelsup = 0;
! 
! 		pitem = makeNode(PlannerParamItem);
! 		pitem->item = (Node *) var;
! 		pitem->abslevel = abslevel;
  
! 		root->glob->paramlist = lappend(root->glob->paramlist, pitem);
! 		/* i is already the correct index for the new item */
! 	}
  
  	retval = makeNode(Param);
  	retval->paramkind = PARAM_EXEC;
--- 109,184 ----
  				pvar->varattno == var->varattno &&
  				pvar->vartype == var->vartype &&
  				pvar->vartypmod == var->vartypmod)
! 				return i;
  		}
  		i++;
  	}
  
! 	/* Nope, so make a new one */
! 	var = (Var *) copyObject(var);
! 	var->varlevelsup = 0;
  
! 	pitem = makeNode(PlannerParamItem);
! 	pitem->item = (Node *) var;
! 	pitem->abslevel = abslevel;
! 
! 	root->glob->paramlist = lappend(root->glob->paramlist, pitem);
! 
! 	/* i is already the correct list index for the new item */
! 	return i;
! }
! 
! /*
!  * Generate a Param node to replace the given Var,
!  * which is expected to have varlevelsup > 0 (ie, it is not local).
!  */
! static Param *
! replace_outer_var(PlannerInfo *root, Var *var)
! {
! 	Param	   *retval;
! 	int			i;
! 
! 	Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
! 
! 	/*
! 	 * Find the Var in root->glob->paramlist, or add it if not present.
! 	 *
! 	 * NOTE: in sufficiently complex querytrees, it is possible for the same
! 	 * varno/abslevel to refer to different RTEs in different parts of the
! 	 * parsetree, so that different fields might end up sharing the same Param
! 	 * number.	As long as we check the vartype/typmod as well, I believe that
! 	 * this sort of aliasing will cause no trouble.  The correct field should
! 	 * get stored into the Param slot at execution in each part of the tree.
! 	 */
! 	i = assign_param_for_var(root, var);
! 
! 	retval = makeNode(Param);
! 	retval->paramkind = PARAM_EXEC;
! 	retval->paramid = i;
! 	retval->paramtype = var->vartype;
! 	retval->paramtypmod = var->vartypmod;
! 	retval->location = -1;
! 
! 	return retval;
! }
! 
! /*
!  * Generate a Param node to replace the given Var, which will be supplied
!  * from an upper NestLoop join node.
!  *
!  * Because we allow nestloop and subquery Params to alias each other,
!  * this is effectively the same as replace_outer_var, except that we expect
!  * the Var to be local to the current query level.
!  */
! Param *
! assign_nestloop_param(PlannerInfo *root, Var *var)
! {
! 	Param	   *retval;
! 	int			i;
! 
! 	Assert(var->varlevelsup == 0);
! 
! 	i = assign_param_for_var(root, var);
  
  	retval = makeNode(Param);
  	retval->paramkind = PARAM_EXEC;
***************
*** 1773,1780 ****
  	 *
  	 * Note: this is a bit overly generous since some parameters of upper
  	 * query levels might belong to query subtrees that don't include this
! 	 * query.  However, valid_params is only a debugging crosscheck, so it
! 	 * doesn't seem worth expending lots of cycles to try to be exact.
  	 */
  	valid_params = bms_copy(initSetParam);
  	paramid = 0;
--- 1815,1823 ----
  	 *
  	 * Note: this is a bit overly generous since some parameters of upper
  	 * query levels might belong to query subtrees that don't include this
! 	 * query, or might be nestloop params that won't be passed down at all.
! 	 * However, valid_params is only a debugging crosscheck, so it doesn't
! 	 * seem worth expending lots of cycles to try to be exact.
  	 */
  	valid_params = bms_copy(initSetParam);
  	paramid = 0;
***************
*** 1849,1854 ****
--- 1892,1899 ----
  {
  	finalize_primnode_context context;
  	int			locally_added_param;
+ 	Bitmapset  *nestloop_params;
+ 	Bitmapset  *child_params;
  
  	if (plan == NULL)
  		return NULL;
***************
*** 1856,1861 ****
--- 1901,1907 ----
  	context.root = root;
  	context.paramids = NULL;	/* initialize set to empty */
  	locally_added_param = -1;	/* there isn't one */
+ 	nestloop_params = NULL;		/* there aren't any */
  
  	/*
  	 * When we call finalize_primnode, context.paramids sets are automatically
***************
*** 2056,2063 ****
  			break;
  
  		case T_NestLoop:
! 			finalize_primnode((Node *) ((Join *) plan)->joinqual,
! 							  &context);
  			break;
  
  		case T_MergeJoin:
--- 2102,2121 ----
  			break;
  
  		case T_NestLoop:
! 			{
! 				ListCell   *l;
! 
! 				finalize_primnode((Node *) ((Join *) plan)->joinqual,
! 								  &context);
! 				/* collect set of params that will be passed to right child */
! 				foreach(l, ((NestLoop *) plan)->nestParams)
! 				{
! 					NestLoopParam *nlp = (NestLoopParam *) lfirst(l);
! 
! 					nestloop_params = bms_add_member(nestloop_params,
! 													 nlp->paramno);
! 				}
! 			}
  			break;
  
  		case T_MergeJoin:
***************
*** 2120,2136 ****
  	}
  
  	/* Process left and right child plans, if any */
! 	context.paramids = bms_add_members(context.paramids,
! 									   finalize_plan(root,
! 													 plan->lefttree,
! 													 valid_params,
! 													 scan_params));
! 
! 	context.paramids = bms_add_members(context.paramids,
! 									   finalize_plan(root,
! 													 plan->righttree,
! 													 valid_params,
! 													 scan_params));
  
  	/*
  	 * Any locally generated parameter doesn't count towards its generating
--- 2178,2209 ----
  	}
  
  	/* Process left and right child plans, if any */
! 	child_params = finalize_plan(root,
! 								 plan->lefttree,
! 								 valid_params,
! 								 scan_params);
! 	context.paramids = bms_add_members(context.paramids, child_params);
! 
! 	if (nestloop_params)
! 	{
! 		/* right child can reference nestloop_params as well as valid_params */
! 		child_params = finalize_plan(root,
! 									 plan->righttree,
! 									 bms_union(nestloop_params, valid_params),
! 									 scan_params);
! 		/* ... and they don't count as parameters used at my level */
! 		child_params = bms_difference(child_params, nestloop_params);
! 		bms_free(nestloop_params);
! 	}
! 	else
! 	{
! 		/* easy case */
! 		child_params = finalize_plan(root,
! 									 plan->righttree,
! 									 valid_params,
! 									 scan_params);
! 	}
! 	context.paramids = bms_add_members(context.paramids, child_params);
  
  	/*
  	 * Any locally generated parameter doesn't count towards its generating
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.234
diff -c -r1.234 nodes.h
*** src/include/nodes/nodes.h	28 Mar 2010 22:59:33 -0000	1.234
--- src/include/nodes/nodes.h	12 Jul 2010 04:26:44 -0000
***************
*** 74,79 ****
--- 74,80 ----
  	T_LockRows,
  	T_Limit,
  	/* these aren't subclasses of Plan: */
+ 	T_NestLoopParam,
  	T_PlanRowMark,
  	T_PlanInvalItem,
  
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.117
diff -c -r1.117 plannodes.h
*** src/include/nodes/plannodes.h	26 Feb 2010 02:01:25 -0000	1.117
--- src/include/nodes/plannodes.h	12 Jul 2010 04:26:44 -0000
***************
*** 432,444 ****
--- 432,457 ----
  
  /* ----------------
   *		nest loop join node
+  *
+  * The nestParams list identifies any executor Params that must be passed
+  * into execution of the inner subplan carrying values from the current row
+  * of the outer subplan.  Currently we restrict these values to be simple
+  * Vars, but perhaps someday that'd be worth relaxing.
   * ----------------
   */
  typedef struct NestLoop
  {
  	Join		join;
+ 	List	   *nestParams;		/* list of NestLoopParam nodes */
  } NestLoop;
  
+ typedef struct NestLoopParam
+ {
+ 	NodeTag		type;
+ 	int			paramno;		/* number of the PARAM_EXEC Param to set */
+ 	Var		   *paramval;		/* outer-relation Var to assign to Param */
+ } NestLoopParam;
+ 
  /* ----------------
   *		merge join node
   *
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/primnodes.h,v
retrieving revision 1.156
diff -c -r1.156 primnodes.h
*** src/include/nodes/primnodes.h	26 Feb 2010 02:01:25 -0000	1.156
--- src/include/nodes/primnodes.h	12 Jul 2010 04:26:44 -0000
***************
*** 175,181 ****
   *				Such parameters are numbered from 1 to n.
   *
   *		PARAM_EXEC:  The parameter is an internal executor parameter, used
!  *				for passing values into and out of sub-queries.
   *				For historical reasons, such parameters are numbered from 0.
   *				These numbers are independent of PARAM_EXTERN numbers.
   *
--- 175,182 ----
   *				Such parameters are numbered from 1 to n.
   *
   *		PARAM_EXEC:  The parameter is an internal executor parameter, used
!  *				for passing values into and out of sub-queries or from
!  *				nestloop joins to their inner scans.
   *				For historical reasons, such parameters are numbered from 0.
   *				These numbers are independent of PARAM_EXTERN numbers.
   *
Index: src/include/nodes/relation.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.187
diff -c -r1.187 relation.h
*** src/include/nodes/relation.h	6 Jul 2010 19:19:00 -0000	1.187
--- src/include/nodes/relation.h	12 Jul 2010 04:26:45 -0000
***************
*** 209,214 ****
--- 209,218 ----
  	int			wt_param_id;	/* PARAM_EXEC ID for the work table */
  	struct Plan *non_recursive_plan;	/* plan for non-recursive term */
  
+ 	/* These fields are workspace for createplan.c */
+ 	Relids		curOuterRels;			/* outer rels above current node */
+ 	List	   *curOuterParams;			/* not-yet-assigned NestLoopParams */
+ 
  	/* optional private data for join_search_hook, e.g., GEQO */
  	void	   *join_search_private;
  } PlannerInfo;
***************
*** 1330,1345 ****
  /*
   * 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
!  * either down into subqueries (for outer references in subqueries) or up out
!  * of subqueries (for the results of a subplan).  The n'th entry in the list
!  * (n counts from 0) corresponds to Param->paramid = n.
   *
   * Each paramlist item shows the absolute query level it is associated with,
   * where the outermost query is level 1 and nested subqueries have higher
   * numbers.  The item the parameter slot represents can be one of three kinds:
   *
   * A Var: the slot represents a variable of that level that must be passed
!  * down because subqueries have outer references to it.  The varlevelsup
   * value in the Var will always be zero.
   *
   * An Aggref (with an expression tree representing its argument): the slot
--- 1334,1353 ----
  /*
   * 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
!  * around from one plan node to another.  They can be used to pass values
!  * down into subqueries (for outer references in subqueries), or up out of
!  * subqueries (for the results of a subplan), or from a NestLoop plan node
!  * into its inner relation (when the inner scan is parameterized with values
!  * from the outer relation).  The n'th entry in the list (n counts from 0)
!  * corresponds to Param->paramid = n.
   *
   * Each paramlist item shows the absolute query level it is associated with,
   * where the outermost query is level 1 and nested subqueries have higher
   * numbers.  The item the parameter slot represents can be one of three kinds:
   *
   * A Var: the slot represents a variable of that level that must be passed
!  * down because subqueries have outer references to it, or must be passed
!  * from a NestLoop node of that level to its inner scan.  The varlevelsup
   * value in the Var will always be zero.
   *
   * An Aggref (with an expression tree representing its argument): the slot
***************
*** 1352,1358 ****
   * to the parent query of the subplan.
   *
   * Note: we detect duplicate Var parameters and coalesce them into one slot,
!  * but we do not do this for Aggref or Param slots.
   */
  typedef struct PlannerParamItem
  {
--- 1360,1372 ----
   * to the parent query of the subplan.
   *
   * Note: we detect duplicate Var parameters and coalesce them into one slot,
!  * but we do not bother to do this for Aggrefs, and it would be incorrect
!  * to do so for Param slots.  Duplicate detection is actually *necessary*
!  * in the case of NestLoop parameters since it serves to match up the usage
!  * of a Param (in the inner scan) with the assignment of the value (in the
!  * NestLoop node).  This might result in the same PARAM_EXEC slot being used
!  * by multiple NestLoop nodes or SubPlan nodes, but no harm is done since
!  * the same value would be assigned anyway.
   */
  typedef struct PlannerParamItem
  {
Index: src/include/optimizer/subselect.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/optimizer/subselect.h,v
retrieving revision 1.39
diff -c -r1.39 subselect.h
*** src/include/optimizer/subselect.h	2 Jan 2010 16:58:07 -0000	1.39
--- src/include/optimizer/subselect.h	12 Jul 2010 04:26:45 -0000
***************
*** 29,34 ****
--- 29,35 ----
  				 bool attach_initplans);
  extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
  						   Oid resulttype, int32 resulttypmod);
+ extern Param *assign_nestloop_param(PlannerInfo *root, Var *var);
  extern int	SS_assign_special_param(PlannerInfo *root);
  
  #endif   /* SUBSELECT_H */
