diff options
Diffstat (limited to 'src/backend/optimizer/path/joinpath.c')
-rw-r--r-- | src/backend/optimizer/path/joinpath.c | 749 |
1 files changed, 517 insertions, 232 deletions
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 65caeb86c9b..1d537d84ee6 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -25,17 +25,20 @@ static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, List *mergeclause_list, - JoinType jointype, SpecialJoinInfo *sjinfo); + JoinType jointype, SpecialJoinInfo *sjinfo, + Relids param_source_rels); static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, List *mergeclause_list, - JoinType jointype, SpecialJoinInfo *sjinfo); + JoinType jointype, SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels); static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, - JoinType jointype, SpecialJoinInfo *sjinfo); -static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel, - RelOptInfo *outer_rel, JoinType jointype); + JoinType jointype, SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels); static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, @@ -79,6 +82,9 @@ add_paths_to_joinrel(PlannerInfo *root, { List *mergeclause_list = NIL; bool mergejoin_allowed = true; + SemiAntiJoinFactors semifactors; + Relids param_source_rels = NULL; + ListCell *lc; /* * Find potential mergejoin clauses. We can skip this if we are not @@ -96,12 +102,59 @@ add_paths_to_joinrel(PlannerInfo *root, &mergejoin_allowed); /* + * If it's SEMI or ANTI join, compute correction factors for cost + * estimation. These will be the same for all paths. + */ + if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) + compute_semi_anti_join_factors(root, outerrel, innerrel, + jointype, sjinfo, restrictlist, + &semifactors); + + /* + * Decide whether it's sensible to generate parameterized paths for this + * joinrel, and if so, which relations such paths should require. There + * is no need to create a parameterized result path unless there is a join + * order restriction that prevents joining one of our input rels directly + * to the parameter source rel instead of joining to the other input rel. + * This restriction reduces the number of parameterized paths we have to + * deal with at higher join levels, without compromising the quality of + * the resulting plan. We express the restriction as a Relids set that + * must overlap the parameterization of any proposed join path. + */ + foreach(lc, root->join_info_list) + { + SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); + + /* + * SJ is relevant to this join if we have some part of its RHS + * (possibly not all of it), and haven't yet joined to its LHS. (This + * test is pretty simplistic, but should be sufficient considering the + * join has already been proven legal.) If the SJ is relevant, it + * presents constraints for joining to anything not in its RHS. + */ + if (bms_overlap(joinrel->relids, sjinfo->min_righthand) && + !bms_overlap(joinrel->relids, sjinfo->min_lefthand)) + param_source_rels = bms_join(param_source_rels, + bms_difference(root->all_baserels, + sjinfo->min_righthand)); + + /* full joins constrain both sides symmetrically */ + if (sjinfo->jointype == JOIN_FULL && + bms_overlap(joinrel->relids, sjinfo->min_lefthand) && + !bms_overlap(joinrel->relids, sjinfo->min_righthand)) + param_source_rels = bms_join(param_source_rels, + bms_difference(root->all_baserels, + sjinfo->min_lefthand)); + } + + /* * 1. Consider mergejoin paths where both relations must be explicitly * sorted. Skip this if we can't mergejoin. */ if (mergejoin_allowed) sort_inner_and_outer(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, sjinfo); + restrictlist, mergeclause_list, jointype, + sjinfo, param_source_rels); /* * 2. Consider paths where the outer relation need not be explicitly @@ -112,7 +165,8 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (mergejoin_allowed) match_unsorted_outer(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, sjinfo); + restrictlist, mergeclause_list, jointype, + sjinfo, &semifactors, param_source_rels); #ifdef NOT_USED @@ -129,7 +183,8 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (mergejoin_allowed) match_unsorted_inner(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, sjinfo); + restrictlist, mergeclause_list, jointype, + sjinfo, &semifactors, param_source_rels); #endif /* @@ -139,7 +194,226 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (enable_hashjoin || jointype == JOIN_FULL) hash_inner_and_outer(root, joinrel, outerrel, innerrel, - restrictlist, jointype, sjinfo); + restrictlist, jointype, + sjinfo, &semifactors, param_source_rels); +} + +/* + * try_nestloop_path + * Consider a nestloop join path; if it appears useful, push it into + * the joinrel's pathlist via add_path(). + */ +static void +try_nestloop_path(PlannerInfo *root, + RelOptInfo *joinrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels, + Path *outer_path, + Path *inner_path, + List *restrict_clauses, + List *pathkeys) +{ + Relids required_outer; + JoinCostWorkspace workspace; + + /* + * Check to see if proposed path is still parameterized, and reject if + * the parameterization wouldn't be sensible. + */ + required_outer = calc_nestloop_required_outer(outer_path, + inner_path); + if (required_outer && + !bms_overlap(required_outer, param_source_rels)) + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + return; + } + + /* + * Do a precheck to quickly eliminate obviously-inferior paths. We + * calculate a cheap lower bound on the path's cost and then use + * add_path_precheck() to see if the path is clearly going to be dominated + * by some existing path for the joinrel. If not, do the full pushup with + * creating a fully valid path structure and submitting it to add_path(). + * The latter two steps are expensive enough to make this two-phase + * methodology worthwhile. + */ + initial_cost_nestloop(root, &workspace, jointype, + outer_path, inner_path, + sjinfo, semifactors); + + if (add_path_precheck(joinrel, + workspace.startup_cost, workspace.total_cost, + pathkeys, required_outer)) + { + add_path(joinrel, (Path *) + create_nestloop_path(root, + joinrel, + jointype, + &workspace, + sjinfo, + semifactors, + outer_path, + inner_path, + restrict_clauses, + pathkeys, + required_outer)); + } + else + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + } +} + +/* + * try_mergejoin_path + * Consider a merge join path; if it appears useful, push it into + * the joinrel's pathlist via add_path(). + */ +static void +try_mergejoin_path(PlannerInfo *root, + RelOptInfo *joinrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + Relids param_source_rels, + Path *outer_path, + Path *inner_path, + List *restrict_clauses, + List *pathkeys, + List *mergeclauses, + List *outersortkeys, + List *innersortkeys) +{ + Relids required_outer; + JoinCostWorkspace workspace; + + /* + * Check to see if proposed path is still parameterized, and reject if + * the parameterization wouldn't be sensible. + */ + required_outer = calc_non_nestloop_required_outer(outer_path, + inner_path); + if (required_outer && + !bms_overlap(required_outer, param_source_rels)) + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + return; + } + + /* + * If the given paths are already well enough ordered, we can skip doing + * an explicit sort. + */ + if (outersortkeys && + pathkeys_contained_in(outersortkeys, outer_path->pathkeys)) + outersortkeys = NIL; + if (innersortkeys && + pathkeys_contained_in(innersortkeys, inner_path->pathkeys)) + innersortkeys = NIL; + + /* + * See comments in try_nestloop_path(). + */ + initial_cost_mergejoin(root, &workspace, jointype, mergeclauses, + outer_path, inner_path, + outersortkeys, innersortkeys, + sjinfo); + + if (add_path_precheck(joinrel, + workspace.startup_cost, workspace.total_cost, + pathkeys, required_outer)) + { + add_path(joinrel, (Path *) + create_mergejoin_path(root, + joinrel, + jointype, + &workspace, + sjinfo, + outer_path, + inner_path, + restrict_clauses, + pathkeys, + required_outer, + mergeclauses, + outersortkeys, + innersortkeys)); + } + else + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + } +} + +/* + * try_hashjoin_path + * Consider a hash join path; if it appears useful, push it into + * the joinrel's pathlist via add_path(). + */ +static void +try_hashjoin_path(PlannerInfo *root, + RelOptInfo *joinrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels, + Path *outer_path, + Path *inner_path, + List *restrict_clauses, + List *hashclauses) +{ + Relids required_outer; + JoinCostWorkspace workspace; + + /* + * Check to see if proposed path is still parameterized, and reject if + * the parameterization wouldn't be sensible. + */ + required_outer = calc_non_nestloop_required_outer(outer_path, + inner_path); + if (required_outer && + !bms_overlap(required_outer, param_source_rels)) + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + return; + } + + /* + * See comments in try_nestloop_path(). Also note that hashjoin paths + * never have any output pathkeys, per comments in create_hashjoin_path. + */ + initial_cost_hashjoin(root, &workspace, jointype, hashclauses, + outer_path, inner_path, + sjinfo, semifactors); + + if (add_path_precheck(joinrel, + workspace.startup_cost, workspace.total_cost, + NIL, required_outer)) + { + add_path(joinrel, (Path *) + create_hashjoin_path(root, + joinrel, + jointype, + &workspace, + sjinfo, + semifactors, + outer_path, + inner_path, + restrict_clauses, + required_outer, + hashclauses)); + } + else + { + /* Waste no memory when we reject a path here */ + bms_free(required_outer); + } } /* @@ -187,6 +461,7 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel, * mergejoin clauses in this join * 'jointype' is the type of join to do * 'sjinfo' is extra info about the join for selectivity estimation + * 'param_source_rels' are OK targets for parameterization of result paths */ static void sort_inner_and_outer(PlannerInfo *root, @@ -196,7 +471,8 @@ sort_inner_and_outer(PlannerInfo *root, List *restrictlist, List *mergeclause_list, JoinType jointype, - SpecialJoinInfo *sjinfo) + SpecialJoinInfo *sjinfo, + Relids param_source_rels) { Path *outer_path; Path *inner_path; @@ -209,6 +485,13 @@ sort_inner_and_outer(PlannerInfo *root, * cheapest-startup-cost input paths later, and only if they don't need a * sort. * + * This function intentionally does not consider parameterized input paths + * (implicit in the fact that it only looks at cheapest_total_path, which + * is always unparameterized). If we did so, we'd have a combinatorial + * explosion of mergejoin paths of dubious value. This interacts with + * decisions elsewhere that also discriminate against mergejoins with + * parameterized inputs; see comments in src/backend/optimizer/README. + * * If unique-ification is requested, do it and then handle as a plain * inner join. */ @@ -299,21 +582,21 @@ sort_inner_and_outer(PlannerInfo *root, * And now we can make the path. * * Note: it's possible that the cheapest paths will already be sorted - * properly. create_mergejoin_path will detect that case and suppress + * properly. try_mergejoin_path will detect that case and suppress * an explicit sort step, so we needn't do so here. */ - add_path(joinrel, (Path *) - create_mergejoin_path(root, - joinrel, - jointype, - sjinfo, - outer_path, - inner_path, - restrictlist, - merge_pathkeys, - cur_mergeclauses, - outerkeys, - innerkeys)); + try_mergejoin_path(root, + joinrel, + jointype, + sjinfo, + param_source_rels, + outer_path, + inner_path, + restrictlist, + merge_pathkeys, + cur_mergeclauses, + outerkeys, + innerkeys); } } @@ -350,6 +633,8 @@ sort_inner_and_outer(PlannerInfo *root, * mergejoin clauses in this join * 'jointype' is the type of join to do * 'sjinfo' is extra info about the join for selectivity estimation + * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'param_source_rels' are OK targets for parameterization of result paths */ static void match_unsorted_outer(PlannerInfo *root, @@ -359,17 +644,16 @@ match_unsorted_outer(PlannerInfo *root, List *restrictlist, List *mergeclause_list, JoinType jointype, - SpecialJoinInfo *sjinfo) + SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels) { JoinType save_jointype = jointype; bool nestjoinOK; bool useallclauses; - Path *inner_cheapest_startup = innerrel->cheapest_startup_path; Path *inner_cheapest_total = innerrel->cheapest_total_path; Path *matpath = NULL; - Path *index_cheapest_startup = NULL; - Path *index_cheapest_total = NULL; - ListCell *l; + ListCell *lc1; /* * Nestloop only supports inner, left, semi, and anti joins. Also, if we @@ -408,14 +692,13 @@ match_unsorted_outer(PlannerInfo *root, /* * If we need to unique-ify the inner path, we will consider only the - * cheapest inner. + * cheapest-total inner. */ if (save_jointype == JOIN_UNIQUE_INNER) { inner_cheapest_total = (Path *) create_unique_path(root, innerrel, inner_cheapest_total, sjinfo); Assert(inner_cheapest_total); - inner_cheapest_startup = inner_cheapest_total; } else if (nestjoinOK) { @@ -428,28 +711,11 @@ match_unsorted_outer(PlannerInfo *root, !ExecMaterializesOutput(inner_cheapest_total->pathtype)) matpath = (Path *) 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) - { - if (IsA(inner_cheapest_total, AppendPath)) - index_cheapest_total = best_appendrel_indexscan(root, - innerrel, - outerrel, - jointype); - else if (innerrel->rtekind == RTE_RELATION) - best_inner_indexscan(root, innerrel, outerrel, jointype, - &index_cheapest_startup, - &index_cheapest_total); - } } - foreach(l, outerrel->pathlist) + foreach(lc1, outerrel->pathlist) { - Path *outerpath = (Path *) lfirst(l); + Path *outerpath = (Path *) lfirst(lc1); List *merge_pathkeys; List *mergeclauses; List *innersortkeys; @@ -460,8 +726,15 @@ match_unsorted_outer(PlannerInfo *root, int sortkeycnt; /* + * We cannot use an outer path that is parameterized by the inner rel. + */ + if (bms_overlap(outerpath->required_outer, innerrel->relids)) + continue; + + /* * If we need to unique-ify the outer path, it's pointless to consider - * any but the cheapest outer. + * any but the cheapest outer. (XXX we don't consider parameterized + * outers, nor inners, for unique-ified cases. Should we?) */ if (save_jointype == JOIN_UNIQUE_OUTER) { @@ -480,65 +753,61 @@ match_unsorted_outer(PlannerInfo *root, merge_pathkeys = build_join_pathkeys(root, joinrel, jointype, outerpath->pathkeys); - if (nestjoinOK) + if (save_jointype == JOIN_UNIQUE_INNER) + { + /* + * Consider nestloop join, but only with the unique-ified cheapest + * inner path + */ + try_nestloop_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + outerpath, + inner_cheapest_total, + restrictlist, + merge_pathkeys); + } + else if (nestjoinOK) { /* - * Always consider a nestloop join with this outer and - * 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. + * Consider nestloop joins using this outer path and various + * available paths for the inner relation. We consider the + * cheapest-total paths for each available parameterization of + * the inner relation, including the unparameterized case. */ - add_path(joinrel, (Path *) - create_nestloop_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - inner_cheapest_total, - restrictlist, - merge_pathkeys)); + ListCell *lc2; + + foreach(lc2, innerrel->cheapest_parameterized_paths) + { + Path *innerpath = (Path *) lfirst(lc2); + + try_nestloop_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + outerpath, + innerpath, + restrictlist, + merge_pathkeys); + } + + /* Also consider materialized form of the cheapest inner path */ if (matpath != NULL) - add_path(joinrel, (Path *) - create_nestloop_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - matpath, - restrictlist, - merge_pathkeys)); - if (inner_cheapest_startup != inner_cheapest_total) - add_path(joinrel, (Path *) - create_nestloop_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - inner_cheapest_startup, - restrictlist, - merge_pathkeys)); - if (index_cheapest_total != NULL) - add_path(joinrel, (Path *) - create_nestloop_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - index_cheapest_total, - restrictlist, - merge_pathkeys)); - if (index_cheapest_startup != NULL && - index_cheapest_startup != index_cheapest_total) - add_path(joinrel, (Path *) - create_nestloop_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - index_cheapest_startup, - restrictlist, - merge_pathkeys)); + try_nestloop_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + outerpath, + matpath, + restrictlist, + merge_pathkeys); } /* Can't do anything else if outer path needs to be unique'd */ @@ -578,21 +847,21 @@ match_unsorted_outer(PlannerInfo *root, /* * Generate a mergejoin on the basis of sorting the cheapest inner. * Since a sort will be needed, only cheapest total cost matters. (But - * create_mergejoin_path will do the right thing if + * try_mergejoin_path will do the right thing if * inner_cheapest_total is already correctly sorted.) */ - add_path(joinrel, (Path *) - create_mergejoin_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - inner_cheapest_total, - restrictlist, - merge_pathkeys, - mergeclauses, - NIL, - innersortkeys)); + try_mergejoin_path(root, + joinrel, + jointype, + sjinfo, + param_source_rels, + outerpath, + inner_cheapest_total, + restrictlist, + merge_pathkeys, + mergeclauses, + NIL, + innersortkeys); /* Can't do anything else if inner path needs to be unique'd */ if (save_jointype == JOIN_UNIQUE_INNER) @@ -604,6 +873,11 @@ match_unsorted_outer(PlannerInfo *root, * mergejoin using a subset of the merge clauses. Here, we consider * both cheap startup cost and cheap total cost. * + * Currently we do not consider parameterized inner paths here. + * This interacts with decisions elsewhere that also discriminate + * against mergejoins with parameterized inputs; see comments in + * src/backend/optimizer/README. + * * As we shorten the sortkey list, we should consider only paths that * are strictly cheaper than (in particular, not the same as) any path * found in an earlier iteration. Otherwise we'd be intentionally @@ -654,6 +928,7 @@ match_unsorted_outer(PlannerInfo *root, trialsortkeys = list_truncate(trialsortkeys, sortkeycnt); innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist, trialsortkeys, + NULL, TOTAL_COST); if (innerpath != NULL && (cheapest_total_inner == NULL || @@ -673,23 +948,24 @@ match_unsorted_outer(PlannerInfo *root, } else newclauses = mergeclauses; - add_path(joinrel, (Path *) - create_mergejoin_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - innerpath, - restrictlist, - merge_pathkeys, - newclauses, - NIL, - NIL)); + try_mergejoin_path(root, + joinrel, + jointype, + sjinfo, + param_source_rels, + outerpath, + innerpath, + restrictlist, + merge_pathkeys, + newclauses, + NIL, + NIL); cheapest_total_inner = innerpath; } /* Same on the basis of cheapest startup cost ... */ innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist, trialsortkeys, + NULL, STARTUP_COST); if (innerpath != NULL && (cheapest_startup_inner == NULL || @@ -717,18 +993,18 @@ match_unsorted_outer(PlannerInfo *root, else newclauses = mergeclauses; } - add_path(joinrel, (Path *) - create_mergejoin_path(root, - joinrel, - jointype, - sjinfo, - outerpath, - innerpath, - restrictlist, - merge_pathkeys, - newclauses, - NIL, - NIL)); + try_mergejoin_path(root, + joinrel, + jointype, + sjinfo, + param_source_rels, + outerpath, + innerpath, + restrictlist, + merge_pathkeys, + newclauses, + NIL, + NIL); } cheapest_startup_inner = innerpath; } @@ -754,6 +1030,8 @@ match_unsorted_outer(PlannerInfo *root, * clauses that apply to this join * 'jointype' is the type of join to do * 'sjinfo' is extra info about the join for selectivity estimation + * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'param_source_rels' are OK targets for parameterization of result paths */ static void hash_inner_and_outer(PlannerInfo *root, @@ -762,15 +1040,17 @@ hash_inner_and_outer(PlannerInfo *root, RelOptInfo *innerrel, List *restrictlist, JoinType jointype, - SpecialJoinInfo *sjinfo) + SpecialJoinInfo *sjinfo, + SemiAntiJoinFactors *semifactors, + Relids param_source_rels) { bool isouterjoin = IS_OUTER_JOIN(jointype); List *hashclauses; ListCell *l; /* - * We need to build only one hashpath for any given pair of outer and - * inner relations; all of the hashable clauses will be used as keys. + * We need to build only one hashclauses list for any given pair of outer + * and inner relations; all of the hashable clauses will be used as keys. * * Scan the join's restrictinfo list to find hashjoinable clauses that are * usable with this pair of sub-relations. @@ -800,7 +1080,7 @@ hash_inner_and_outer(PlannerInfo *root, hashclauses = lappend(hashclauses, restrictinfo); } - /* If we found any usable hashclauses, make a path */ + /* If we found any usable hashclauses, make paths */ if (hashclauses) { /* @@ -812,15 +1092,25 @@ hash_inner_and_outer(PlannerInfo *root, Path *cheapest_total_outer = outerrel->cheapest_total_path; Path *cheapest_total_inner = innerrel->cheapest_total_path; - /* Unique-ify if need be */ + /* Unique-ify if need be; we ignore parameterized possibilities */ if (jointype == JOIN_UNIQUE_OUTER) { cheapest_total_outer = (Path *) create_unique_path(root, outerrel, cheapest_total_outer, sjinfo); Assert(cheapest_total_outer); - cheapest_startup_outer = cheapest_total_outer; jointype = JOIN_INNER; + try_hashjoin_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + cheapest_total_outer, + cheapest_total_inner, + restrictlist, + hashclauses); + /* no possibility of cheap startup here */ } else if (jointype == JOIN_UNIQUE_INNER) { @@ -829,97 +1119,92 @@ hash_inner_and_outer(PlannerInfo *root, cheapest_total_inner, sjinfo); Assert(cheapest_total_inner); jointype = JOIN_INNER; + try_hashjoin_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + cheapest_total_outer, + cheapest_total_inner, + restrictlist, + hashclauses); + if (cheapest_startup_outer != cheapest_total_outer) + try_hashjoin_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + cheapest_startup_outer, + cheapest_total_inner, + restrictlist, + hashclauses); } + else + { + /* + * For other jointypes, we consider the cheapest startup outer + * together with the cheapest total inner, and then consider + * pairings of cheapest-total paths including parameterized ones. + * There is no use in generating parameterized paths on the basis + * of possibly cheap startup cost, so this is sufficient. + */ + ListCell *lc1; + ListCell *lc2; + + try_hashjoin_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + cheapest_startup_outer, + cheapest_total_inner, + restrictlist, + hashclauses); + + foreach(lc1, outerrel->cheapest_parameterized_paths) + { + Path *outerpath = (Path *) lfirst(lc1); - add_path(joinrel, (Path *) - create_hashjoin_path(root, - joinrel, - jointype, - sjinfo, - cheapest_total_outer, - cheapest_total_inner, - restrictlist, - hashclauses)); - if (cheapest_startup_outer != cheapest_total_outer) - add_path(joinrel, (Path *) - create_hashjoin_path(root, - joinrel, - jointype, - sjinfo, - cheapest_startup_outer, - cheapest_total_inner, - restrictlist, - hashclauses)); - } -} - -/* - * best_appendrel_indexscan - * Finds the best available set of inner indexscans for a nestloop join - * with the given append relation on the inside and the given outer_rel - * outside. Returns an AppendPath comprising the best inner scans, or - * NULL if there are no possible inner indexscans. - * - * Note that we currently consider only cheapest-total-cost. It's not - * very clear what cheapest-startup-cost might mean for an AppendPath. - */ -static Path * -best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel, - RelOptInfo *outer_rel, JoinType jointype) -{ - int parentRTindex = rel->relid; - List *append_paths = NIL; - bool found_indexscan = false; - ListCell *l; - - foreach(l, root->append_rel_list) - { - AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); - int childRTindex; - RelOptInfo *childrel; - Path *index_cheapest_startup; - Path *index_cheapest_total; - - /* append_rel_list contains all append rels; ignore others */ - if (appinfo->parent_relid != parentRTindex) - continue; - - childRTindex = appinfo->child_relid; - childrel = find_base_rel(root, childRTindex); - Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + /* + * We cannot use an outer path that is parameterized by the + * inner rel. + */ + if (bms_overlap(outerpath->required_outer, innerrel->relids)) + continue; - /* - * Check to see if child was rejected by constraint exclusion. If so, - * it will have a cheapest_total_path that's a "dummy" path. - */ - if (IS_DUMMY_PATH(childrel->cheapest_total_path)) - continue; /* OK, we can ignore it */ + foreach(lc2, innerrel->cheapest_parameterized_paths) + { + Path *innerpath = (Path *) lfirst(lc2); - /* - * Get the best innerjoin indexpaths (if any) for this child rel. - */ - best_inner_indexscan(root, childrel, outer_rel, jointype, - &index_cheapest_startup, &index_cheapest_total); + /* + * We cannot use an inner path that is parameterized by + * the outer rel, either. + */ + if (bms_overlap(innerpath->required_outer, + outerrel->relids)) + continue; - /* - * If no luck on an indexpath for this rel, we'll still consider an - * Append substituting the cheapest-total inner path. However we must - * find at least one indexpath, else there's not going to be any - * improvement over the base path for the appendrel. - */ - if (index_cheapest_total) - found_indexscan = true; - else - index_cheapest_total = childrel->cheapest_total_path; + if (outerpath == cheapest_startup_outer && + innerpath == cheapest_total_inner) + continue; /* already tried it */ - append_paths = lappend(append_paths, index_cheapest_total); + try_hashjoin_path(root, + joinrel, + jointype, + sjinfo, + semifactors, + param_source_rels, + outerpath, + innerpath, + restrictlist, + hashclauses); + } + } + } } - - if (!found_indexscan) - return NULL; - - /* Form and return the completed Append path. */ - return (Path *) create_append_path(rel, append_paths); } /* |