explain-params-1.patch

text/x-patch

Filename: explain-params-1.patch
Type: text/x-patch
Part: 0
Message: Re: explain.c: why trace PlanState and Plan trees separately?

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/commands/explain.c 109 128
src/backend/utils/adt/ruleutils.c 366 144
src/include/utils/builtins.h 2 2
Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.206
diff -c -r1.206 explain.c
*** src/backend/commands/explain.c	10 Jun 2010 01:26:30 -0000	1.206
--- src/backend/commands/explain.c	13 Jul 2010 18:13:25 -0000
***************
*** 54,80 ****
  static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
  				ExplainState *es);
  static double elapsed_time(instr_time *starttime);
! static void ExplainNode(Plan *plan, PlanState *planstate,
! 			Plan *outer_plan,
  			const char *relationship, const char *plan_name,
  			ExplainState *es);
! static void show_plan_tlist(Plan *plan, ExplainState *es);
! static void show_qual(List *qual, const char *qlabel, Plan *plan,
! 		  Plan *outer_plan, bool useprefix, ExplainState *es);
  static void show_scan_qual(List *qual, const char *qlabel,
! 			   Plan *scan_plan, Plan *outer_plan,
! 			   ExplainState *es);
! static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
! 				ExplainState *es);
! static void show_sort_keys(Plan *sortplan, ExplainState *es);
  static void show_sort_info(SortState *sortstate, ExplainState *es);
  static void show_hash_info(HashState *hashstate, ExplainState *es);
  static const char *explain_get_index_name(Oid indexId);
  static void ExplainScanTarget(Scan *plan, ExplainState *es);
! static void ExplainMemberNodes(List *plans, PlanState **planstate,
! 				   Plan *outer_plan, ExplainState *es);
! static void ExplainSubPlans(List *plans, const char *relationship,
! 				ExplainState *es);
  static void ExplainPropertyList(const char *qlabel, List *data,
  					ExplainState *es);
  static void ExplainProperty(const char *qlabel, const char *value,
--- 54,83 ----
  static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
  				ExplainState *es);
  static double elapsed_time(instr_time *starttime);
! static void ExplainNode(PlanState *planstate, List *ancestors,
  			const char *relationship, const char *plan_name,
  			ExplainState *es);
! static void show_plan_tlist(PlanState *planstate, List *ancestors,
! 							ExplainState *es);
! static void show_qual(List *qual, const char *qlabel,
! 					  PlanState *planstate, List *ancestors,
! 					  bool useprefix, ExplainState *es);
  static void show_scan_qual(List *qual, const char *qlabel,
! 						   PlanState *planstate, List *ancestors,
! 						   ExplainState *es);
! static void show_upper_qual(List *qual, const char *qlabel,
! 							PlanState *planstate, List *ancestors,
! 							ExplainState *es);
! static void show_sort_keys(SortState *sortstate, List *ancestors,
! 						   ExplainState *es);
  static void show_sort_info(SortState *sortstate, ExplainState *es);
  static void show_hash_info(HashState *hashstate, ExplainState *es);
  static const char *explain_get_index_name(Oid indexId);
  static void ExplainScanTarget(Scan *plan, ExplainState *es);
! static void ExplainMemberNodes(List *plans, PlanState **planstates,
! 				   List *ancestors, ExplainState *es);
! static void ExplainSubPlans(List *plans, List *ancestors,
! 							const char *relationship, ExplainState *es);
  static void ExplainPropertyList(const char *qlabel, List *data,
  					ExplainState *es);
  static void ExplainProperty(const char *qlabel, const char *value,
***************
*** 484,491 ****
  	Assert(queryDesc->plannedstmt != NULL);
  	es->pstmt = queryDesc->plannedstmt;
  	es->rtable = queryDesc->plannedstmt->rtable;
! 	ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
! 				NULL, NULL, NULL, es);
  }
  
  /*
--- 487,493 ----
  	Assert(queryDesc->plannedstmt != NULL);
  	es->pstmt = queryDesc->plannedstmt;
  	es->rtable = queryDesc->plannedstmt->rtable;
! 	ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
  }
  
  /*
***************
*** 585,615 ****
  
  /*
   * ExplainNode -
!  *	  Appends a description of the Plan node to es->str
   *
!  * planstate points to the executor state node corresponding to the plan node.
!  * We need this to get at the instrumentation data (if any) as well as the
!  * list of subplans.
!  *
!  * outer_plan, if not null, references another plan node that is the outer
!  * side of a join with the current node.  This is only interesting for
!  * deciphering runtime keys of an inner indexscan.
   *
   * relationship describes the relationship of this plan node to its parent
   * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
   * optional name to be attached to the node.
   *
   * In text format, es->indent is controlled in this function since we only
!  * want it to change at Plan-node boundaries.  In non-text formats, es->indent
   * corresponds to the nesting depth of logical output groups, and therefore
   * is controlled by ExplainOpenGroup/ExplainCloseGroup.
   */
  static void
! ExplainNode(Plan *plan, PlanState *planstate,
! 			Plan *outer_plan,
  			const char *relationship, const char *plan_name,
  			ExplainState *es)
  {
  	const char *pname;			/* node type name for text output */
  	const char *sname;			/* node type name for non-text output */
  	const char *strategy = NULL;
--- 587,616 ----
  
  /*
   * ExplainNode -
!  *	  Appends a description of a plan tree to es->str
   *
!  * planstate points to the executor state node for the current plan node.
!  * We need to work from a PlanState node, not just a Plan node, in order to
!  * get at the instrumentation data (if any) as well as the list of subplans.
!  *
!  * ancestors is a list of parent PlanState nodes, most-closely-nested first.
!  * These are needed in order to interpret PARAM_EXEC Params.
   *
   * relationship describes the relationship of this plan node to its parent
   * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
   * optional name to be attached to the node.
   *
   * In text format, es->indent is controlled in this function since we only
!  * want it to change at plan-node boundaries.  In non-text formats, es->indent
   * corresponds to the nesting depth of logical output groups, and therefore
   * is controlled by ExplainOpenGroup/ExplainCloseGroup.
   */
  static void
! ExplainNode(PlanState *planstate, List *ancestors,
  			const char *relationship, const char *plan_name,
  			ExplainState *es)
  {
+ 	Plan	   *plan = planstate->plan;
  	const char *pname;			/* node type name for text output */
  	const char *sname;			/* node type name for non-text output */
  	const char *strategy = NULL;
***************
*** 617,624 ****
  	int			save_indent = es->indent;
  	bool		haschildren;
  
- 	Assert(plan);
- 
  	switch (nodeTag(plan))
  	{
  		case T_Result:
--- 618,623 ----
***************
*** 999,1021 ****
  
  	/* target list */
  	if (es->verbose)
! 		show_plan_tlist(plan, es);
  
  	/* quals, sort keys, etc */
  	switch (nodeTag(plan))
  	{
  		case T_IndexScan:
  			show_scan_qual(((IndexScan *) plan)->indexqualorig,
! 						   "Index Cond", plan, outer_plan, es);
! 			show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
  			break;
  		case T_BitmapIndexScan:
  			show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
! 						   "Index Cond", plan, outer_plan, es);
  			break;
  		case T_BitmapHeapScan:
  			show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
! 						   "Recheck Cond", plan, outer_plan, es);
  			/* FALL THRU */
  		case T_SeqScan:
  		case T_FunctionScan:
--- 998,1020 ----
  
  	/* target list */
  	if (es->verbose)
! 		show_plan_tlist(planstate, ancestors, es);
  
  	/* quals, sort keys, etc */
  	switch (nodeTag(plan))
  	{
  		case T_IndexScan:
  			show_scan_qual(((IndexScan *) plan)->indexqualorig,
! 						   "Index Cond", planstate, ancestors, es);
! 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_BitmapIndexScan:
  			show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
! 						   "Index Cond", planstate, ancestors, es);
  			break;
  		case T_BitmapHeapScan:
  			show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
! 						   "Recheck Cond", planstate, ancestors, es);
  			/* FALL THRU */
  		case T_SeqScan:
  		case T_FunctionScan:
***************
*** 1023,1029 ****
  		case T_CteScan:
  		case T_WorkTableScan:
  		case T_SubqueryScan:
! 			show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
  			break;
  		case T_TidScan:
  			{
--- 1022,1028 ----
  		case T_CteScan:
  		case T_WorkTableScan:
  		case T_SubqueryScan:
! 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_TidScan:
  			{
***************
*** 1035,1075 ****
  
  				if (list_length(tidquals) > 1)
  					tidquals = list_make1(make_orclause(tidquals));
! 				show_scan_qual(tidquals, "TID Cond", plan, outer_plan, es);
! 				show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
  			}
  			break;
  		case T_NestLoop:
  			show_upper_qual(((NestLoop *) plan)->join.joinqual,
! 							"Join Filter", plan, es);
! 			show_upper_qual(plan->qual, "Filter", plan, es);
  			break;
  		case T_MergeJoin:
  			show_upper_qual(((MergeJoin *) plan)->mergeclauses,
! 							"Merge Cond", plan, es);
  			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
! 							"Join Filter", plan, es);
! 			show_upper_qual(plan->qual, "Filter", plan, es);
  			break;
  		case T_HashJoin:
  			show_upper_qual(((HashJoin *) plan)->hashclauses,
! 							"Hash Cond", plan, es);
  			show_upper_qual(((HashJoin *) plan)->join.joinqual,
! 							"Join Filter", plan, es);
! 			show_upper_qual(plan->qual, "Filter", plan, es);
  			break;
  		case T_Agg:
  		case T_Group:
! 			show_upper_qual(plan->qual, "Filter", plan, es);
  			break;
  		case T_Sort:
! 			show_sort_keys(plan, es);
  			show_sort_info((SortState *) planstate, es);
  			break;
  		case T_Result:
  			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
! 							"One-Time Filter", plan, es);
! 			show_upper_qual(plan->qual, "Filter", plan, es);
  			break;
  		case T_Hash:
  			show_hash_info((HashState *) planstate, es);
--- 1034,1074 ----
  
  				if (list_length(tidquals) > 1)
  					tidquals = list_make1(make_orclause(tidquals));
! 				show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
! 				show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
  			}
  			break;
  		case T_NestLoop:
  			show_upper_qual(((NestLoop *) plan)->join.joinqual,
! 							"Join Filter", planstate, ancestors, es);
! 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_MergeJoin:
  			show_upper_qual(((MergeJoin *) plan)->mergeclauses,
! 							"Merge Cond", planstate, ancestors, es);
  			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
! 							"Join Filter", planstate, ancestors, es);
! 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_HashJoin:
  			show_upper_qual(((HashJoin *) plan)->hashclauses,
! 							"Hash Cond", planstate, ancestors, es);
  			show_upper_qual(((HashJoin *) plan)->join.joinqual,
! 							"Join Filter", planstate, ancestors, es);
! 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_Agg:
  		case T_Group:
! 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_Sort:
! 			show_sort_keys((SortState *) planstate, ancestors, es);
  			show_sort_info((SortState *) planstate, es);
  			break;
  		case T_Result:
  			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
! 							"One-Time Filter", planstate, ancestors, es);
! 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
  			break;
  		case T_Hash:
  			show_hash_info((HashState *) planstate, es);
***************
*** 1169,1198 ****
  	if (haschildren)
  		ExplainOpenGroup("Plans", "Plans", false, es);
  
  	/* initPlan-s */
  	if (plan->initPlan)
! 		ExplainSubPlans(planstate->initPlan, "InitPlan", es);
  
  	/* lefttree */
! 	if (outerPlan(plan))
! 	{
! 		/*
! 		 * Ordinarily we don't pass down our own outer_plan value to our child
! 		 * nodes, but in bitmap scan trees we must, since the bottom
! 		 * BitmapIndexScan nodes may have outer references.
! 		 */
! 		ExplainNode(outerPlan(plan), outerPlanState(planstate),
! 					IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
  					"Outer", NULL, es);
- 	}
  
  	/* righttree */
! 	if (innerPlan(plan))
! 	{
! 		ExplainNode(innerPlan(plan), innerPlanState(planstate),
! 					outerPlan(plan),
  					"Inner", NULL, es);
- 	}
  
  	/* special child plans */
  	switch (nodeTag(plan))
--- 1168,1189 ----
  	if (haschildren)
  		ExplainOpenGroup("Plans", "Plans", false, es);
  
+ 	/* Pass current PlanState as head of ancestors list for children */
+ 	ancestors = lcons(planstate, ancestors); 
+ 
  	/* initPlan-s */
  	if (plan->initPlan)
! 		ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
  
  	/* lefttree */
! 	if (outerPlanState(planstate))
! 		ExplainNode(outerPlanState(planstate), ancestors,
  					"Outer", NULL, es);
  
  	/* righttree */
! 	if (innerPlanState(planstate))
! 		ExplainNode(innerPlanState(planstate), ancestors,
  					"Inner", NULL, es);
  
  	/* special child plans */
  	switch (nodeTag(plan))
***************
*** 1200,1231 ****
  		case T_ModifyTable:
  			ExplainMemberNodes(((ModifyTable *) plan)->plans,
  							   ((ModifyTableState *) planstate)->mt_plans,
! 							   outer_plan, es);
  			break;
  		case T_Append:
  			ExplainMemberNodes(((Append *) plan)->appendplans,
  							   ((AppendState *) planstate)->appendplans,
! 							   outer_plan, es);
  			break;
  		case T_BitmapAnd:
  			ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
  							   ((BitmapAndState *) planstate)->bitmapplans,
! 							   outer_plan, es);
  			break;
  		case T_BitmapOr:
  			ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
  							   ((BitmapOrState *) planstate)->bitmapplans,
! 							   outer_plan, es);
  			break;
  		case T_SubqueryScan:
! 			{
! 				SubqueryScan *subqueryscan = (SubqueryScan *) plan;
! 				SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
! 
! 				ExplainNode(subqueryscan->subplan, subquerystate->subplan,
! 							NULL,
! 							"Subquery", NULL, es);
! 			}
  			break;
  		default:
  			break;
--- 1191,1216 ----
  		case T_ModifyTable:
  			ExplainMemberNodes(((ModifyTable *) plan)->plans,
  							   ((ModifyTableState *) planstate)->mt_plans,
! 							   ancestors, es);
  			break;
  		case T_Append:
  			ExplainMemberNodes(((Append *) plan)->appendplans,
  							   ((AppendState *) planstate)->appendplans,
! 							   ancestors, es);
  			break;
  		case T_BitmapAnd:
  			ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
  							   ((BitmapAndState *) planstate)->bitmapplans,
! 							   ancestors, es);
  			break;
  		case T_BitmapOr:
  			ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
  							   ((BitmapOrState *) planstate)->bitmapplans,
! 							   ancestors, es);
  			break;
  		case T_SubqueryScan:
! 			ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
! 						"Subquery", NULL, es);
  			break;
  		default:
  			break;
***************
*** 1233,1241 ****
  
  	/* subPlan-s */
  	if (planstate->subPlan)
! 		ExplainSubPlans(planstate->subPlan, "SubPlan", es);
  
  	/* end of child plans */
  	if (haschildren)
  		ExplainCloseGroup("Plans", "Plans", false, es);
  
--- 1218,1227 ----
  
  	/* subPlan-s */
  	if (planstate->subPlan)
! 		ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
  
  	/* end of child plans */
+ 	ancestors = list_delete_first(ancestors); 
  	if (haschildren)
  		ExplainCloseGroup("Plans", "Plans", false, es);
  
***************
*** 1252,1259 ****
   * Show the targetlist of a plan node
   */
  static void
! show_plan_tlist(Plan *plan, ExplainState *es)
  {
  	List	   *context;
  	List	   *result = NIL;
  	bool		useprefix;
--- 1238,1246 ----
   * Show the targetlist of a plan node
   */
  static void
! show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
  {
+ 	Plan	   *plan = planstate->plan;
  	List	   *context;
  	List	   *result = NIL;
  	bool		useprefix;
***************
*** 1271,1280 ****
  		return;
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_plan((Node *) plan,
! 									   NULL,
! 									   es->rtable,
! 									   es->pstmt->subplans);
  	useprefix = list_length(es->rtable) > 1;
  
  	/* Deparse each result column (we now include resjunk ones) */
--- 1258,1266 ----
  		return;
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_planstate((Node *) planstate,
! 											ancestors,
! 											es->rtable);
  	useprefix = list_length(es->rtable) > 1;
  
  	/* Deparse each result column (we now include resjunk ones) */
***************
*** 1294,1305 ****
  
  /*
   * Show a qualifier expression
-  *
-  * Note: outer_plan is the referent for any OUTER vars in the scan qual;
-  * this would be the outer side of a nestloop plan.  Pass NULL if none.
   */
  static void
! show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
  		  bool useprefix, ExplainState *es)
  {
  	List	   *context;
--- 1280,1289 ----
  
  /*
   * Show a qualifier expression
   */
  static void
! show_qual(List *qual, const char *qlabel,
! 		  PlanState *planstate, List *ancestors,
  		  bool useprefix, ExplainState *es)
  {
  	List	   *context;
***************
*** 1314,1323 ****
  	node = (Node *) make_ands_explicit(qual);
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_plan((Node *) plan,
! 									   (Node *) outer_plan,
! 									   es->rtable,
! 									   es->pstmt->subplans);
  
  	/* Deparse the expression */
  	exprstr = deparse_expression(node, context, useprefix, false);
--- 1298,1306 ----
  	node = (Node *) make_ands_explicit(qual);
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_planstate((Node *) planstate,
! 											ancestors,
! 											es->rtable);
  
  	/* Deparse the expression */
  	exprstr = deparse_expression(node, context, useprefix, false);
***************
*** 1331,1366 ****
   */
  static void
  show_scan_qual(List *qual, const char *qlabel,
! 			   Plan *scan_plan, Plan *outer_plan,
  			   ExplainState *es)
  {
  	bool		useprefix;
  
! 	useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
! 				 es->verbose);
! 	show_qual(qual, qlabel, scan_plan, outer_plan, useprefix, es);
  }
  
  /*
   * Show a qualifier expression for an upper-level plan node
   */
  static void
! show_upper_qual(List *qual, const char *qlabel, Plan *plan, ExplainState *es)
  {
  	bool		useprefix;
  
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
! 	show_qual(qual, qlabel, plan, NULL, useprefix, es);
  }
  
  /*
   * Show the sort keys for a Sort node.
   */
  static void
! show_sort_keys(Plan *sortplan, ExplainState *es)
  {
! 	int			nkeys = ((Sort *) sortplan)->numCols;
! 	AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
  	List	   *context;
  	List	   *result = NIL;
  	bool		useprefix;
--- 1314,1351 ----
   */
  static void
  show_scan_qual(List *qual, const char *qlabel,
! 			   PlanState *planstate, List *ancestors,
  			   ExplainState *es)
  {
  	bool		useprefix;
  
! 	useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
! 	show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
  }
  
  /*
   * Show a qualifier expression for an upper-level plan node
   */
  static void
! show_upper_qual(List *qual, const char *qlabel,
! 				PlanState *planstate, List *ancestors,
! 				ExplainState *es)
  {
  	bool		useprefix;
  
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
! 	show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
  }
  
  /*
   * Show the sort keys for a Sort node.
   */
  static void
! show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
  {
! 	Sort	   *plan = (Sort *) sortstate->ss.ps.plan;
! 	int			nkeys = plan->numCols;
! 	AttrNumber *keycols = plan->sortColIdx;
  	List	   *context;
  	List	   *result = NIL;
  	bool		useprefix;
***************
*** 1371,1387 ****
  		return;
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_plan((Node *) sortplan,
! 									   NULL,
! 									   es->rtable,
! 									   es->pstmt->subplans);
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
  
  	for (keyno = 0; keyno < nkeys; keyno++)
  	{
  		/* find key expression in tlist */
  		AttrNumber	keyresno = keycols[keyno];
! 		TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);
  
  		if (!target)
  			elog(ERROR, "no tlist entry for key %d", keyresno);
--- 1356,1372 ----
  		return;
  
  	/* Set up deparsing context */
! 	context = deparse_context_for_planstate((Node *) sortstate,
! 											ancestors,
! 											es->rtable);
  	useprefix = (list_length(es->rtable) > 1 || es->verbose);
  
  	for (keyno = 0; keyno < nkeys; keyno++)
  	{
  		/* find key expression in tlist */
  		AttrNumber	keyresno = keycols[keyno];
! 		TargetEntry *target = get_tle_by_resno(plan->plan.targetlist,
! 											   keyresno);
  
  		if (!target)
  			elog(ERROR, "no tlist entry for key %d", keyresno);
***************
*** 1596,1629 ****
   * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
   * or BitmapOr node.
   *
!  * Ordinarily we don't pass down outer_plan to our child nodes, but in these
!  * cases we must, since the node could be an "inner indexscan" in which case
!  * outer references can appear in the child nodes.
   */
  static void
! ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
! 				   ExplainState *es)
  {
! 	ListCell   *lst;
! 	int			j = 0;
  
! 	foreach(lst, plans)
! 	{
! 		Plan	   *subnode = (Plan *) lfirst(lst);
! 
! 		ExplainNode(subnode, planstate[j],
! 					outer_plan,
! 					"Member", NULL,
! 					es);
! 		j++;
! 	}
  }
  
  /*
   * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
   */
  static void
! ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
  {
  	ListCell   *lst;
  
--- 1581,1613 ----
   * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
   * or BitmapOr node.
   *
!  * The ancestors list should already contain the immediate parent of these
!  * plans.
!  *
!  * Note: we don't actually need to examine the Plan list members, but
!  * we need the list in order to determine the length of the PlanState array.
   */
  static void
! ExplainMemberNodes(List *plans, PlanState **planstates,
! 				   List *ancestors, ExplainState *es)
  {
! 	int			nplans = list_length(plans);
! 	int			j;
  
! 	for (j = 0; j < nplans; j++)
! 		ExplainNode(planstates[j], ancestors,
! 					"Member", NULL, es);
  }
  
  /*
   * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
+  *
+  * The ancestors list should already contain the immediate parent of these
+  * SubPlanStates.
   */
  static void
! ExplainSubPlans(List *plans, List *ancestors,
! 				const char *relationship, ExplainState *es)
  {
  	ListCell   *lst;
  
***************
*** 1632,1642 ****
  		SubPlanState *sps = (SubPlanState *) lfirst(lst);
  		SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
  
! 		ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
! 					sps->planstate,
! 					NULL,
! 					relationship, sp->plan_name,
! 					es);
  	}
  }
  
--- 1616,1623 ----
  		SubPlanState *sps = (SubPlanState *) lfirst(lst);
  		SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
  
! 		ExplainNode(sps->planstate, ancestors,
! 					relationship, sp->plan_name, es);
  	}
  }
  
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.327
diff -c -r1.327 ruleutils.c
*** src/backend/utils/adt/ruleutils.c	9 Jul 2010 21:11:47 -0000	1.327
--- src/backend/utils/adt/ruleutils.c	13 Jul 2010 18:13:25 -0000
***************
*** 102,117 ****
   * The rangetable is the list of actual RTEs from the query tree, and the
   * cte list is the list of actual CTEs.
   *
!  * For deparsing plan trees, we provide for outer and inner subplan nodes.
!  * The tlists of these nodes are used to resolve OUTER and INNER varnos.
!  * Also, in the plan-tree case we don't have access to the parse-time CTE
!  * list, so we need a list of subplans instead.
   */
  typedef struct
  {
  	List	   *rtable;			/* List of RangeTblEntry nodes */
  	List	   *ctes;			/* List of CommonTableExpr nodes */
! 	List	   *subplans;		/* List of subplans, in plan-tree case */
  	Plan	   *outer_plan;		/* OUTER subplan, or NULL if none */
  	Plan	   *inner_plan;		/* INNER subplan, or NULL if none */
  } deparse_namespace;
--- 102,124 ----
   * The rangetable is the list of actual RTEs from the query tree, and the
   * cte list is the list of actual CTEs.
   *
!  * When deparsing plan trees, there is always just a single item in the
!  * deparse_namespace list (since a plan tree never contains Vars with
!  * varlevelsup > 0).  We store the PlanState node that is the immediate
!  * parent of the expression to be deparsed, as well as a list of that
!  * PlanState's ancestors.  In addition, we store the outer and inner
!  * subplan nodes, whose targetlists are used to resolve OUTER and INNER Vars.
!  * (Note: these could be derived on-the-fly from the planstate instead.)
   */
  typedef struct
  {
  	List	   *rtable;			/* List of RangeTblEntry nodes */
  	List	   *ctes;			/* List of CommonTableExpr nodes */
! 	/* Remaining fields are used only when deparsing a Plan tree: */
! 	PlanState  *planstate;		/* immediate parent of current expression */
! 	List	   *ancestors;		/* ancestors of planstate */
! 	PlanState  *outer_planstate;	/* OUTER subplan state, or NULL if none */
! 	PlanState  *inner_planstate;	/* INNER subplan state, or NULL if none */
  	Plan	   *outer_plan;		/* OUTER subplan, or NULL if none */
  	Plan	   *inner_plan;		/* INNER subplan, or NULL if none */
  } deparse_namespace;
***************
*** 154,159 ****
--- 161,175 ----
  static int print_function_arguments(StringInfo buf, HeapTuple proctup,
  						 bool print_table_args, bool print_defaults);
  static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+ static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
+ static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
+ 				deparse_namespace *save_dpns);
+ static void pop_child_plan(deparse_namespace *dpns,
+ 						   deparse_namespace *save_dpns);
+ static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+ 				   deparse_namespace *save_dpns);
+ static void pop_ancestor_plan(deparse_namespace *dpns,
+ 							  deparse_namespace *save_dpns);
  static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
  			 int prettyFlags);
  static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
***************
*** 183,193 ****
  static void get_rule_windowclause(Query *query, deparse_context *context);
  static void get_rule_windowspec(WindowClause *wc, List *targetList,
  					deparse_context *context);
- static void push_plan(deparse_namespace *dpns, Plan *subplan);
  static char *get_variable(Var *var, int levelsup, bool showstar,
  			 deparse_context *context);
  static RangeTblEntry *find_rte_by_refname(const char *refname,
  					deparse_context *context);
  static const char *get_simple_binary_op_name(OpExpr *expr);
  static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
  static void appendContextKeyword(deparse_context *context, const char *str,
--- 199,211 ----
  static void get_rule_windowclause(Query *query, deparse_context *context);
  static void get_rule_windowspec(WindowClause *wc, List *targetList,
  					deparse_context *context);
  static char *get_variable(Var *var, int levelsup, bool showstar,
  			 deparse_context *context);
  static RangeTblEntry *find_rte_by_refname(const char *refname,
  					deparse_context *context);
+ static void get_parameter(Param *param, deparse_context *context);
+ static void print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+ 					 deparse_namespace *dpns, deparse_context *context);
  static const char *get_simple_binary_op_name(OpExpr *expr);
  static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
  static void appendContextKeyword(deparse_context *context, const char *str,
***************
*** 625,634 ****
  		newrte->inFromCl = true;
  
  		/* Build two-element rtable */
  		dpns.rtable = list_make2(oldrte, newrte);
  		dpns.ctes = NIL;
- 		dpns.subplans = NIL;
- 		dpns.outer_plan = dpns.inner_plan = NULL;
  
  		/* Set up context with one-deep namespace stack */
  		context.buf = &buf;
--- 643,651 ----
  		newrte->inFromCl = true;
  
  		/* Build two-element rtable */
+ 		memset(&dpns, 0, sizeof(dpns));
  		dpns.rtable = list_make2(oldrte, newrte);
  		dpns.ctes = NIL;
  
  		/* Set up context with one-deep namespace stack */
  		context.buf = &buf;
***************
*** 2072,2078 ****
  	deparse_namespace *dpns;
  	RangeTblEntry *rte;
  
! 	dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
  
  	/* Build a minimal RTE for the rel */
  	rte = makeNode(RangeTblEntry);
--- 2089,2095 ----
  	deparse_namespace *dpns;
  	RangeTblEntry *rte;
  
! 	dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
  
  	/* Build a minimal RTE for the rel */
  	rte = makeNode(RangeTblEntry);
***************
*** 2085,2147 ****
  	/* Build one-element rtable */
  	dpns->rtable = list_make1(rte);
  	dpns->ctes = NIL;
- 	dpns->subplans = NIL;
- 	dpns->outer_plan = dpns->inner_plan = NULL;
  
  	/* Return a one-deep namespace stack */
  	return list_make1(dpns);
  }
  
  /*
!  * deparse_context_for_plan		- Build deparse context for a plan node
   *
   * When deparsing an expression in a Plan tree, we might have to resolve
   * OUTER or INNER references.  To do this, the caller must provide the
!  * parent Plan node.  In the normal case of a join plan node, OUTER and
!  * INNER references can be resolved by drilling down into the left and
!  * right child plans.  A special case is that a nestloop inner indexscan
!  * might have OUTER Vars, but the outer side of the join is not a child
!  * plan node.  To handle such cases the outer plan node must be passed
!  * separately.	(Pass NULL for outer_plan otherwise.)
   *
!  * Note: plan and outer_plan really ought to be declared as "Plan *", but
!  * we use "Node *" to avoid having to include plannodes.h in builtins.h.
   *
   * The plan's rangetable list must also be passed.  We actually prefer to use
   * the rangetable to resolve simple Vars, but the plan inputs are necessary
   * for Vars that reference expressions computed in subplan target lists.
-  *
-  * We also need the list of subplans associated with the Plan tree; this
-  * is for resolving references to CTE subplans.
   */
  List *
! deparse_context_for_plan(Node *plan, Node *outer_plan,
! 						 List *rtable, List *subplans)
  {
  	deparse_namespace *dpns;
  
! 	dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
  
  	dpns->rtable = rtable;
  	dpns->ctes = NIL;
! 	dpns->subplans = subplans;
  
  	/*
! 	 * Set up outer_plan and inner_plan from the Plan node (this includes
! 	 * various special cases for particular Plan types).
  	 */
! 	push_plan(dpns, (Plan *) plan);
  
  	/*
! 	 * If outer_plan is given, that overrides whatever we got from the plan.
  	 */
- 	if (outer_plan)
- 		dpns->outer_plan = (Plan *) outer_plan;
  
! 	/* Return a one-deep namespace stack */
! 	return list_make1(dpns);
  }
  
  /* ----------
   * make_ruledef			- reconstruct the CREATE RULE command
   *				  for a given pg_rewrite tuple
--- 2102,2292 ----
  	/* Build one-element rtable */
  	dpns->rtable = list_make1(rte);
  	dpns->ctes = NIL;
  
  	/* Return a one-deep namespace stack */
  	return list_make1(dpns);
  }
  
  /*
!  * deparse_context_for_planstate	- Build deparse context for a plan
   *
   * When deparsing an expression in a Plan tree, we might have to resolve
   * OUTER or INNER references.  To do this, the caller must provide the
!  * parent PlanState node.  Then OUTER and INNER references can be resolved
!  * by drilling down into the left and right child plans.
   *
!  * Note: planstate really ought to be declared as "PlanState *", but we use
!  * "Node *" to avoid having to include execnodes.h in builtins.h.
!  *
!  * The ancestors list is a list of the PlanState's parent PlanStates, the
!  * most-closely-nested first.  This is needed to resolve PARAM_EXEC Params.
!  * Note we assume that all the PlanStates share the same rtable.
   *
   * The plan's rangetable list must also be passed.  We actually prefer to use
   * the rangetable to resolve simple Vars, but the plan inputs are necessary
   * for Vars that reference expressions computed in subplan target lists.
   */
  List *
! deparse_context_for_planstate(Node *planstate, List *ancestors,
! 							  List *rtable)
  {
  	deparse_namespace *dpns;
  
! 	dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
  
+ 	/* Initialize fields that stay the same across the whole plan tree */
  	dpns->rtable = rtable;
  	dpns->ctes = NIL;
! 
! 	/* Set our attention on the specific plan node passed in */
! 	set_deparse_planstate(dpns, (PlanState *) planstate);
! 	dpns->ancestors = ancestors;
! 
! 	/* Return a one-deep namespace stack */
! 	return list_make1(dpns);
! }
! 
! /*
!  * set_deparse_planstate: set up deparse_namespace to parse subexpressions
!  * of a given PlanState node
!  *
!  * This sets the planstate, outer_planstate, inner_planstate, outer_plan, and
!  * inner_plan fields.  Caller is responsible for adjusting the ancestors list
!  * if necessary.  Note that the rtable and ctes fields do not need to change
!  * when shifting attention to different plan nodes in a single plan tree.
!  */
! static void
! set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
! {
! 	dpns->planstate = ps;
! 
! 	/*
! 	 * We special-case Append to pretend that the first child plan is the
! 	 * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
! 	 * according to one of the children, and the first one is the most natural
! 	 * choice.	Likewise special-case ModifyTable to pretend that the first
! 	 * child plan is the OUTER referent; this is to support RETURNING lists
! 	 * containing references to non-target relations.
! 	 */
! 	if (IsA(ps, AppendState))
! 		dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
! 	else if (IsA(ps, ModifyTableState))
! 		dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
! 	else
! 		dpns->outer_planstate = outerPlanState(ps);
! 
! 	if (dpns->outer_planstate)
! 		dpns->outer_plan = dpns->outer_planstate->plan;
! 	else
! 		dpns->outer_plan = NULL;
  
  	/*
! 	 * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
! 	 * use OUTER because that could someday conflict with the normal meaning.)
! 	 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
  	 */
! 	if (IsA(ps, SubqueryScanState))
! 		dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
! 	else if (IsA(ps, CteScanState))
! 		dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
! 	else
! 		dpns->inner_planstate = innerPlanState(ps);
! 
! 	if (dpns->inner_planstate)
! 		dpns->inner_plan = dpns->inner_planstate->plan;
! 	else
! 		dpns->inner_plan = NULL;
! }
! 
! /*
!  * push_child_plan: temporarily transfer deparsing attention to a child plan
!  *
!  * When expanding an OUTER or INNER reference, we must adjust the deparse
!  * context in case the referenced expression itself uses OUTER/INNER.	We
!  * modify the top stack entry in-place to avoid affecting levelsup issues
!  * (although in a Plan tree there really shouldn't be any).
!  *
!  * Caller must provide a local deparse_namespace variable to save the
!  * previous state for pop_child_plan.
!  */
! static void
! push_child_plan(deparse_namespace *dpns, PlanState *ps,
! 				deparse_namespace *save_dpns)
! {
! 	/* Save state for restoration later */
! 	*save_dpns = *dpns;
  
  	/*
! 	 * Currently we don't bother to adjust the ancestors list, because an
! 	 * OUTER or INNER reference really shouldn't contain any Params that
! 	 * would be set by the parent node itself.  If we did want to adjust it,
! 	 * lcons'ing dpns->planstate onto dpns->ancestors would be the appropriate
! 	 * thing --- and pop_child_plan would need to undo the change to the list.
  	 */
  
! 	/* Set attention on selected child */
! 	set_deparse_planstate(dpns, ps);
! }
! 
! /*
!  * pop_child_plan: undo the effects of push_child_plan
!  */
! static void
! pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
! {
! 	/* Restore fields changed by push_child_plan */
! 	*dpns = *save_dpns;
! }
! 
! /*
!  * push_ancestor_plan: temporarily transfer deparsing attention to an
!  * ancestor plan
!  *
!  * When expanding a Param reference, we must adjust the deparse context
!  * to match the plan node that contains the expression being printed;
!  * otherwise we'd fail if that expression itself contains a Param or
!  * OUTER/INNER variables.
!  *
!  * The target ancestor is conveniently identified by the ListCell holding it
!  * in dpns->ancestors.
!  *
!  * Caller must provide a local deparse_namespace variable to save the
!  * previous state for pop_ancestor_plan.
!  */
! static void
! push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
! 				   deparse_namespace *save_dpns)
! {
! 	PlanState  *ps = (PlanState *) lfirst(ancestor_cell);
! 	List	   *ancestors;
! 
! 	/* Save state for restoration later */
! 	*save_dpns = *dpns;
! 
! 	/* Build a new ancestor list with just this node's ancestors */
! 	ancestors = NIL;
! 	while ((ancestor_cell = lnext(ancestor_cell)) != NULL)
! 		ancestors = lappend(ancestors, lfirst(ancestor_cell));
! 	dpns->ancestors = ancestors;
! 
! 	/* Set attention on selected ancestor */
! 	set_deparse_planstate(dpns, ps);
! }
! 
! /*
!  * pop_ancestor_plan: undo the effects of push_ancestor_plan
!  */
! static void
! pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
! {
! 	/* Free the ancestor list made in push_ancestor_plan */
! 	list_free(dpns->ancestors);
! 
! 	/* Restore fields changed by push_ancestor_plan */
! 	*dpns = *save_dpns;
  }
  
+ 
  /* ----------
   * make_ruledef			- reconstruct the CREATE RULE command
   *				  for a given pg_rewrite tuple
***************
*** 2285,2294 ****
  		context.varprefix = (list_length(query->rtable) != 1);
  		context.prettyFlags = prettyFlags;
  		context.indentLevel = PRETTYINDENT_STD;
  		dpns.rtable = query->rtable;
  		dpns.ctes = query->cteList;
- 		dpns.subplans = NIL;
- 		dpns.outer_plan = dpns.inner_plan = NULL;
  
  		get_rule_expr(qual, &context, false);
  	}
--- 2430,2439 ----
  		context.varprefix = (list_length(query->rtable) != 1);
  		context.prettyFlags = prettyFlags;
  		context.indentLevel = PRETTYINDENT_STD;
+ 
+ 		memset(&dpns, 0, sizeof(dpns));
  		dpns.rtable = query->rtable;
  		dpns.ctes = query->cteList;
  
  		get_rule_expr(qual, &context, false);
  	}
***************
*** 2432,2441 ****
  	context.prettyFlags = prettyFlags;
  	context.indentLevel = startIndent;
  
  	dpns.rtable = query->rtable;
  	dpns.ctes = query->cteList;
- 	dpns.subplans = NIL;
- 	dpns.outer_plan = dpns.inner_plan = NULL;
  
  	switch (query->commandType)
  	{
--- 2577,2585 ----
  	context.prettyFlags = prettyFlags;
  	context.indentLevel = startIndent;
  
+ 	memset(&dpns, 0, sizeof(dpns));
  	dpns.rtable = query->rtable;
  	dpns.ctes = query->cteList;
  
  	switch (query->commandType)
  	{
***************
*** 3481,3537 ****
  
  
  /*
-  * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
-  *
-  * When expanding an OUTER or INNER reference, we must push new outer/inner
-  * subplans in case the referenced expression itself uses OUTER/INNER.	We
-  * modify the top stack entry in-place to avoid affecting levelsup issues
-  * (although in a Plan tree there really shouldn't be any).
-  *
-  * Caller must save and restore outer_plan and inner_plan around this.
-  *
-  * We also use this to initialize the fields during deparse_context_for_plan.
-  */
- static void
- push_plan(deparse_namespace *dpns, Plan *subplan)
- {
- 	/*
- 	 * We special-case Append to pretend that the first child plan is the
- 	 * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
- 	 * according to one of the children, and the first one is the most natural
- 	 * choice.	Likewise special-case ModifyTable to pretend that the first
- 	 * child plan is the OUTER referent; this is to support RETURNING lists
- 	 * containing references to non-target relations.
- 	 */
- 	if (IsA(subplan, Append))
- 		dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
- 	else if (IsA(subplan, ModifyTable))
- 		dpns->outer_plan = (Plan *) linitial(((ModifyTable *) subplan)->plans);
- 	else
- 		dpns->outer_plan = outerPlan(subplan);
- 
- 	/*
- 	 * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
- 	 * use OUTER because that could someday conflict with the normal meaning.)
- 	 * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
- 	 */
- 	if (IsA(subplan, SubqueryScan))
- 		dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
- 	else if (IsA(subplan, CteScan))
- 	{
- 		int			ctePlanId = ((CteScan *) subplan)->ctePlanId;
- 
- 		if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
- 			dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
- 		else
- 			dpns->inner_plan = NULL;
- 	}
- 	else
- 		dpns->inner_plan = innerPlan(subplan);
- }
- 
- 
- /*
   * Display a Var appropriately.
   *
   * In some cases (currently only when recursing into an unnamed join)
--- 3625,3630 ----
***************
*** 3576,3592 ****
  	else if (var->varno == OUTER && dpns->outer_plan)
  	{
  		TargetEntry *tle;
! 		Plan	   *save_outer;
! 		Plan	   *save_inner;
  
  		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		save_outer = dpns->outer_plan;
! 		save_inner = dpns->inner_plan;
! 		push_plan(dpns, dpns->outer_plan);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
--- 3669,3682 ----
  	else if (var->varno == OUTER && dpns->outer_plan)
  	{
  		TargetEntry *tle;
! 		deparse_namespace save_dpns;
  
  		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
***************
*** 3598,3621 ****
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		dpns->outer_plan = save_outer;
! 		dpns->inner_plan = save_inner;
  		return NULL;
  	}
  	else if (var->varno == INNER && dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		Plan	   *save_outer;
! 		Plan	   *save_inner;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		save_outer = dpns->outer_plan;
! 		save_inner = dpns->inner_plan;
! 		push_plan(dpns, dpns->inner_plan);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
--- 3688,3707 ----
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		pop_child_plan(dpns, &save_dpns);
  		return NULL;
  	}
  	else if (var->varno == INNER && dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		deparse_namespace save_dpns;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
***************
*** 3627,3634 ****
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		dpns->outer_plan = save_outer;
! 		dpns->inner_plan = save_inner;
  		return NULL;
  	}
  	else
--- 3713,3719 ----
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		pop_child_plan(dpns, &save_dpns);
  		return NULL;
  	}
  	else
***************
*** 3653,3669 ****
  		dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		Plan	   *save_outer;
! 		Plan	   *save_inner;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		save_outer = dpns->outer_plan;
! 		save_inner = dpns->inner_plan;
! 		push_plan(dpns, dpns->inner_plan);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
--- 3738,3751 ----
  		dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		deparse_namespace save_dpns;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
  		if (!tle)
  			elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
  
  		/*
  		 * Force parentheses because our caller probably assumed a Var is a
***************
*** 3675,3682 ****
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		dpns->outer_plan = save_outer;
! 		dpns->inner_plan = save_inner;
  		return NULL;
  	}
  
--- 3757,3763 ----
  		if (!IsA(tle->expr, Var))
  			appendStringInfoChar(buf, ')');
  
! 		pop_child_plan(dpns, &save_dpns);
  		return NULL;
  	}
  
***************
*** 3827,3834 ****
  	else if (var->varno == OUTER && dpns->outer_plan)
  	{
  		TargetEntry *tle;
! 		Plan	   *save_outer;
! 		Plan	   *save_inner;
  		const char *result;
  
  		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
--- 3908,3914 ----
  	else if (var->varno == OUTER && dpns->outer_plan)
  	{
  		TargetEntry *tle;
! 		deparse_namespace save_dpns;
  		const char *result;
  
  		tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
***************
*** 3836,3857 ****
  			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		save_outer = dpns->outer_plan;
! 		save_inner = dpns->inner_plan;
! 		push_plan(dpns, dpns->outer_plan);
  
  		result = get_name_for_var_field((Var *) tle->expr, fieldno,
  										levelsup, context);
  
! 		dpns->outer_plan = save_outer;
! 		dpns->inner_plan = save_inner;
  		return result;
  	}
  	else if (var->varno == INNER && dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		Plan	   *save_outer;
! 		Plan	   *save_inner;
  		const char *result;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
--- 3916,3933 ----
  			elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
  
  		result = get_name_for_var_field((Var *) tle->expr, fieldno,
  										levelsup, context);
  
! 		pop_child_plan(dpns, &save_dpns);
  		return result;
  	}
  	else if (var->varno == INNER && dpns->inner_plan)
  	{
  		TargetEntry *tle;
! 		deparse_namespace save_dpns;
  		const char *result;
  
  		tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
***************
*** 3859,3873 ****
  			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		save_outer = dpns->outer_plan;
! 		save_inner = dpns->inner_plan;
! 		push_plan(dpns, dpns->inner_plan);
  
  		result = get_name_for_var_field((Var *) tle->expr, fieldno,
  										levelsup, context);
  
! 		dpns->outer_plan = save_outer;
! 		dpns->inner_plan = save_inner;
  		return result;
  	}
  	else
--- 3935,3946 ----
  			elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
  
  		Assert(netlevelsup == 0);
! 		push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
  
  		result = get_name_for_var_field((Var *) tle->expr, fieldno,
  										levelsup, context);
  
! 		pop_child_plan(dpns, &save_dpns);
  		return result;
  	}
  	else
***************
*** 3926,3935 ****
  						deparse_namespace mydpns;
  						const char *result;
  
  						mydpns.rtable = rte->subquery->rtable;
  						mydpns.ctes = rte->subquery->cteList;
- 						mydpns.subplans = NIL;
- 						mydpns.outer_plan = mydpns.inner_plan = NULL;
  
  						context->namespaces = lcons(&mydpns,
  													context->namespaces);
--- 3999,4007 ----
  						deparse_namespace mydpns;
  						const char *result;
  
+ 						memset(&mydpns, 0, sizeof(mydpns));
  						mydpns.rtable = rte->subquery->rtable;
  						mydpns.ctes = rte->subquery->cteList;
  
  						context->namespaces = lcons(&mydpns,
  													context->namespaces);
***************
*** 3954,3961 ****
  					 * look into the child plan's tlist instead.
  					 */
  					TargetEntry *tle;
! 					Plan	   *save_outer;
! 					Plan	   *save_inner;
  					const char *result;
  
  					if (!dpns->inner_plan)
--- 4026,4032 ----
  					 * look into the child plan's tlist instead.
  					 */
  					TargetEntry *tle;
! 					deparse_namespace save_dpns;
  					const char *result;
  
  					if (!dpns->inner_plan)
***************
*** 3967,3981 ****
  						elog(ERROR, "bogus varattno for subquery var: %d",
  							 attnum);
  					Assert(netlevelsup == 0);
! 					save_outer = dpns->outer_plan;
! 					save_inner = dpns->inner_plan;
! 					push_plan(dpns, dpns->inner_plan);
  
  					result = get_name_for_var_field((Var *) tle->expr, fieldno,
  													levelsup, context);
  
! 					dpns->outer_plan = save_outer;
! 					dpns->inner_plan = save_inner;
  					return result;
  				}
  			}
--- 4038,4049 ----
  						elog(ERROR, "bogus varattno for subquery var: %d",
  							 attnum);
  					Assert(netlevelsup == 0);
! 					push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
  
  					result = get_name_for_var_field((Var *) tle->expr, fieldno,
  													levelsup, context);
  
! 					pop_child_plan(dpns, &save_dpns);
  					return result;
  				}
  			}
***************
*** 4049,4058 ****
  						deparse_namespace mydpns;
  						const char *result;
  
  						mydpns.rtable = ctequery->rtable;
  						mydpns.ctes = ctequery->cteList;
- 						mydpns.subplans = NIL;
- 						mydpns.outer_plan = mydpns.inner_plan = NULL;
  
  						new_nslist = list_copy_tail(context->namespaces,
  													ctelevelsup);
--- 4117,4125 ----
  						deparse_namespace mydpns;
  						const char *result;
  
+ 						memset(&mydpns, 0, sizeof(mydpns));
  						mydpns.rtable = ctequery->rtable;
  						mydpns.ctes = ctequery->cteList;
  
  						new_nslist = list_copy_tail(context->namespaces,
  													ctelevelsup);
***************
*** 4076,4083 ****
  					 * can look into the subplan's tlist instead.
  					 */
  					TargetEntry *tle;
! 					Plan	   *save_outer;
! 					Plan	   *save_inner;
  					const char *result;
  
  					if (!dpns->inner_plan)
--- 4143,4149 ----
  					 * can look into the subplan's tlist instead.
  					 */
  					TargetEntry *tle;
! 					deparse_namespace save_dpns;
  					const char *result;
  
  					if (!dpns->inner_plan)
***************
*** 4089,4103 ****
  						elog(ERROR, "bogus varattno for subquery var: %d",
  							 attnum);
  					Assert(netlevelsup == 0);
! 					save_outer = dpns->outer_plan;
! 					save_inner = dpns->inner_plan;
! 					push_plan(dpns, dpns->inner_plan);
  
  					result = get_name_for_var_field((Var *) tle->expr, fieldno,
  													levelsup, context);
  
! 					dpns->outer_plan = save_outer;
! 					dpns->inner_plan = save_inner;
  					return result;
  				}
  			}
--- 4155,4166 ----
  						elog(ERROR, "bogus varattno for subquery var: %d",
  							 attnum);
  					Assert(netlevelsup == 0);
! 					push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
  
  					result = get_name_for_var_field((Var *) tle->expr, fieldno,
  													levelsup, context);
  
! 					pop_child_plan(dpns, &save_dpns);
  					return result;
  				}
  			}
***************
*** 4160,4165 ****
--- 4223,4387 ----
  	return result;
  }
  
+ /*
+  * Display a Param appropriately.
+  */
+ static void
+ get_parameter(Param *param, deparse_context *context)
+ {
+ 	/*
+ 	 * If it's a PARAM_EXEC parameter, try to locate the expression from
+ 	 * which the parameter was computed.  This will necessarily be in some
+ 	 * ancestor of the current expression's PlanState.  Note that failing
+ 	 * to find a referent isn't an error, since the Param might well be a
+ 	 * subplan output rather than an input.
+ 	 */
+ 	if (param->paramkind == PARAM_EXEC)
+ 	{
+ 		deparse_namespace *dpns;
+ 		PlanState  *child_ps;
+ 		bool		in_same_plan_level;
+ 		ListCell   *lc;
+ 
+ 		dpns = (deparse_namespace *) linitial(context->namespaces);
+ 		child_ps = dpns->planstate;
+ 		in_same_plan_level = true;
+ 
+ 		foreach(lc, dpns->ancestors)
+ 		{
+ 			PlanState  *ps = (PlanState *) lfirst(lc);
+ 			ListCell   *lc2;
+ 
+ 			/*
+ 			 * NestLoops transmit params to their inner child only; also,
+ 			 * once we've crawled up out of a subplan, this couldn't
+ 			 * possibly be the right match.
+ 			 */
+ 			if (IsA(ps, NestLoopState) &&
+ 				child_ps == innerPlanState(ps) &&
+ 				in_same_plan_level)
+ 			{
+ 				NestLoop   *nl = (NestLoop *) ps->plan;
+ 
+ 				foreach(lc2, nl->nestParams)
+ 				{
+ 					NestLoopParam  *nlp = (NestLoopParam *) lfirst(lc2);
+ 
+ 					if (nlp->paramno == param->paramid)
+ 					{
+ 						/* Found a match, so print it */
+ 						print_parameter_expr((Node *) nlp->paramval, lc,
+ 											 dpns, context);
+ 						return;
+ 					}
+ 				}
+ 			}
+ 
+ 			/*
+ 			 * Check to see if we're crawling up from an initPlan.
+ 			 */
+ 			foreach(lc2, ps->initPlan)
+ 			{
+ 				SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+ 				SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+ 				ListCell   *lc3;
+ 				ListCell   *lc4;
+ 
+ 				if (child_ps != sstate->planstate)
+ 					continue;
+ 
+ 				/* Matched initPlan, so check its arguments */
+ 				forboth(lc3, subplan->parParam, lc4, subplan->args)
+ 				{
+ 					int		paramid = lfirst_int(lc3);
+ 					Node   *arg = (Node *) lfirst(lc4);
+ 
+ 					if (paramid == param->paramid)
+ 					{
+ 						/* Found a match, so print it */
+ 						print_parameter_expr(arg, lc, dpns, context);
+ 						return;
+ 					}
+ 				}
+ 
+ 				/* Keep looking, but we are emerging from an initPlan. */
+ 				in_same_plan_level = false;
+ 				break;
+ 			}
+ 
+ 			/*
+ 			 * Likewise for subPlans.
+ 			 */
+ 			foreach(lc2, ps->subPlan)
+ 			{
+ 				SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+ 				SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+ 				ListCell   *lc3;
+ 				ListCell   *lc4;
+ 
+ 				if (child_ps != sstate->planstate)
+ 					continue;
+ 
+ 				/* Matched subPlan, so check its arguments */
+ 				forboth(lc3, subplan->parParam, lc4, subplan->args)
+ 				{
+ 					int		paramid = lfirst_int(lc3);
+ 					Node   *arg = (Node *) lfirst(lc4);
+ 
+ 					if (paramid == param->paramid)
+ 					{
+ 						/* Found a match, so print it */
+ 						print_parameter_expr(arg, lc, dpns, context);
+ 						return;
+ 					}
+ 				}
+ 
+ 				/* Keep looking, but we are emerging from a subPlan. */
+ 				in_same_plan_level = false;
+ 				break;
+ 			}
+ 
+ 			/* No luck, crawl up to next ancestor */
+ 			child_ps = ps;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Not PARAM_EXEC, or couldn't find referent: just print $N.
+ 	 */
+ 	appendStringInfo(context->buf, "$%d", param->paramid);
+ }
+ 
+ /* Print a parameter reference expression found by get_parameter */
+ static void
+ print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+ 					 deparse_namespace *dpns, deparse_context *context)
+ {
+ 	deparse_namespace save_dpns;
+ 	bool		save_varprefix;
+ 
+ 	/* Switch attention to the ancestor plan node */
+ 	push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+ 
+ 	/*
+ 	 * Force prefixing of Vars, since they won't belong to the relation being
+ 	 * scanned in the original plan node.
+ 	 */
+ 	save_varprefix = context->varprefix;
+ 	context->varprefix = true;
+ 
+ 	/*
+ 	 * We don't need to add parentheses because a Param's expansion is
+ 	 * (currently) always a Var or Aggref.
+ 	 */
+ 	Assert(IsA(expr, Var) || IsA(expr, Aggref));
+ 
+ 	get_rule_expr(expr, context, false);
+ 
+ 	context->varprefix = save_varprefix;
+ 
+ 	pop_ancestor_plan(dpns, &save_dpns);
+ }
  
  /*
   * get_simple_binary_op_name
***************
*** 4496,4502 ****
  			break;
  
  		case T_Param:
! 			appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
  			break;
  
  		case T_Aggref:
--- 4718,4724 ----
  			break;
  
  		case T_Param:
! 			get_parameter((Param *) node, context);
  			break;
  
  		case T_Aggref:
Index: src/include/utils/builtins.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.350
diff -c -r1.350 builtins.h
*** src/include/utils/builtins.h	6 Jul 2010 19:19:00 -0000	1.350
--- src/include/utils/builtins.h	13 Jul 2010 18:13:25 -0000
***************
*** 609,616 ****
  extern char *deparse_expression(Node *expr, List *dpcontext,
  				   bool forceprefix, bool showimplicit);
  extern List *deparse_context_for(const char *aliasname, Oid relid);
! extern List *deparse_context_for_plan(Node *plan, Node *outer_plan,
! 						 List *rtable, List *subplans);
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *qualifier,
  						   const char *ident);
--- 609,616 ----
  extern char *deparse_expression(Node *expr, List *dpcontext,
  				   bool forceprefix, bool showimplicit);
  extern List *deparse_context_for(const char *aliasname, Oid relid);
! extern List *deparse_context_for_planstate(Node *planstate, List *ancestors,
! 							  List *rtable);
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *qualifier,
  						   const char *ident);