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