From f2f5a2fd92ed9cfbabc3bcb67cb1a3c5ae99bc7c Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 6 Jun 2018 20:07:31 +0300 Subject: [PATCH 2/2] WIP: Allow pushing join quals down into subqueries. Plan subqueries a second time, to create parameterized plans with join quals. --- src/backend/nodes/outfuncs.c | 2 - src/backend/optimizer/path/allpaths.c | 143 ++++++++++++++++++++--- src/backend/optimizer/path/costsize.c | 3 +- src/backend/optimizer/plan/createplan.c | 30 ++++- src/backend/optimizer/plan/setrefs.c | 8 +- src/backend/optimizer/plan/subselect.c | 4 +- src/backend/optimizer/prep/prepunion.c | 22 ++-- src/backend/optimizer/util/pathnode.c | 16 ++- src/backend/optimizer/util/relnode.c | 7 +- src/backend/utils/adt/selfuncs.c | 25 +++- src/include/nodes/relation.h | 8 +- src/include/optimizer/cost.h | 2 +- src/include/optimizer/pathnode.h | 7 +- src/test/regress/expected/subselect_pushdown.out | 17 ++- src/test/regress/sql/subselect_pushdown.sql | 7 +- 15 files changed, 227 insertions(+), 74 deletions(-) diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 610f9edaf5..0bc7855d0d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2340,8 +2340,6 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) WRITE_UINT_FIELD(pages); WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_FLOAT_FIELD(allvisfrac, "%.6f"); - WRITE_NODE_FIELD(subroot); - WRITE_NODE_FIELD(subplan_params); WRITE_INT_FIELD(rel_parallel_workers); WRITE_OID_FIELD(serverid); WRITE_OID_FIELD(userid); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 35537272a3..bdcdc30985 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -106,6 +106,11 @@ static void accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths); static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); +static void add_subqueryscan_variant(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte, + Bitmapset *required_outer, + Query *subquery, List *pushed_down_clauses, double tuple_fraction, + bool update_estimates); static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, @@ -2080,13 +2085,13 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { Query *parse = root->parse; + Query *unparameterized_subquery; Query *subquery = rte->subquery; Relids required_outer; pushdown_safety_info safetyInfo; double tuple_fraction; - RelOptInfo *sub_final_rel; - ListCell *lc; List *pushed_down_ec_joins = NIL; + bool sq_is_pushdown_safe; /* * Must copy the Query so that planning doesn't mess up the RTE contents @@ -2147,9 +2152,10 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, * 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 || - (!bms_is_empty(required_outer) && (rel->joininfo || rel->has_eclass_joins))) && - subquery_is_pushdown_safe(subquery, subquery, &safetyInfo)) + sq_is_pushdown_safe = subquery_is_pushdown_safe(subquery, subquery, &safetyInfo); + if (sq_is_pushdown_safe && + (rel->baserestrictinfo != NIL || + (!bms_is_empty(required_outer) && (rel->joininfo || rel->has_eclass_joins)))) { /* OK to consider pushing down individual quals */ ListCell *l; @@ -2218,6 +2224,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, if (rel->has_eclass_joins) { List *clauses; + ListCell *lc; clauses = generate_join_implied_equalities(root, available_relids, @@ -2243,8 +2250,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, } } - pfree(safetyInfo.unsafeColumns); - /* * The upper query might not use all the subquery's output columns; if * not, we can simplify. @@ -2268,16 +2273,112 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, else tuple_fraction = root->tuple_fraction; + unparameterized_subquery = copyObject(subquery); + + add_subqueryscan_variant(root, rel, rti, rte, + required_outer, subquery, pushed_down_ec_joins, tuple_fraction, true); + + /* + * Also create parameterized join paths, where we push the join condition + * down to the subquery. + * + * To keep the planning time reasonable, this is all-or-nothing. We try to + * push all join conditions down to the subquery, and create paths for that. + * We don't create paths for every combination of join conditions that we + * could push down. + */ + if ((rel->has_eclass_joins || rel->joininfo) && + sq_is_pushdown_safe) + { + List *clauses; + ListCell *lc; + List *pushed_down_clauses = list_copy(pushed_down_ec_joins); + Bitmapset *available_relids; + Bitmapset *other_relids; + + subquery = copyObject(unparameterized_subquery); + + required_outer = bms_copy(required_outer); + + foreach(lc, rel->joininfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *clause = (Node *) rinfo->clause; + + if (!rinfo->pseudoconstant && + qual_is_pushdown_safe(subquery, rti, clause, &safetyInfo)) + { + /* Push it down */ + required_outer = bms_union(required_outer, + pull_varnos(clause)); + required_outer = bms_del_member(required_outer, rti); + + subquery_push_qual(subquery, rte, rti, clause, 0); + + pushed_down_clauses = lappend(pushed_down_clauses, rinfo); + } + } + + /* + * We already pushed down any join quals with LATERAL referenced rels, don't add + * them again. + */ + available_relids = bms_difference(root->all_baserels, rel->lateral_referencers); + other_relids = bms_del_member(bms_copy(available_relids), rti); + + clauses = generate_join_implied_equalities(root, + available_relids, + other_relids, + rel); + foreach(lc, clauses) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *clause = (Node *) rinfo->clause; + + if (!rinfo->pseudoconstant && + qual_is_pushdown_safe(subquery, rti, clause, &safetyInfo)) + { + /* Push it down */ + required_outer = bms_union(required_outer, + pull_varnos(clause)); + required_outer = bms_del_member(required_outer, rti); + + subquery_push_qual(subquery, rte, rti, clause, 0); + + pushed_down_clauses = lappend(pushed_down_clauses, rinfo); + } + } + if (pushed_down_clauses) + add_subqueryscan_variant(root, rel, rti, rte, + required_outer, + subquery, pushed_down_clauses, tuple_fraction, false); + } + + pfree(safetyInfo.unsafeColumns); +} + +static void +add_subqueryscan_variant(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte, + Bitmapset *required_outer, + Query *subquery, List *pushed_down_clauses, double tuple_fraction, + bool update_estimates) +{ + RelOptInfo *sub_final_rel; + ListCell *lc; + PlannerInfo *subroot; + List *subplan_params; + /* plan_params should not be in use in current query level */ Assert(root->plan_params == NIL); /* Generate a subroot and Paths for the subquery */ - rel->subroot = subquery_planner(root->glob, subquery, - root, - false, tuple_fraction); + subroot = subquery_planner(root->glob, subquery, + root, + false, tuple_fraction); /* Isolate the params needed by this specific subplan */ - rel->subplan_params = root->plan_params; + subplan_params = root->plan_params; root->plan_params = NIL; /* @@ -2285,7 +2386,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, * so, it's desirable to produce an unadorned dummy path so that we will * recognize appropriate optimizations at this query level. */ - sub_final_rel = fetch_upper_rel(rel->subroot, UPPERREL_FINAL, NULL); + sub_final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL); if (IS_DUMMY_REL(sub_final_rel)) { @@ -2297,8 +2398,13 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, * Mark rel with estimated output rows, width, etc. Note that we have to * do this before generating outer-query paths, else cost_subqueryscan is * not happy. + * + * Don't overwrite the estimates when we're creating parameterized paths + * for joins. The estimate for a parameterized path includes the effects + * of the join clauses. */ - set_subquery_size_estimates(root, rel); + if (update_estimates) + set_subquery_size_estimates(root, rel, subroot); /* * For each Path that subquery_planner produced, make a SubqueryScanPath @@ -2317,8 +2423,8 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Generate outer path using this subpath */ add_path(rel, (Path *) - create_subqueryscan_path(root, rel, subpath, - pathkeys, required_outer, pushed_down_ec_joins)); + create_subqueryscan_path(root, rel, subroot, subplan_params, subpath, + pathkeys, required_outer, pushed_down_clauses)); } /* If outer rel allows parallelism, do same for partial paths. */ @@ -2342,9 +2448,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Generate outer path using this subpath */ add_partial_path(rel, (Path *) - create_subqueryscan_path(root, rel, subpath, + create_subqueryscan_path(root, rel, subroot, subplan_params, subpath, pathkeys, - required_outer, pushed_down_ec_joins)); + required_outer, pushed_down_clauses)); } } } @@ -3231,8 +3337,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, Assert(!contain_window_function(qual)); /* - * Examine all Vars used in clause; since it's a restriction clause, all - * such Vars must refer to subselect output columns. + * Examine all Vars used in clause. */ vars = pull_var_clause(qual, PVC_INCLUDE_PLACEHOLDERS); foreach(vl, vars) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index a2a7e0c520..5f93328e22 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -4807,9 +4807,8 @@ get_foreign_key_join_selectivity(PlannerInfo *root, * We set the same fields as set_baserel_size_estimates. */ void -set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel) +set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel, PlannerInfo *subroot) { - PlannerInfo *subroot = rel->subroot; RelOptInfo *sub_final_rel; ListCell *lc; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d34c1bf128..8261c3e17c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -3188,18 +3188,40 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, Plan *subplan; ListCell *l; List *qpqual; - List *sq_quals = best_path->pushed_down_ec_joins; + List *sq_quals = best_path->pushed_down_clauses; /* it should be a subquery base rel... */ Assert(scan_relid > 0); Assert(rel->rtekind == RTE_SUBQUERY); + Assert(rel->chosen_plan == NULL); /* * Recursively create Plan from Path for subquery. Since we are entering * a different planner context (subroot), recurse to create_plan not * create_plan_recurse. */ - subplan = create_plan(rel->subroot, best_path->subpath); + subplan = create_plan(best_path->subroot, best_path->subpath); + + /* + * If this path used join quals that were pushed down to the subquery, + * we don't need to re-check those quals on the SubqueryScan node itself. + */ + if (best_path->pushed_down_clauses) + { + List *new_clauses = NIL; + ListCell *l; + + foreach(l, scan_clauses) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); + + if (list_member_ptr(best_path->pushed_down_clauses, rinfo)) + continue; + + new_clauses = lappend(new_clauses, rinfo); + } + scan_clauses = new_clauses; + } /* * If we had pushed down any join clauses to the subquery, we don't need @@ -3240,7 +3262,7 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, scan_clauses = (List *) replace_nestloop_params(root, (Node *) scan_clauses); process_subquery_nestloop_params(root, - rel->subplan_params); + best_path->subplan_params); } scan_plan = make_subqueryscan(tlist, @@ -3250,6 +3272,8 @@ create_subqueryscan_plan(PlannerInfo *root, SubqueryScanPath *best_path, copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + rel->chosen_plan = best_path->subroot; + return scan_plan; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 69dd327f0c..29b26c086a 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -323,12 +323,12 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * that some upper query level is treating this one as dummy, * and so we won't scan this level's plan tree at all. */ - if (rel->subroot == NULL) + if (rel->chosen_plan == NULL) flatten_unplanned_rtes(glob, rte); else if (recursing || - IS_DUMMY_REL(fetch_upper_rel(rel->subroot, + IS_DUMMY_REL(fetch_upper_rel(rel->chosen_plan, UPPERREL_FINAL, NULL))) - add_rtes_to_flat_rtable(rel->subroot, true); + add_rtes_to_flat_rtable(rel->chosen_plan, true); } } rti++; @@ -1070,7 +1070,7 @@ set_subqueryscan_references(PlannerInfo *root, rel = find_base_rel(root, plan->scan.scanrelid); /* Recursively process the subplan */ - plan->subplan = set_plan_references(rel->subroot, plan->subplan); + plan->subplan = set_plan_references(rel->chosen_plan, plan->subplan); if (trivial_subqueryscan(plan)) { diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 83008d7661..f756febe03 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2418,11 +2418,11 @@ finalize_plan(PlannerInfo *root, Plan *plan, /* We must run finalize_plan on the subquery */ rel = find_base_rel(root, sscan->scan.scanrelid); - subquery_params = rel->subroot->outer_params; + subquery_params = rel->chosen_plan->outer_params; if (gather_param >= 0) subquery_params = bms_add_member(bms_copy(subquery_params), gather_param); - finalize_plan(rel->subroot, sscan->subplan, gather_param, + finalize_plan(rel->chosen_plan, sscan->subplan, gather_param, subquery_params, NULL); /* Now we can add its extParams to the parent's params */ diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 8f424031be..8bf864f768 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -266,10 +266,10 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, Assert(root->plan_params == NIL); /* Generate a subroot and Paths for the subquery */ - subroot = rel->subroot = subquery_planner(root->glob, subquery, - root, - false, - root->tuple_fraction); + subroot = subquery_planner(root->glob, subquery, + root, + false, + root->tuple_fraction); /* * It should not be possible for the primitive query to contain any @@ -295,7 +295,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * to do this before generating outer-query paths, else * cost_subqueryscan is not happy. */ - set_subquery_size_estimates(root, rel); + set_subquery_size_estimates(root, rel, subroot); /* * Since we may want to add a partial path to this relation, we must @@ -320,8 +320,12 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * the SubqueryScanPath with nil pathkeys. (XXX that should change * soon too, likely.) */ - path = (Path *) create_subqueryscan_path(root, rel, subpath, - NIL, NULL, NIL); + path = (Path *) create_subqueryscan_path(root, rel, + subroot, + NIL, + subpath, + NIL, + NULL, NIL); add_path(rel, path); @@ -338,8 +342,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, partial_subpath = linitial(final_rel->partial_pathlist); partial_path = (Path *) - create_subqueryscan_path(root, rel, partial_subpath, - NIL, NULL, NIL); + create_subqueryscan_path(root, rel, subroot, NIL, partial_subpath, NIL, + NULL, NIL); add_partial_path(rel, partial_path); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index e4909c1855..748e0120f1 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1868,8 +1868,12 @@ create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, * returning the pathnode. */ SubqueryScanPath * -create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, - List *pathkeys, Relids required_outer, List *pushed_down_ec_joins) +create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, + PlannerInfo *subroot, + List *subplan_params, + Path *subpath, + List *pathkeys, Relids required_outer, + List *pushed_down_clauses) { SubqueryScanPath *pathnode = makeNode(SubqueryScanPath); @@ -1884,7 +1888,9 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, pathnode->path.parallel_workers = subpath->parallel_workers; pathnode->path.pathkeys = pathkeys; pathnode->subpath = subpath; - pathnode->pushed_down_ec_joins = pushed_down_ec_joins; + pathnode->subplan_params = subplan_params; + pathnode->subroot = subroot; + pathnode->pushed_down_clauses = pushed_down_clauses; cost_subqueryscan(pathnode, root, rel, pathnode->path.param_info); @@ -3561,10 +3567,12 @@ reparameterize_path(PlannerInfo *root, Path *path, return (Path *) create_subqueryscan_path(root, rel, + spath->subroot, + spath->subplan_params, spath->subpath, spath->path.pathkeys, required_outer, - spath->pushed_down_ec_joins); + spath->pushed_down_clauses); } case T_Append: { diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 82b78420e7..b1b89310cb 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -135,8 +135,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->pages = 0; rel->tuples = 0; rel->allvisfrac = 0; - rel->subroot = NULL; - rel->subplan_params = NIL; rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->serverid = InvalidOid; rel->userid = rte->checkAsUser; @@ -549,8 +547,6 @@ build_join_rel(PlannerInfo *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->allvisfrac = 0; - joinrel->subroot = NULL; - joinrel->subplan_params = NIL; joinrel->rel_parallel_workers = -1; joinrel->serverid = InvalidOid; joinrel->userid = InvalidOid; @@ -724,8 +720,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, joinrel->pages = 0; joinrel->tuples = 0; joinrel->allvisfrac = 0; - joinrel->subroot = NULL; - joinrel->subplan_params = NIL; + joinrel->chosen_plan = NULL; joinrel->serverid = InvalidOid; joinrel->userid = InvalidOid; joinrel->useridiscurrent = false; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 4b08cdb721..a813da2b00 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5037,6 +5037,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, Query *subquery = rte->subquery; RelOptInfo *rel; TargetEntry *ste; + PlannerInfo *subroot; /* * Punt if it's a whole-row var rather than a plain column reference. @@ -5065,10 +5066,24 @@ examine_simple_variable(PlannerInfo *root, Var *var, */ rel = find_base_rel(root, var->varno); - /* If the subquery hasn't been planned yet, we have to punt */ - if (rel->subroot == NULL) + if (rel->chosen_plan) + subroot = rel->chosen_plan; + else if (rel->pathlist && IsA(linitial(rel->pathlist), SubqueryScanPath)) + { + /* + * Use the estimates from the first path. XXX: what if it's a parameterized + * path? + */ + SubqueryScanPath *sqpath = (SubqueryScanPath *) linitial(rel->pathlist); + + subroot = sqpath->subroot; + } + else + { + /* If the subquery hasn't been planned yet, we have to punt */ return; - Assert(IsA(rel->subroot, PlannerInfo)); + } + Assert(IsA(subroot, PlannerInfo)); /* * Switch our attention to the subquery as mangled by the planner. It @@ -5078,7 +5093,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * planning, Vars in the targetlist might have gotten replaced, and we * need to see the replacement expressions. */ - subquery = rel->subroot->parse; + subquery = subroot->parse; Assert(IsA(subquery, Query)); /* Get the subquery output expression referenced by the upper Var */ @@ -5130,7 +5145,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, * if the underlying column is unique, the subquery may have * joined to other tables in a way that creates duplicates. */ - examine_simple_variable(rel->subroot, var, vardata); + examine_simple_variable(subroot, var, vardata); } } else diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index de942b4e40..aca74a4ece 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -651,8 +651,7 @@ typedef struct RelOptInfo BlockNumber pages; /* size estimates derived from pg_class */ double tuples; double allvisfrac; - PlannerInfo *subroot; /* if subquery */ - List *subplan_params; /* if subquery */ + PlannerInfo *chosen_plan; int rel_parallel_workers; /* wanted number of parallel workers */ /* Information about foreign tables and foreign joins */ @@ -1237,7 +1236,10 @@ typedef struct SubqueryScanPath { Path path; Path *subpath; /* path representing subquery execution */ - List *pushed_down_ec_joins; /* pushed-down quals derived from ECs */ + PlannerInfo *subroot; /* */ + List *subplan_params; /* */ + + List *pushed_down_clauses; /* pushed-down join quals */ } SubqueryScanPath; /* diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 77ca7ff837..82bb0f51a3 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -189,7 +189,7 @@ extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel, RelOptInfo *inner_rel, SpecialJoinInfo *sjinfo, List *restrictlist); -extern void set_subquery_size_estimates(PlannerInfo *root, RelOptInfo *rel); +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, diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 848839ed15..596815ecfc 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -91,8 +91,11 @@ extern GatherMergePath *create_gather_merge_path(PlannerInfo *root, Relids required_outer, double *rows); extern SubqueryScanPath *create_subqueryscan_path(PlannerInfo *root, - RelOptInfo *rel, Path *subpath, - List *pathkeys, Relids required_outer, List *pushed_down_ec_joins); + RelOptInfo *rel, + PlannerInfo *subroot, + List *subplan_params, + Path *subpath, + List *pathkeys, Relids required_outer, List *pushed_down_clauses); extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer); extern Path *create_tablexprscan_path(PlannerInfo *root, RelOptInfo *rel, diff --git a/src/test/regress/expected/subselect_pushdown.out b/src/test/regress/expected/subselect_pushdown.out index f160bc84c8..792f66e7fd 100644 --- a/src/test/regress/expected/subselect_pushdown.out +++ b/src/test/regress/expected/subselect_pushdown.out @@ -25,7 +25,7 @@ where smalltab.i = subq.i and smalltab.i = 123; Index Cond: (i = 123) (7 rows) --- Join quals are not currently pushed down +-- Push down join quals. explain (costs off) select * from smalltab, ( @@ -36,18 +36,17 @@ select * from smalltab, where smalltab.i = subq.i; QUERY PLAN ------------------------------------------------- - Merge Join - Merge Cond: (smalltab.i = bigtab.i) - -> Sort - Sort Key: smalltab.i - -> Seq Scan on smalltab + Nested Loop + -> Seq Scan on smalltab -> GroupAggregate Group Key: bigtab.i -> Index Scan using bigtab_i on bigtab -(8 rows) + Index Cond: (smalltab.i = i) +(6 rows) --- Except when the subquery is LATERAL, and already references the other relation. --- Such join clauses can be pushed down. +-- Subquery is LATERAL, and already references the other relation. The join +-- qual is always pushed down in that case, as the plan is "parameterized" +-- in respect to the other relation even if it was not pushed down. explain (costs off) select * from smalltab, lateral ( diff --git a/src/test/regress/sql/subselect_pushdown.sql b/src/test/regress/sql/subselect_pushdown.sql index c9e7a43794..d6431d314c 100644 --- a/src/test/regress/sql/subselect_pushdown.sql +++ b/src/test/regress/sql/subselect_pushdown.sql @@ -20,7 +20,7 @@ select * from smalltab, ) as subq(i, avg) where smalltab.i = subq.i and smalltab.i = 123; --- Join quals are not currently pushed down +-- Push down join quals. explain (costs off) select * from smalltab, ( @@ -30,8 +30,9 @@ select * from smalltab, ) as subq(i, avg) where smalltab.i = subq.i; --- Except when the subquery is LATERAL, and already references the other relation. --- Such join clauses can be pushed down. +-- Subquery is LATERAL, and already references the other relation. The join +-- qual is always pushed down in that case, as the plan is "parameterized" +-- in respect to the other relation even if it was not pushed down. explain (costs off) select * from smalltab, lateral ( -- 2.11.0