On 2011-07-22 16:17, Hitoshi Harada wrote:
:(
I updated the patch. Could you try attached once more? The "issafe"
switch seems wrong.
Works like a charm :-). However, now there is always a copyObject of a
subquery even when the subquery is not safe for qual pushdown. The
problem with the previous issafe was that it was only assigned for
rel->baserestrictinfo != NIL. If it is assigned before the if statement,
it still works. See attached patch that avoids subquery copy for unsafe
subqueries, and also exits best_inner_subqueryscan before palloc of
differenttypes in case of unsafe queries.
regards,
--
Yeb Havinga
http://www.mgrid.net/
Mastering Medical Data
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
new file mode 100644
index b5be09a..6596a1c
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outTidPath(StringInfo str, TidPath *nod
*** 1557,1562 ****
--- 1557,1573 ----
}
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");
*************** _outRelOptInfo(StringInfo str, RelOptInf
*** 1738,1746 ****
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);
--- 1749,1754 ----
*************** _outNode(StringInfo str, void *obj)
*** 2949,2954 ****
--- 2957,2965 ----
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
new file mode 100644
index 6b43aee..bd3a53f
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
***************
*** 31,36 ****
--- 31,37 ----
#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"
*************** has_multiple_baserels(PlannerInfo *root)
*** 677,682 ****
--- 678,689 ----
/*
* 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,
*************** set_subquery_pathlist(PlannerInfo *root,
*** 687,693 ****
bool *differentTypes;
double tuple_fraction;
PlannerInfo *subroot;
! List *pathkeys;
/*
* Must copy the Query so that planning doesn't mess up the RTE contents
--- 694,702 ----
bool *differentTypes;
double tuple_fraction;
PlannerInfo *subroot;
! Plan *subplan;
! List *pathkeys;
! bool issafe;
/*
* Must copy the Query so that planning doesn't mess up the RTE contents
*************** set_subquery_pathlist(PlannerInfo *root,
*** 720,727 ****
* XXX Are there any cases where we want to make a policy decision not to
* push down a pushable qual, because it'd result in a worse plan?
*/
! if (rel->baserestrictinfo != NIL &&
! subquery_is_pushdown_safe(subquery, subquery, differentTypes))
{
/* OK to consider pushing down individual quals */
List *upperrestrictlist = NIL;
--- 729,738 ----
* XXX Are there any cases where we want to make a policy decision not to
* push down a pushable qual, because it'd result in a worse plan?
*/
!
! issafe = subquery_is_pushdown_safe(subquery, subquery, differentTypes);
!
! if (rel->baserestrictinfo != NIL && issafe)
{
/* OK to consider pushing down individual quals */
List *upperrestrictlist = NIL;
*************** set_subquery_pathlist(PlannerInfo *root,
*** 749,754 ****
--- 760,769 ----
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
*************** set_subquery_pathlist(PlannerInfo *root,
*** 766,792 ****
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;
/* Mark rel with estimated output rows, width, etc */
! set_subquery_size_estimates(root, rel, subroot);
/* Convert subquery pathkeys to outer representation */
! pathkeys = convert_subquery_pathkeys(root, rel, subroot->query_pathkeys);
/* Generate appropriate path */
! add_path(rel, create_subqueryscan_path(rel, pathkeys));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* set_function_pathlist
* Build the (single) access path for a function RTE
*/
--- 781,959 ----
tuple_fraction = root->tuple_fraction;
/* Generate the plan for the subquery */
! 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, subplan);
/* Convert subquery pathkeys to outer representation */
! pathkeys = convert_subquery_pathkeys(root, rel,
! subroot->query_pathkeys, subplan);
/* Generate appropriate path */
! 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;
+
+ /* rel->preprocessed_subquery was only saved for pushdown save subqueries */
+ if ((subquery = rel->preprocessed_subquery) == NULL)
+ return;
+
+ /* 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))
+ {
+ /* copying the whole Query is expensive; let's 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
new file mode 100644
index bb38768..b7271c3
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_tidscan(Path *path, PlannerInfo *ro
*** 892,898 ****
* Determines and returns the cost of scanning a subquery RTE.
*/
void
! cost_subqueryscan(Path *path, RelOptInfo *baserel)
{
Cost startup_cost;
Cost run_cost;
--- 892,898 ----
* Determines and returns the cost of scanning a subquery RTE.
*/
void
! cost_subqueryscan(SubqueryPath *path, RelOptInfo *baserel)
{
Cost startup_cost;
Cost run_cost;
*************** cost_subqueryscan(Path *path, RelOptInfo
*** 907,921 ****
* 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;
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;
}
/*
--- 907,921 ----
* any restriction clauses that will be attached to the SubqueryScan node,
* plus cpu_tuple_cost to account for selection and projection overhead.
*/
! 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->path.startup_cost += startup_cost;
! path->path.total_cost += startup_cost + run_cost;
}
/*
*************** set_joinrel_size_estimates(PlannerInfo *
*** 3222,3228 ****
*/
void
set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
! PlannerInfo *subroot)
{
RangeTblEntry *rte;
ListCell *lc;
--- 3222,3228 ----
*/
void
set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
! PlannerInfo *subroot, Plan *subplan)
{
RangeTblEntry *rte;
ListCell *lc;
*************** set_subquery_size_estimates(PlannerInfo
*** 3233,3239 ****
Assert(rte->rtekind == RTE_SUBQUERY);
/* Copy raw number of output rows from subplan */
! rel->tuples = rel->subplan->plan_rows;
/*
* Compute per-output-column width estimates by examining the subquery's
--- 3233,3239 ----
Assert(rte->rtekind == RTE_SUBQUERY);
/* Copy raw number of output rows from subplan */
! 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
new file mode 100644
index 7d3cf42..c79fd43
*** a/src/backend/optimizer/path/joinpath.c
--- b/src/backend/optimizer/path/joinpath.c
*************** sort_inner_and_outer(PlannerInfo *root,
*** 329,335 ****
* 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).
*
* We also consider mergejoins if mergejoin clauses are available. We have
* two ways to generate the inner path for a mergejoin: sort the cheapest
--- 329,337 ----
* 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) 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
*************** match_unsorted_outer(PlannerInfo *root,
*** 369,374 ****
--- 371,378 ----
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;
/*
*************** match_unsorted_outer(PlannerInfo *root,
*** 430,437 ****
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.
*/
if (innerrel->reloptkind != RELOPT_JOINREL)
{
--- 434,441 ----
create_material_path(innerrel, inner_cheapest_total);
/*
! * 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)
{
*************** match_unsorted_outer(PlannerInfo *root,
*** 444,449 ****
--- 448,457 ----
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);
}
}
*************** match_unsorted_outer(PlannerInfo *root,
*** 487,493 ****
* 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.
*/
add_path(joinrel, (Path *)
create_nestloop_path(root,
--- 495,501 ----
* 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 or subquerypaths.
*/
add_path(joinrel, (Path *)
create_nestloop_path(root,
*************** match_unsorted_outer(PlannerInfo *root,
*** 539,544 ****
--- 547,562 ----
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
new file mode 100644
index 24e4e59..c3f7ca0
*** a/src/backend/optimizer/path/joinrels.c
--- b/src/backend/optimizer/path/joinrels.c
*************** make_join_rel(PlannerInfo *root, RelOptI
*** 745,751 ****
return joinrel;
}
-
/*
* have_join_order_restriction
* Detect whether the two relations should be joined to satisfy
--- 745,750 ----
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
new file mode 100644
index e5228a8..cee5c71
*** a/src/backend/optimizer/path/pathkeys.c
--- b/src/backend/optimizer/path/pathkeys.c
*************** find_indexkey_var(PlannerInfo *root, Rel
*** 623,634 ****
*/
List *
convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
! List *subquery_pathkeys)
{
List *retval = NIL;
int retvallen = 0;
int outer_query_keys = list_length(root->query_pathkeys);
! List *sub_tlist = rel->subplan->targetlist;
ListCell *i;
foreach(i, subquery_pathkeys)
--- 623,634 ----
*/
List *
convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
! List *subquery_pathkeys, Plan *subplan)
{
List *retval = NIL;
int retvallen = 0;
int outer_query_keys = list_length(root->query_pathkeys);
! 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
new file mode 100644
index a3a82ec..f0c1a6c
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** static Plan *create_bitmap_subplan(Plann
*** 62,69 ****
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 FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
--- 62,69 ----
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,
! 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,
*************** create_scan_plan(PlannerInfo *root, Path
*** 321,327 ****
case T_SubqueryScan:
plan = (Plan *) create_subqueryscan_plan(root,
! best_path,
tlist,
scan_clauses);
break;
--- 321,327 ----
case T_SubqueryScan:
plan = (Plan *) create_subqueryscan_plan(root,
! (SubqueryPath *) best_path,
tlist,
scan_clauses);
break;
*************** create_tidscan_plan(PlannerInfo *root, T
*** 1538,1552 ****
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SubqueryScan *
! create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses)
{
SubqueryScan *scan_plan;
! Index scan_relid = best_path->parent->relid;
/* it should be a subquery base rel... */
Assert(scan_relid > 0);
! Assert(best_path->parent->rtekind == RTE_SUBQUERY);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
--- 1538,1552 ----
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SubqueryScan *
! create_subqueryscan_plan(PlannerInfo *root, SubqueryPath *best_path,
List *tlist, List *scan_clauses)
{
SubqueryScan *scan_plan;
! Index scan_relid = best_path->path.parent->relid;
/* it should be a subquery base rel... */
Assert(scan_relid > 0);
! Assert(best_path->path.parent->rtekind == RTE_SUBQUERY);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
*************** create_subqueryscan_plan(PlannerInfo *ro
*** 1557,1567 ****
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
! best_path->parent->subplan,
! best_path->parent->subrtable,
! best_path->parent->subrowmark);
! copy_path_costsize(&scan_plan->scan.plan, best_path);
return scan_plan;
}
--- 1557,1619 ----
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
! best_path->subplan,
! best_path->subrtable,
! best_path->subrowmark);
! 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
new file mode 100644
index 161d5ab..2e711d8
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** distinct_col_search(int colno, List *col
*** 1331,1347 ****
* returning the pathnode.
*/
Path *
! create_subqueryscan_path(RelOptInfo *rel, List *pathkeys)
{
! Path *pathnode = makeNode(Path);
! pathnode->pathtype = T_SubqueryScan;
! pathnode->parent = rel;
! pathnode->pathkeys = pathkeys;
cost_subqueryscan(pathnode, rel);
! return pathnode;
}
/*
--- 1331,1351 ----
* returning the pathnode.
*/
Path *
! create_subqueryscan_path(RelOptInfo *rel, List *pathkeys, Plan *subplan,
! List *subrtable, List *subrowmark)
{
! SubqueryPath *pathnode = makeNode(SubqueryPath);
! 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 (Path *) pathnode;
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
new file mode 100644
index b7a5845..ab2338f
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_simple_rel(PlannerInfo *root, int
*** 82,90 ****
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
! rel->subplan = NULL;
! rel->subrtable = NIL;
! rel->subrowmark = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
--- 82,88 ----
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
! rel->preprocessed_subquery = NULL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
*************** build_join_rel(PlannerInfo *root,
*** 336,344 ****
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
! joinrel->subplan = NULL;
! joinrel->subrtable = NIL;
! joinrel->subrowmark = NIL;
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost.startup = 0;
joinrel->baserestrictcost.per_tuple = 0;
--- 334,340 ----
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
! 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
new file mode 100644
index d8bc6b8..e951fd1
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 220,225 ****
--- 220,226 ----
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
new file mode 100644
index f659269..98e2744
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct PlannerInfo
*** 322,334 ****
* (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.
--- 322,327 ----
*************** typedef struct RelOptInfo
*** 408,416 ****
List *indexlist; /* list of IndexOptInfo */
BlockNumber pages;
double tuples;
! struct Plan *subplan; /* if subquery */
! List *subrtable; /* if subquery */
! List *subrowmark; /* if subquery */
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base
--- 401,408 ----
List *indexlist; /* list of IndexOptInfo */
BlockNumber pages;
double tuples;
!
! Query *preprocessed_subquery; /* if subquery */
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base
*************** typedef struct TidPath
*** 770,775 ****
--- 762,784 ----
} 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
new file mode 100644
index 2763863..7ec2c7b
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
*************** extern void cost_bitmap_or_node(BitmapOr
*** 75,81 ****
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_functionscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel);
extern void cost_valuesscan(Path *path, PlannerInfo *root,
--- 75,81 ----
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(SubqueryPath *path, RelOptInfo *baserel);
extern void cost_functionscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel);
extern void cost_valuesscan(Path *path, PlannerInfo *root,
*************** extern void set_joinrel_size_estimates(P
*** 122,128 ****
SpecialJoinInfo *sjinfo,
List *restrictlist);
extern void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
! PlannerInfo *subroot);
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,
--- 122,128 ----
SpecialJoinInfo *sjinfo,
List *restrictlist);
extern void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel,
! 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
new file mode 100644
index 1da2131..6de2816
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 14,19 ****
--- 14,20 ----
#ifndef PATHNODE_H
#define PATHNODE_H
+ #include "nodes/plannodes.h"
#include "nodes/relation.h"
*************** extern ResultPath *create_result_path(Li
*** 56,62 ****
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_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
--- 57,64 ----
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,
! 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
new file mode 100644
index 7f1353a..54d0b01
*** a/src/include/optimizer/paths.h
--- b/src/include/optimizer/paths.h
*************** extern PGDLLIMPORT join_search_hook_type
*** 33,38 ****
--- 33,41 ----
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);
*************** extern Path *get_cheapest_fractional_pat
*** 154,160 ****
extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index,
ScanDirection scandir);
extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
! List *subquery_pathkeys);
extern List *build_join_pathkeys(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
--- 157,163 ----
extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index,
ScanDirection scandir);
extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
! 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