2011/7/2 Hitoshi Harada <umi.tan...@gmail.com>:
> 2011/6/29 Yeb Havinga <yebhavi...@gmail.com>:
>>
>> On 2011-06-17 09:54, Hitoshi Harada wrote:
>>>
>>> While reviewing the gist/box patch, I found some planner APIs that can
>>> replace parts in my patch. Also, comments in includes wasn't updated
>>> appropriately. Revised patch attached.
>>
>> Hello Hitoshi-san,
>>
>> I read your latest patch implementing parameterizing subquery scans.
>
> Attached is revised version.

I failed to attached the patch. I'm trying again.

>> 1)
>> In the email from june 9 with the patch You wrote: "While IndexScan
>> is simple since its information like costs are well known by the base
>> relation, SubqueryScan should re-plan its Query to gain that, which is
>> expensive."
>>
>> Initial concerns I had were caused by misinterpreting 'replanning' as: for
>> each outer tuple, replan the subquery (it sounds a bit like 'ReScan').
>> Though the general comments in the patch are helpful, I think it would be an
>> improvement to describe why subqueries are planned more than once, i.e.
>> something like
>> "best_inner_subqueryscan
>>   Try to find a better subqueryscan path and its associated plan for each
>> join clause that can be pushed down, in addition to the path that is already
>> calculated by set_subquery_pathlist."
>
> I changed comments around set_subquery_pathlist and best_inner_subqueryscan.
>
>> 2)
>> Since 'subquery_is_pushdown_safe' is invariant under join clause push down,
>> it might be possible to have it called only once in set_subquery_pathlist,
>> i.e. by only attaching rel->preprocessed_subquery if the
>> subquery_is_pushdown_safe.
>
> I modified as you suggested.
>
>> 3)
>> /*
>>  * set_subquery_pathlist
>>  *        Build the (single) access path for a subquery RTE
>>  */
>> This unchanged comment is still correct, but 'the (single) access path'
>> might have become a bit misleading now there are multiple paths possible,
>> though not by set_subquery_pathlist.
>
> As noted #1.
>
>> 4) somewhere in the patch
>> s/regsitered/registered/
>
> Fixed.
>
>> 5) Regression tests are missing; I think at this point they'd aid in
>> speeding up development/test cycles.
>
> I'm still thinking about it. I can add complex test but the concept of
> regression test focuses on small pieces of simple cases. I don't want
> take pg_regress much more than before.
>
>> 6) Before patching Postgres, I could execute the test with the size_l and
>> size_m tables, after patching against current git HEAD (patch without
>> errors), I get the following error when running the example query:
>> ERROR:  plan should not reference subplan's variable
>>
>> I get the same error with the version from june 9 with current git HEAD.
>
> Fixed. Some variable initializing was wrong.
>
>> 7) Since both set_subquery_pathlist and best_inner_subqueryscan push down
>> clauses, as well as add a path and subplan, could a generalized function be
>> made to support both, to reduce duplicate code?
>
> No touch as answered before.
>
> Although I still need to think about suitable regression test case,
> the patch itself can be reviewed again. You may want to try some
> additional tests as you imagine after finding my test case gets
> quicker.
>
> Thanks,
>
> --
> Hitoshi Harada
>



-- 
Hitoshi Harada
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 681f5f8..039fd7f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1557,6 +1557,17 @@ _outTidPath(StringInfo str, TidPath *node)
 }
 
 static void
+_outSubqueryPath(StringInfo str, SubqueryPath *node)
+{
+	WRITE_NODE_TYPE("SUBQUERYPATH");
+
+	_outPathInfo(str, (Path *) node);
+	WRITE_NODE_FIELD(subplan);
+	WRITE_NODE_FIELD(subrtable);
+	WRITE_NODE_FIELD(subrowmark);
+}
+
+static void
 _outForeignPath(StringInfo str, ForeignPath *node)
 {
 	WRITE_NODE_TYPE("FOREIGNPATH");
@@ -1738,9 +1749,6 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
 	WRITE_NODE_FIELD(indexlist);
 	WRITE_UINT_FIELD(pages);
 	WRITE_FLOAT_FIELD(tuples, "%.0f");
-	WRITE_NODE_FIELD(subplan);
-	WRITE_NODE_FIELD(subrtable);
-	WRITE_NODE_FIELD(subrowmark);
 	WRITE_NODE_FIELD(baserestrictinfo);
 	WRITE_NODE_FIELD(joininfo);
 	WRITE_BOOL_FIELD(has_eclass_joins);
@@ -2948,6 +2956,9 @@ _outNode(StringInfo str, void *obj)
 			case T_TidPath:
 				_outTidPath(str, obj);
 				break;
+			case T_SubqueryPath:
+				_outSubqueryPath(str, obj);
+				break;
 			case T_ForeignPath:
 				_outForeignPath(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 47ab08e..354a3e5 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -31,6 +31,7 @@
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/subselect.h"
 #include "optimizer/var.h"
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
@@ -677,6 +678,12 @@ has_multiple_baserels(PlannerInfo *root)
 /*
  * set_subquery_pathlist
  *		Build the (single) access path for a subquery RTE
+ *
+ * Although we build only one access path for the subquery,
+ * join search process may find another path by pushing down
+ * the nestloop param into subquery. The finding process will
+ * be done much later than here, but some common operation like
+ * preprocessing subquery are shared.
  */
 static void
 set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -687,7 +694,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	bool	   *differentTypes;
 	double		tuple_fraction;
 	PlannerInfo *subroot;
-	List	   *pathkeys;
+	Plan	    *subplan;
+	List	    *pathkeys;
+	bool		issafe = false;
 
 	/*
 	 * Must copy the Query so that planning doesn't mess up the RTE contents
@@ -721,7 +730,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	 * push down a pushable qual, because it'd result in a worse plan?
 	 */
 	if (rel->baserestrictinfo != NIL &&
-		subquery_is_pushdown_safe(subquery, subquery, differentTypes))
+		(issafe = subquery_is_pushdown_safe(subquery, subquery, differentTypes)))
 	{
 		/* OK to consider pushing down individual quals */
 		List	   *upperrestrictlist = NIL;
@@ -749,6 +758,10 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 
 	pfree(differentTypes);
 
+	/* save it for later use in best_inner_subqueryscan */
+	if (issafe)
+		rel->preprocessed_subquery = copyObject(subquery);
+
 	/*
 	 * We can safely pass the outer tuple_fraction down to the subquery if the
 	 * outer level has no joining, aggregation, or sorting to do. Otherwise
@@ -766,27 +779,187 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 		tuple_fraction = root->tuple_fraction;
 
 	/* Generate the plan for the subquery */
-	rel->subplan = subquery_planner(root->glob, subquery,
-									root,
-									false, tuple_fraction,
-									&subroot);
-	rel->subrtable = subroot->parse->rtable;
-	rel->subrowmark = subroot->rowMarks;
+	subplan = subquery_planner(root->glob, subquery,
+							   root,
+							   false, tuple_fraction,
+							   &subroot);
 
 	/* Mark rel with estimated output rows, width, etc */
-	set_subquery_size_estimates(root, rel, subroot);
+	set_subquery_size_estimates(root, rel, subroot, subplan);
 
 	/* Convert subquery pathkeys to outer representation */
-	pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
+	pathkeys = convert_subquery_pathkeys(root, rel,
+										 subroot->query_pathkeys, subplan);
 
 	/* Generate appropriate path */
-	add_path(rel, create_subqueryscan_path(rel, pathkeys));
+	add_path(rel, create_subqueryscan_path(rel, pathkeys,
+			 subplan, subroot->parse->rtable, subroot->rowMarks));
 
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
 
 /*
+ * best_inner_subqueryscan
+ *
+ * As the inner scan relation, try to find another subquer path which may
+ * be better by pushing down some join clauses. If such possibility is found,
+ * return the path in *cheapest_start_path or *cheapest_total_path.
+ * Currently both paths will be resulted in the same, whereas further
+ * improvement might find cases they'd be different.
+ *
+ * It re-uses modified subquery (a.k.a. rel->preprocessed_subquery.) The basic
+ * push-down of baserestrictinfo was common among base path (done in
+ * set_subquery_pathlist()) so this routine doesn't care it. But once we find
+ * push-down-able join clause, the parsed subquery will be modified after copied.
+ */
+void
+best_inner_subqueryscan(PlannerInfo *root, RelOptInfo *rel,
+						RelOptInfo *outer_rel, List *join_clause,
+						Path **cheapest_start_path, Path **cheapest_total_path)
+{
+	Query	   *parse = root->parse;
+	Query	   *subquery;
+	Index		rti = rel->relid;
+	RangeTblEntry *rte = planner_rt_fetch(rti, root);
+	bool	   *differentTypes;
+	double		tuple_fraction;
+	PlannerInfo *subroot;
+	Plan	   *subplan;
+	List	   *pathkeys;
+	Path	   *path;
+	bool		query_modified = false;
+
+	/* initialize paths to return immediately anytime */
+	*cheapest_start_path = *cheapest_total_path = NULL;
+
+	/*
+	 * empty join condition doesn't make sense here
+	 */
+	if (join_clause == NIL)
+		return;
+
+	/*
+	 * If the subquery is not safe for pushdown, preprocessed
+	 * subquery was not saved. Simply skip this process.
+	 */
+	if (!rel->preprocessed_subquery)
+		return;
+
+	/* copying the whole Query is expensive; let's copy-on-write */
+	subquery = rel->preprocessed_subquery;
+
+	/* We need a workspace for keeping track of set-op type coercions */
+	differentTypes = (bool *)
+		palloc0((list_length(subquery->targetList) + 1) * sizeof(bool));
+
+	/*
+	 * Currently we focus on only cases of aggregate subquery.
+	 * I'm not quite sure if there are other useful cases.
+	 */
+	if (subquery->hasAggs)
+	{
+		ListCell   *l;
+
+		foreach (l, join_clause)
+		{
+			RestrictInfo   *rinfo = (RestrictInfo *) lfirst(l);
+			Node		   *lexpr, *rexpr;
+			Expr		   *clause = NULL;
+			Param		   *param;
+			OpExpr		   *rclause;
+
+			/*
+			 * For the first step, only Var = Var join clause is targeted.
+			 * This may be improved in the future.
+			 */
+			if (!is_opclause(rinfo->clause))
+				continue;
+			rclause = (OpExpr *) rinfo->clause;
+			lexpr = get_leftop((Expr *) rclause);
+			rexpr = get_rightop((Expr *) rclause);
+			if (!(IsA(lexpr, Var) && IsA(rexpr, Var)))
+				continue;
+
+			/* see which arg is mine and outer's */
+			if (bms_is_member(((Var *) lexpr)->varno, outer_rel->relids))
+			{
+				param = assign_nestloop_param(root, (Var *) lexpr);
+				clause = make_opclause(rclause->opno,
+									   rclause->opresulttype,
+									   false,
+									   (Expr *) param,
+									   (Expr *) copyObject(rexpr),
+									   InvalidOid,
+									   rclause->opcollid);
+			}
+			else if (bms_is_member(((Var *) rexpr)->varno, outer_rel->relids))
+			{
+				param = assign_nestloop_param(root, (Var *) rexpr);
+				clause = make_opclause(rclause->opno,
+									   rclause->opresulttype,
+									   false,
+									   (Expr *) copyObject(lexpr),
+									   (Expr *) param,
+									   InvalidOid,
+									   rclause->opcollid);
+			}
+
+			/* TODO:no check isouter or not? */
+			if (clause &&
+				qual_is_pushdown_safe(subquery, rti, (Node *) clause,
+									  differentTypes))
+			{
+				/* copy on write */
+				if (rel->preprocessed_subquery == subquery)
+					subquery = copyObject(subquery);
+				subquery_push_qual(subquery, rte, rti, (Node *) clause);
+				query_modified = true;
+			}
+		}
+	}
+
+	pfree(differentTypes);
+
+	/* return immediately if such subquery isn't found. Use original one */
+	if (!query_modified)
+		return;
+
+	/*
+	 * We can safely pass the outer tuple_fraction down to the subquery if the
+	 * outer level has no joining, aggregation, or sorting to do. Otherwise
+	 * we'd better tell the subquery to plan for full retrieval. (XXX This
+	 * could probably be made more intelligent ...)
+	 */
+	if (parse->hasAggs ||
+		parse->groupClause ||
+		parse->havingQual ||
+		parse->distinctClause ||
+		parse->sortClause ||
+		has_multiple_baserels(root))
+		tuple_fraction = 0.0;	/* default case */
+	else
+		tuple_fraction = root->tuple_fraction;
+
+	/* Generate the plan for the subquery */
+	subplan = subquery_planner(root->glob, subquery,
+							   root,
+							   false, tuple_fraction,
+							   &subroot);
+
+	/* Convert subquery pathkeys to outer representation */
+	pathkeys = convert_subquery_pathkeys(root, rel,
+										 subroot->query_pathkeys, subplan);
+
+	/* Generate appropriate path */
+	path = create_subqueryscan_path(rel, pathkeys,
+			 subplan, subroot->parse->rtable, subroot->rowMarks);
+
+	/* return the same path for now */
+	*cheapest_start_path = *cheapest_total_path = path;
+}
+
+/*
  * set_function_pathlist
  *		Build the (single) access path for a function RTE
  */
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index bb38768..b7271c3 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -892,7 +892,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
  *	  Determines and returns the cost of scanning a subquery RTE.
  */
 void
-cost_subqueryscan(Path *path, RelOptInfo *baserel)
+cost_subqueryscan(SubqueryPath *path, RelOptInfo *baserel)
 {
 	Cost		startup_cost;
 	Cost		run_cost;
@@ -907,15 +907,15 @@ cost_subqueryscan(Path *path, RelOptInfo *baserel)
 	 * any restriction clauses that will be attached to the SubqueryScan node,
 	 * plus cpu_tuple_cost to account for selection and projection overhead.
 	 */
-	path->startup_cost = baserel->subplan->startup_cost;
-	path->total_cost = baserel->subplan->total_cost;
+	path->path.startup_cost = path->subplan->startup_cost;
+	path->path.total_cost = path->subplan->total_cost;
 
 	startup_cost = baserel->baserestrictcost.startup;
 	cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
 	run_cost = cpu_per_tuple * baserel->tuples;
 
-	path->startup_cost += startup_cost;
-	path->total_cost += startup_cost + run_cost;
+	path->path.startup_cost += startup_cost;
+	path->path.total_cost += startup_cost + run_cost;
 }
 
 /*
@@ -3222,7 +3222,7 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
  */
 void
 set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
-							PlannerInfo *subroot)
+							PlannerInfo *subroot, Plan *subplan)
 {
 	RangeTblEntry *rte;
 	ListCell   *lc;
@@ -3233,7 +3233,7 @@ set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
 	Assert(rte->rtekind == RTE_SUBQUERY);
 
 	/* Copy raw number of output rows from subplan */
-	rel->tuples = rel->subplan->plan_rows;
+	rel->tuples = subplan->plan_rows;
 
 	/*
 	 * Compute per-output-column width estimates by examining the subquery's
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 7d3cf42..c79fd43 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -329,7 +329,9 @@ sort_inner_and_outer(PlannerInfo *root,
  * inner path, one on the same with materialization, one on the
  * cheapest-startup-cost inner path (if different), one on the
  * cheapest-total inner-indexscan path (if any), and one on the
- * cheapest-startup inner-indexscan path (if different).
+ * cheapest-startup inner-indexscan path (if different) or on the
+ * cheapest-total inner-subqueryscan path (if any).
+ * Note inner-indexpaths and inner-subquerypaths are not made at the same time.
  *
  * We also consider mergejoins if mergejoin clauses are available.	We have
  * two ways to generate the inner path for a mergejoin: sort the cheapest
@@ -369,6 +371,8 @@ match_unsorted_outer(PlannerInfo *root,
 	Path	   *matpath = NULL;
 	Path	   *index_cheapest_startup = NULL;
 	Path	   *index_cheapest_total = NULL;
+	Path	   *sub_cheapest_startup = NULL;
+	Path	   *sub_cheapest_total = NULL;
 	ListCell   *l;
 
 	/*
@@ -430,8 +434,8 @@ match_unsorted_outer(PlannerInfo *root,
 				create_material_path(innerrel, inner_cheapest_total);
 
 		/*
-		 * Get the best innerjoin indexpaths (if any) for this outer rel.
-		 * They're the same for all outer paths.
+		 * Get the best innerjoin indexpaths or subquerypaths (if any)
+		 * for this outer rel. They're the same for all outer paths.
 		 */
 		if (innerrel->reloptkind != RELOPT_JOINREL)
 		{
@@ -444,6 +448,10 @@ match_unsorted_outer(PlannerInfo *root,
 				best_inner_indexscan(root, innerrel, outerrel, jointype,
 									 &index_cheapest_startup,
 									 &index_cheapest_total);
+			else if (innerrel->rtekind == RTE_SUBQUERY)
+				best_inner_subqueryscan(root, innerrel, outerrel, restrictlist,
+										&sub_cheapest_startup,
+										&sub_cheapest_total);
 		}
 	}
 
@@ -487,7 +495,7 @@ match_unsorted_outer(PlannerInfo *root,
 			 * cheapest-total-cost inner.  When appropriate, also consider
 			 * using the materialized form of the cheapest inner, the
 			 * cheapest-startup-cost inner path, and the cheapest innerjoin
-			 * indexpaths.
+			 * indexpaths or subquerypaths.
 			 */
 			add_path(joinrel, (Path *)
 					 create_nestloop_path(root,
@@ -539,6 +547,16 @@ match_unsorted_outer(PlannerInfo *root,
 											  index_cheapest_startup,
 											  restrictlist,
 											  merge_pathkeys));
+			if (sub_cheapest_total != NULL)
+				add_path(joinrel, (Path *)
+						 create_nestloop_path(root,
+											  joinrel,
+											  jointype,
+											  sjinfo,
+											  outerpath,
+											  sub_cheapest_startup,
+											  restrictlist,
+											  merge_pathkeys));
 		}
 
 		/* Can't do anything else if outer path needs to be unique'd */
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 24e4e59..c3f7ca0 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -745,7 +745,6 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	return joinrel;
 }
 
-
 /*
  * have_join_order_restriction
  *		Detect whether the two relations should be joined to satisfy
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index e5228a8..cee5c71 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -623,12 +623,12 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
  */
 List *
 convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
-						  List *subquery_pathkeys)
+						  List *subquery_pathkeys, Plan *subplan)
 {
 	List	   *retval = NIL;
 	int			retvallen = 0;
 	int			outer_query_keys = list_length(root->query_pathkeys);
-	List	   *sub_tlist = rel->subplan->targetlist;
+	List	   *sub_tlist = subplan->targetlist;
 	ListCell   *i;
 
 	foreach(i, subquery_pathkeys)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e4ccf5c..417c9e3 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -62,8 +62,8 @@ static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
 					  List **qual, List **indexqual);
 static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
 					List *tlist, List *scan_clauses);
-static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
-						 List *tlist, List *scan_clauses);
+static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root,
+					SubqueryPath *best_path, List *tlist, List *scan_clauses);
 static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
 						 List *tlist, List *scan_clauses);
 static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
@@ -321,7 +321,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 
 		case T_SubqueryScan:
 			plan = (Plan *) create_subqueryscan_plan(root,
-													 best_path,
+													 (SubqueryPath *) best_path,
 													 tlist,
 													 scan_clauses);
 			break;
@@ -1538,15 +1538,15 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SubqueryScan *
-create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
+create_subqueryscan_plan(PlannerInfo *root, SubqueryPath *best_path,
 						 List *tlist, List *scan_clauses)
 {
 	SubqueryScan *scan_plan;
-	Index		scan_relid = best_path->parent->relid;
+	Index		scan_relid = best_path->path.parent->relid;
 
 	/* it should be a subquery base rel... */
 	Assert(scan_relid > 0);
-	Assert(best_path->parent->rtekind == RTE_SUBQUERY);
+	Assert(best_path->path.parent->rtekind == RTE_SUBQUERY);
 
 	/* Sort clauses into best execution order */
 	scan_clauses = order_qual_clauses(root, scan_clauses);
@@ -1557,11 +1557,63 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
 	scan_plan = make_subqueryscan(tlist,
 								  scan_clauses,
 								  scan_relid,
-								  best_path->parent->subplan,
-								  best_path->parent->subrtable,
-								  best_path->parent->subrowmark);
+								  best_path->subplan,
+								  best_path->subrtable,
+								  best_path->subrowmark);
 
-	copy_path_costsize(&scan_plan->scan.plan, best_path);
+	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+
+	/*
+	 * If this plan is inner of nestloop and push-down-qual case,
+	 * params are extracted and registered for the upper nestloop.
+	 * As Plan has only bitmap extParam field, we cannot take them
+	 * directly; instead take them from PlannerGlobal.
+	 */
+	if (scan_plan->subplan->extParam)
+	{
+		int		i;
+		ListCell   *ppl;
+		PlannerParamItem   *pitem;
+
+		i = 0;
+		foreach(ppl, root->glob->paramlist)
+		{
+			pitem = (PlannerParamItem *) lfirst(ppl);
+			/* take the ones only in this query level */
+			if (pitem->abslevel == root->query_level &&
+				IsA(pitem->item, Var) &&
+				((Var *) pitem->item)->varlevelsup == 0)
+			{
+				Var	   *var = (Var *) pitem->item;
+				NestLoopParam	   *nlp;
+				ListCell   *lc;
+				bool		found = false;
+
+				/* Is the param already listed in root->curOuterParams? */
+				foreach(lc, root->curOuterParams)
+				{
+					nlp = (NestLoopParam *) lfirst(lc);
+					if (equal(nlp->paramval, var))
+					{
+						/* Present, so we can just skip */
+						found = true;
+						break;
+					}
+				}
+				if (found)
+				{
+					i++;
+					continue;
+				}
+				/* No, so add it */
+				nlp = makeNode(NestLoopParam);
+				nlp->paramno = i;
+				nlp->paramval = var;
+				root->curOuterParams = lappend(root->curOuterParams, nlp);
+			}
+			i++;
+		}
+	}
 
 	return scan_plan;
 }
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 161d5ab..2e711d8 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1331,17 +1331,21 @@ distinct_col_search(int colno, List *colnos, List *opids)
  *	  returning the pathnode.
  */
 Path *
-create_subqueryscan_path(RelOptInfo *rel, List *pathkeys)
+create_subqueryscan_path(RelOptInfo *rel, List *pathkeys, Plan *subplan,
+						 List *subrtable, List *subrowmark)
 {
-	Path	   *pathnode = makeNode(Path);
+	SubqueryPath   *pathnode = makeNode(SubqueryPath);
 
-	pathnode->pathtype = T_SubqueryScan;
-	pathnode->parent = rel;
-	pathnode->pathkeys = pathkeys;
+	pathnode->path.pathtype = T_SubqueryScan;
+	pathnode->path.parent = rel;
+	pathnode->path.pathkeys = pathkeys;
+	pathnode->subplan = subplan;
+	pathnode->subrtable = subrtable;
+	pathnode->subrowmark = subrowmark;
 
 	cost_subqueryscan(pathnode, rel);
 
-	return pathnode;
+	return (Path *) pathnode;
 }
 
 /*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b7a5845..ab2338f 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -82,9 +82,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	rel->indexlist = NIL;
 	rel->pages = 0;
 	rel->tuples = 0;
-	rel->subplan = NULL;
-	rel->subrtable = NIL;
-	rel->subrowmark = NIL;
+	rel->preprocessed_subquery = NULL;
 	rel->baserestrictinfo = NIL;
 	rel->baserestrictcost.startup = 0;
 	rel->baserestrictcost.per_tuple = 0;
@@ -336,9 +334,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->indexlist = NIL;
 	joinrel->pages = 0;
 	joinrel->tuples = 0;
-	joinrel->subplan = NULL;
-	joinrel->subrtable = NIL;
-	joinrel->subrowmark = NIL;
+	joinrel->preprocessed_subquery = NULL;
 	joinrel->baserestrictinfo = NIL;
 	joinrel->baserestrictcost.startup = 0;
 	joinrel->baserestrictcost.per_tuple = 0;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d8bc6b8..e951fd1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -220,6 +220,7 @@ typedef enum NodeTag
 	T_MergePath,
 	T_HashPath,
 	T_TidPath,
+	T_SubqueryPath,
 	T_ForeignPath,
 	T_AppendPath,
 	T_MergeAppendPath,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index f659269..98e2744 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -322,13 +322,6 @@ typedef struct PlannerInfo
  *					(always NIL if it's not a table)
  *		pages - number of disk pages in relation (zero if not a table)
  *		tuples - number of tuples in relation (not considering restrictions)
- *		subplan - plan for subquery (NULL if it's not a subquery)
- *		subrtable - rangetable for subquery (NIL if it's not a subquery)
- *		subrowmark - rowmarks for subquery (NIL if it's not a subquery)
- *
- *		Note: for a subquery, tuples and subplan are not set immediately
- *		upon creation of the RelOptInfo object; they are filled in when
- *		set_base_rel_pathlist processes the object.
  *
  *		For otherrels that are appendrel members, these fields are filled
  *		in just as for a baserel.
@@ -408,9 +401,8 @@ typedef struct RelOptInfo
 	List	   *indexlist;		/* list of IndexOptInfo */
 	BlockNumber pages;
 	double		tuples;
-	struct Plan *subplan;		/* if subquery */
-	List	   *subrtable;		/* if subquery */
-	List	   *subrowmark;		/* if subquery */
+
+	Query	   *preprocessed_subquery; /* if subquery */
 
 	/* used by various scans and joins: */
 	List	   *baserestrictinfo;		/* RestrictInfo structures (if base
@@ -770,6 +762,23 @@ typedef struct TidPath
 } TidPath;
 
 /*
+ * SubqueryPath represents a scan of a subquery scan
+ *
+ * The struct holds subplan, subrtable and subrowmark, which are
+ * the temporary spaces to transport to the final stage of planner.
+ * Note each SubqueryPath may have different plan, depending on
+ * the pushed down qual clauses (as parameterized inner subquery).
+ */
+typedef struct SubqueryPath
+{
+	Path		path;
+	struct Plan *subplan;
+	List	   *subrtable;
+	List	   *subrowmark;
+	double		rows;
+} SubqueryPath;
+
+/*
  * ForeignPath represents a scan of a foreign table
  */
 typedef struct ForeignPath
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 2763863..7ec2c7b 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -75,7 +75,7 @@ extern void cost_bitmap_or_node(BitmapOrPath *path, PlannerInfo *root);
 extern void cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec);
 extern void cost_tidscan(Path *path, PlannerInfo *root,
 			 RelOptInfo *baserel, List *tidquals);
-extern void cost_subqueryscan(Path *path, RelOptInfo *baserel);
+extern void cost_subqueryscan(SubqueryPath *path, RelOptInfo *baserel);
 extern void cost_functionscan(Path *path, PlannerInfo *root,
 				  RelOptInfo *baserel);
 extern void cost_valuesscan(Path *path, PlannerInfo *root,
@@ -122,7 +122,7 @@ extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
 						   SpecialJoinInfo *sjinfo,
 						   List *restrictlist);
 extern void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
-							PlannerInfo *subroot);
+							PlannerInfo *subroot, Plan *subplan);
 extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 1da2131..6de2816 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -14,6 +14,7 @@
 #ifndef PATHNODE_H
 #define PATHNODE_H
 
+#include "nodes/plannodes.h"
 #include "nodes/relation.h"
 
 
@@ -56,7 +57,8 @@ extern ResultPath *create_result_path(List *quals);
 extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
 extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
 				   Path *subpath, SpecialJoinInfo *sjinfo);
-extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
+extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys,
+					Plan *subplan, List *subrtable, List *subrowmark);
 extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
 extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
 extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 7f1353a..54d0b01 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -33,6 +33,9 @@ extern PGDLLIMPORT join_search_hook_type join_search_hook;
 extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
 extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
 					 List *initial_rels);
+extern void best_inner_subqueryscan(PlannerInfo *root, RelOptInfo *rel,
+						RelOptInfo *outer_rel, List *join_clause,
+						Path **cheapest_start_path, Path **cheapest_total_path);
 
 #ifdef OPTIMIZER_DEBUG
 extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
@@ -154,7 +157,7 @@ extern Path *get_cheapest_fractional_path_for_pathkeys(List *paths,
 extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index,
 					 ScanDirection scandir);
 extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
-						  List *subquery_pathkeys);
+						  List *subquery_pathkeys, Plan *subplan);
 extern List *build_join_pathkeys(PlannerInfo *root,
 					RelOptInfo *joinrel,
 					JoinType jointype,
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to