diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index fc65d81e2177..1cce9cb430ae 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -5857,7 +5857,7 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, rinfo->clause); if (IS_OUTER_JOIN(jointype) && - !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + !RINFO_IS_PUSHED_DOWN(rinfo, extra->ojrelids, joinrel->relids)) { if (!is_remote_clause) return false; @@ -6619,7 +6619,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Assert(!IsA(expr, RestrictInfo)); rinfo = make_restrictinfo(root, expr, - true, false, false, false, diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 79991b19807b..a3106f96e3a5 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -4972,7 +4972,7 @@ get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, * sjinfo: SpecialJoinInfo relevant to this join * restrictlist: join quals * Output parameters: - * *semifactors is filled in (see pathnodes.h for field definitions) + * extra->semifactors is filled in (see pathnodes.h for field definitions) */ void compute_semi_anti_join_factors(PlannerInfo *root, @@ -4982,7 +4982,7 @@ compute_semi_anti_join_factors(PlannerInfo *root, JoinType jointype, SpecialJoinInfo *sjinfo, List *restrictlist, - SemiAntiJoinFactors *semifactors) + JoinPathExtraData *extra) { Selectivity jselec; Selectivity nselec; @@ -5005,7 +5005,7 @@ compute_semi_anti_join_factors(PlannerInfo *root, { RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); - if (!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + if (!RINFO_IS_PUSHED_DOWN(rinfo, extra->ojrelids, joinrel->relids)) joinquals = lappend(joinquals, rinfo); } } @@ -5057,8 +5057,8 @@ compute_semi_anti_join_factors(PlannerInfo *root, else avgmatch = 1.0; - semifactors->outer_match_frac = jselec; - semifactors->match_count = avgmatch; + extra->semifactors.outer_match_frac = jselec; + extra->semifactors.match_count = avgmatch; } /* @@ -5408,13 +5408,16 @@ calc_joinrel_size_estimate(PlannerInfo *root, List *joinquals = NIL; List *pushedquals = NIL; ListCell *l; + Relids ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids, + outer_rel->relids, + inner_rel->relids); /* Grovel through the clauses to separate into two lists */ foreach(l, restrictlist) { RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); - if (RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + if (RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids)) pushedquals = lappend(pushedquals, rinfo); else joinquals = lappend(joinquals, rinfo); diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 51d806326eba..2507427f8f5e 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -196,7 +196,6 @@ process_equivalence(PlannerInfo *root, *p_restrictinfo = make_restrictinfo(root, (Expr *) ntest, - restrictinfo->is_pushed_down, restrictinfo->has_clone, restrictinfo->is_clone, restrictinfo->pseudoconstant, @@ -2020,7 +2019,6 @@ reconsider_outer_join_clauses(PlannerInfo *root) /* throw back a dummy replacement clause (see notes above) */ rinfo = make_restrictinfo(root, (Expr *) makeBoolConst(true, false), - rinfo->is_pushed_down, rinfo->has_clone, rinfo->is_clone, false, /* pseudoconstant */ @@ -2048,7 +2046,6 @@ reconsider_outer_join_clauses(PlannerInfo *root) /* throw back a dummy replacement clause (see notes above) */ rinfo = make_restrictinfo(root, (Expr *) makeBoolConst(true, false), - rinfo->is_pushed_down, rinfo->has_clone, rinfo->is_clone, false, /* pseudoconstant */ @@ -2076,7 +2073,6 @@ reconsider_outer_join_clauses(PlannerInfo *root) /* throw back a dummy replacement clause (see notes above) */ rinfo = make_restrictinfo(root, (Expr *) makeBoolConst(true, false), - rinfo->is_pushed_down, rinfo->has_clone, rinfo->is_clone, false, /* pseudoconstant */ diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 7a2c20b14507..60907843c3e6 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -85,6 +85,7 @@ static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *innerrel, List *restrictlist, JoinType jointype, + JoinPathExtraData *extra, bool *mergejoin_allowed); static void generate_mergejoin_paths(PlannerInfo *root, RelOptInfo *joinrel, @@ -151,6 +152,9 @@ add_paths_to_joinrel(PlannerInfo *root, extra.mergeclause_list = NIL; extra.sjinfo = sjinfo; extra.param_source_rels = NULL; + extra.ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids, + outerrel->relids, + innerrel->relids); /* * See if the inner relation is provably unique for this outer rel. @@ -215,6 +219,7 @@ add_paths_to_joinrel(PlannerInfo *root, innerrel, restrictlist, jointype, + &extra, &mergejoin_allowed); /* @@ -224,7 +229,7 @@ add_paths_to_joinrel(PlannerInfo *root, if (jointype == JOIN_SEMI || jointype == JOIN_ANTI || extra.inner_unique) compute_semi_anti_join_factors(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist, - &extra.semifactors); + &extra); /* * Decide whether it's sensible to generate parameterized paths for this @@ -2254,7 +2259,8 @@ hash_inner_and_outer(PlannerInfo *root, * If processing an outer join, only use its own join clauses for * hashing. For inner joins we need not be so picky. */ - if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids)) + if (isouterjoin && + RINFO_IS_PUSHED_DOWN(restrictinfo, extra->ojrelids, joinrel->relids)) continue; if (!restrictinfo->can_join || @@ -2487,6 +2493,7 @@ select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *innerrel, List *restrictlist, JoinType jointype, + JoinPathExtraData *extra, bool *mergejoin_allowed) { List *result_list = NIL; @@ -2514,7 +2521,8 @@ select_mergejoin_clauses(PlannerInfo *root, * we don't set have_nonmergeable_joinclause here because pushed-down * clauses will become otherquals not joinquals.) */ - if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids)) + if (isouterjoin && + RINFO_IS_PUSHED_DOWN(restrictinfo, extra->ojrelids, joinrel->relids)) continue; /* Check that clause is a mergeable operator clause */ diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 7db5e30eef8c..7e93aea1d2ab 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -34,6 +34,7 @@ static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel); static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel); static bool restriction_is_constant_false(List *restrictlist, RelOptInfo *joinrel, + Relids ojrelids, bool only_pushed_down); static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, RelOptInfo *joinrel, @@ -894,6 +895,10 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, RelOptInfo *joinrel, SpecialJoinInfo *sjinfo, List *restrictlist) { + Relids ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids, + rel1->relids, + rel2->relids); + /* * Consider paths using each rel as both outer and inner. Depending on * the join type, a provably empty outer or inner rel might mean the join @@ -916,7 +921,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, { case JOIN_INNER: if (is_dummy_rel(rel1) || is_dummy_rel(rel2) || - restriction_is_constant_false(restrictlist, joinrel, false)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, false)) { mark_dummy_rel(joinrel); break; @@ -930,12 +935,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, break; case JOIN_LEFT: if (is_dummy_rel(rel1) || - restriction_is_constant_false(restrictlist, joinrel, true)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, true)) { mark_dummy_rel(joinrel); break; } - if (restriction_is_constant_false(restrictlist, joinrel, false) && + if (restriction_is_constant_false(restrictlist, joinrel, ojrelids, false) && bms_is_subset(rel2->relids, sjinfo->syn_righthand)) mark_dummy_rel(rel2); add_paths_to_joinrel(root, joinrel, rel1, rel2, @@ -947,7 +952,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, break; case JOIN_FULL: if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) || - restriction_is_constant_false(restrictlist, joinrel, true)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, true)) { mark_dummy_rel(joinrel); break; @@ -983,7 +988,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, bms_is_subset(sjinfo->min_righthand, rel2->relids)) { if (is_dummy_rel(rel1) || is_dummy_rel(rel2) || - restriction_is_constant_false(restrictlist, joinrel, false)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, false)) { mark_dummy_rel(joinrel); break; @@ -1009,7 +1014,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, sjinfo) != NULL) { if (is_dummy_rel(rel1) || is_dummy_rel(rel2) || - restriction_is_constant_false(restrictlist, joinrel, false)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, false)) { mark_dummy_rel(joinrel); break; @@ -1024,12 +1029,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, break; case JOIN_ANTI: if (is_dummy_rel(rel1) || - restriction_is_constant_false(restrictlist, joinrel, true)) + restriction_is_constant_false(restrictlist, joinrel, ojrelids, true)) { mark_dummy_rel(joinrel); break; } - if (restriction_is_constant_false(restrictlist, joinrel, false) && + if (restriction_is_constant_false(restrictlist, joinrel, ojrelids, false) && bms_is_subset(rel2->relids, sjinfo->syn_righthand)) mark_dummy_rel(rel2); add_paths_to_joinrel(root, joinrel, rel1, rel2, @@ -1426,6 +1431,7 @@ mark_dummy_rel(RelOptInfo *rel) static bool restriction_is_constant_false(List *restrictlist, RelOptInfo *joinrel, + Relids ojrelids, bool only_pushed_down) { ListCell *lc; @@ -1440,7 +1446,8 @@ restriction_is_constant_false(List *restrictlist, { RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); - if (only_pushed_down && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + if (only_pushed_down && + !RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids)) continue; if (rinfo->clause && IsA(rinfo->clause, Const)) diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index c3fd4a81f8a6..3166525df30f 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -282,7 +282,9 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo) * above the outer join, even if it references no other rels (it might * be from WHERE, for example). */ - if (RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids)) + if (RINFO_IS_PUSHED_DOWN(restrictinfo, + bms_make_singleton(sjinfo->ojrelid), + joinrelids)) continue; /* ignore; not useful here */ /* Ignore if it's not a mergejoinable clause */ @@ -493,7 +495,9 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo) remove_join_clause_from_rels(root, rinfo, rinfo->required_relids); - if (RINFO_IS_PUSHED_DOWN(rinfo, join_plus_commute)) + if (RINFO_IS_PUSHED_DOWN(rinfo, + bms_make_singleton(sjinfo->ojrelid), + join_plus_commute)) { /* * There might be references to relid or ojrelid in the @@ -1298,6 +1302,9 @@ is_innerrel_unique_for(PlannerInfo *root, { List *clause_list = NIL; ListCell *lc; + Relids ojrelids = CALC_OUTER_JOIN_RELIDS(joinrelids, + outerrelids, + innerrel->relids); /* * Search for mergejoinable clauses that constrain the inner rel against @@ -1315,7 +1322,7 @@ is_innerrel_unique_for(PlannerInfo *root, * join, we can't use it. */ if (IS_OUTER_JOIN(jointype) && - RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids)) + RINFO_IS_PUSHED_DOWN(restrictinfo, ojrelids, joinrelids)) continue; /* Ignore if it's not a mergejoinable clause */ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index fe5a323cfd7f..5e629b8464a7 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4391,6 +4391,7 @@ create_nestloop_plan(PlannerInfo *root, { extract_actual_join_clauses(joinrestrictclauses, best_path->jpath.path.parent->relids, + best_path->jpath.ojrelids, &joinclauses, &otherclauses); } else @@ -4479,6 +4480,7 @@ create_mergejoin_plan(PlannerInfo *root, { extract_actual_join_clauses(joinclauses, best_path->jpath.path.parent->relids, + best_path->jpath.ojrelids, &joinclauses, &otherclauses); } else @@ -4781,6 +4783,7 @@ create_hashjoin_plan(PlannerInfo *root, { extract_actual_join_clauses(joinclauses, best_path->jpath.path.parent->relids, + best_path->jpath.ojrelids, &joinclauses, &otherclauses); } else diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e2c68fe6f995..d4353ff56aa0 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -962,19 +962,16 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, child_domain->jd_relids); jtitem->qualscope = bms_union(left_item->qualscope, right_item->qualscope); - /* caution: ANTI join derived from SEMI will lack rtindex */ - if (j->rtindex != 0) - { - parent_domain->jd_relids = - bms_add_member(parent_domain->jd_relids, - j->rtindex); - jtitem->qualscope = bms_add_member(jtitem->qualscope, + Assert(j->rtindex != 0); + parent_domain->jd_relids = + bms_add_member(parent_domain->jd_relids, + j->rtindex); + jtitem->qualscope = bms_add_member(jtitem->qualscope, + j->rtindex); + root->outer_join_rels = bms_add_member(root->outer_join_rels, j->rtindex); - root->outer_join_rels = bms_add_member(root->outer_join_rels, - j->rtindex); - mark_rels_nulled_by_join(root, j->rtindex, - right_item->qualscope); - } + mark_rels_nulled_by_join(root, j->rtindex, + right_item->qualscope); jtitem->inner_join_rels = bms_union(left_item->inner_join_rels, right_item->inner_join_rels); jtitem->left_rels = left_item->qualscope; @@ -2208,7 +2205,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, List **postponed_oj_qual_list) { Relids relids; - bool is_pushed_down; bool pseudoconstant = false; bool maybe_equivalence; bool maybe_outer_join; @@ -2318,37 +2314,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, } } - /*---------- + /* * Check to see if clause application must be delayed by outer-join * considerations. - * - * A word about is_pushed_down: we mark the qual as "pushed down" if - * it is (potentially) applicable at a level different from its original - * syntactic level. This flag is used to distinguish OUTER JOIN ON quals - * from other quals pushed down to the same joinrel. The rules are: - * WHERE quals and INNER JOIN quals: is_pushed_down = true. - * Non-degenerate OUTER JOIN quals: is_pushed_down = false. - * Degenerate OUTER JOIN quals: is_pushed_down = true. - * A "degenerate" OUTER JOIN qual is one that doesn't mention the - * non-nullable side, and hence can be pushed down into the nullable side - * without changing the join result. It is correct to treat it as a - * regular filter condition at the level where it is evaluated. - * - * Note: it is not immediately obvious that a simple boolean is enough - * for this: if for some reason we were to attach a degenerate qual to - * its original join level, it would need to be treated as an outer join - * qual there. However, this cannot happen, because all the rels the - * clause mentions must be in the outer join's min_righthand, therefore - * the join it needs must be formed before the outer join; and we always - * attach quals to the lowest level where they can be evaluated. But - * if we were ever to re-introduce a mechanism for delaying evaluation - * of "expensive" quals, this area would need work. - * - * Note: generally, use of is_pushed_down has to go through the macro - * RINFO_IS_PUSHED_DOWN, because that flag alone is not always sufficient - * to tell whether a clause must be treated as pushed-down in context. - * This seems like another reason why it should perhaps be rethought. - *---------- */ if (bms_overlap(relids, outerjoin_nonnullable)) { @@ -2372,7 +2340,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, * deductions, if it is mergejoinable. So consider adding it to the * lists of set-aside outer-join clauses. */ - is_pushed_down = false; maybe_equivalence = false; maybe_outer_join = true; @@ -2389,12 +2356,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, } else { - /* - * Normal qual clause or degenerate outer-join clause. Either way, we - * can mark it as pushed-down. - */ - is_pushed_down = true; - /* * It's possible that this is an IS NULL clause that's redundant with * a lower antijoin; if so we can just discard it. We need not test @@ -2419,7 +2380,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, */ restrictinfo = make_restrictinfo(root, (Expr *) clause, - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -2665,7 +2625,6 @@ add_base_clause_to_rel(PlannerInfo *root, Index relid, restrictinfo = make_restrictinfo(root, (Expr *) makeBoolConst(false, false), - restrictinfo->is_pushed_down, restrictinfo->has_clone, restrictinfo->is_clone, restrictinfo->pseudoconstant, @@ -2991,7 +2950,6 @@ process_implied_equality(PlannerInfo *root, */ restrictinfo = make_restrictinfo(root, (Expr *) clause, - true, /* is_pushed_down */ false, /* !has_clone */ false, /* !is_clone */ pseudoconstant, @@ -3085,7 +3043,6 @@ build_implied_join_equality(PlannerInfo *root, */ restrictinfo = make_restrictinfo(root, clause, - true, /* is_pushed_down */ false, /* !has_clone */ false, /* !is_clone */ false, /* pseudoconstant */ diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 6d003cc8e5cb..984d8e509a21 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1515,7 +1515,33 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, result->join_using_alias = NULL; result->quals = whereClause; result->alias = NULL; - result->rtindex = 0; /* we don't need an RTE for it */ + result->rtindex = 0; /* we don't need an RTE for JOIN_SEMI */ + + /* + * Add a RTE for JOIN_ANTI + */ + if (result->jointype == JOIN_ANTI) + { + ParseNamespaceItem *jnsitem; + ParseState *pstate; + + /* Create a dummy ParseState for addRangeTableEntryForJoin */ + pstate = make_parsestate(NULL); + + jnsitem = addRangeTableEntryForJoin(pstate, + NULL, + NULL, + JOIN_ANTI, + 0, + NULL, + NIL, + NIL, + NULL, + NULL, + false); + parse->rtable = lappend(parse->rtable, jnsitem->p_rte); + result->rtindex = list_length(parse->rtable); + } return result; } diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 4797312ae53e..3050bf5e3713 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -891,7 +891,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, /* reconstitute RestrictInfo with appropriate properties */ childrinfo = make_restrictinfo(root, (Expr *) onecq, - rinfo->is_pushed_down, rinfo->has_clone, rinfo->is_clone, pseudoconstant, @@ -938,7 +937,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, /* not likely that we'd see constants here, so no check */ childquals = lappend(childquals, make_restrictinfo(root, qual, - true, false, false, false, security_level, diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c index 5fb0c17630a1..173aff761b79 100644 --- a/src/backend/optimizer/util/joininfo.c +++ b/src/backend/optimizer/util/joininfo.c @@ -115,7 +115,6 @@ add_join_clause_to_rels(PlannerInfo *root, restrictinfo = make_restrictinfo(root, (Expr *) makeBoolConst(false, false), - restrictinfo->is_pushed_down, restrictinfo->has_clone, restrictinfo->is_clone, restrictinfo->pseudoconstant, diff --git a/src/backend/optimizer/util/orclauses.c b/src/backend/optimizer/util/orclauses.c index 5e2bf26ec43d..35500e40b972 100644 --- a/src/backend/optimizer/util/orclauses.c +++ b/src/backend/optimizer/util/orclauses.c @@ -264,7 +264,6 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel, */ or_rinfo = make_restrictinfo(root, orclause, - true, false, false, false, diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 54e042a8a59c..f48df407a87f 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -2517,6 +2517,7 @@ create_nestloop_path(PlannerInfo *root, pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->jpath.ojrelids = extra->ojrelids; final_cost_nestloop(root, pathnode, workspace, extra); @@ -2581,6 +2582,7 @@ create_mergejoin_path(PlannerInfo *root, pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->jpath.ojrelids = extra->ojrelids; pathnode->path_mergeclauses = mergeclauses; pathnode->outersortkeys = outersortkeys; pathnode->innersortkeys = innersortkeys; @@ -2658,6 +2660,7 @@ create_hashjoin_path(PlannerInfo *root, pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->jpath.ojrelids = extra->ojrelids; pathnode->path_hashclauses = hashclauses; /* final_cost_hashjoin will fill in pathnode->num_batches */ diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 971d1c7aae50..bfd83401334c 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -58,6 +58,7 @@ static List *subbuild_joinrel_restrictlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *input_rel, Relids both_input_relids, + SpecialJoinInfo *sjinfo, List *new_restrictlist); static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel, List *joininfo_list, @@ -1299,9 +1300,9 @@ build_joinrel_restrictlist(PlannerInfo *root, * same clauses arriving from both input relations). */ result = subbuild_joinrel_restrictlist(root, joinrel, outer_rel, - both_input_relids, NIL); + both_input_relids, sjinfo, NIL); result = subbuild_joinrel_restrictlist(root, joinrel, inner_rel, - both_input_relids, result); + both_input_relids, sjinfo, result); /* * Add on any clauses derived from EquivalenceClasses. These cannot be @@ -1341,6 +1342,7 @@ subbuild_joinrel_restrictlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *input_rel, Relids both_input_relids, + SpecialJoinInfo *sjinfo, List *new_restrictlist) { ListCell *l; @@ -1362,11 +1364,15 @@ subbuild_joinrel_restrictlist(PlannerInfo *root, */ if (rinfo->has_clone || rinfo->is_clone) { - Assert(!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)); if (!bms_is_subset(rinfo->required_relids, both_input_relids)) continue; if (bms_overlap(rinfo->incompatible_relids, both_input_relids)) continue; + + Assert(!RINFO_IS_PUSHED_DOWN(rinfo, + bms_difference(joinrel->relids, + both_input_relids), + joinrel->relids)); } else { @@ -1377,7 +1383,11 @@ subbuild_joinrel_restrictlist(PlannerInfo *root, * (There is little point in checking incompatible_relids, * because it'll be NULL.) */ - Assert(RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids) || + Assert(sjinfo->ojrelid == 0 || + RINFO_IS_PUSHED_DOWN(rinfo, + bms_difference(joinrel->relids, + both_input_relids), + joinrel->relids) || bms_is_subset(rinfo->required_relids, both_input_relids)); } @@ -2084,6 +2094,9 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel, int cnt_pks; bool pk_has_clause[PARTITION_MAX_KEYS]; bool strict_op; + Relids ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids, + rel1->relids, + rel2->relids); /* * This function must only be called when the joined relations have same @@ -2104,7 +2117,7 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel, /* If processing an outer join, only use its own join clauses. */ if (IS_OUTER_JOIN(jointype) && - RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) + RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids)) continue; /* Skip clauses which can not be used for a join. */ diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c index 0b406e93342d..5d05287bf3cb 100644 --- a/src/backend/optimizer/util/restrictinfo.c +++ b/src/backend/optimizer/util/restrictinfo.c @@ -24,7 +24,6 @@ static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root, Expr *clause, Expr *orclause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -34,7 +33,6 @@ static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root, Relids outer_relids); static Expr *make_sub_restrictinfos(PlannerInfo *root, Expr *clause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -49,11 +47,11 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root, * * Build a RestrictInfo node containing the given subexpression. * - * The is_pushed_down, has_clone, is_clone, and pseudoconstant flags for the - * RestrictInfo must be supplied by the caller, as well as the correct values - * for security_level, incompatible_relids, and outer_relids. - * required_relids can be NULL, in which case it defaults to the actual clause - * contents (i.e., clause_relids). + * The has_clone, is_clone, and pseudoconstant flags for the RestrictInfo + * must be supplied by the caller, as well as the correct values for + * security_level, incompatible_relids, and outer_relids. required_relids + * can be NULL, in which case it defaults to the actual clause contents + * (i.e., clause_relids). * * We initialize fields that depend only on the given subexpression, leaving * others that depend on context (or may never be needed at all) to be filled @@ -62,7 +60,6 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root, RestrictInfo * make_restrictinfo(PlannerInfo *root, Expr *clause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -78,7 +75,6 @@ make_restrictinfo(PlannerInfo *root, if (is_orclause(clause)) return (RestrictInfo *) make_sub_restrictinfos(root, clause, - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -93,7 +89,6 @@ make_restrictinfo(PlannerInfo *root, return make_restrictinfo_internal(root, clause, NULL, - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -112,7 +107,6 @@ static RestrictInfo * make_restrictinfo_internal(PlannerInfo *root, Expr *clause, Expr *orclause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -126,7 +120,6 @@ make_restrictinfo_internal(PlannerInfo *root, restrictinfo->clause = clause; restrictinfo->orclause = orclause; - restrictinfo->is_pushed_down = is_pushed_down; restrictinfo->pseudoconstant = pseudoconstant; restrictinfo->has_clone = has_clone; restrictinfo->is_clone = is_clone; @@ -259,9 +252,9 @@ make_restrictinfo_internal(PlannerInfo *root, * implicit-AND lists at top level of RestrictInfo lists. Only ORs and * simple clauses are valid RestrictInfos. * - * The same is_pushed_down, has_clone, is_clone, and pseudoconstant flag - * values can be applied to all RestrictInfo nodes in the result. Likewise - * for security_level, incompatible_relids, and outer_relids. + * The same has_clone, is_clone, and pseudoconstant flag values can be + * applied to all RestrictInfo nodes in the result. Likewise for + * security_level, incompatible_relids, and outer_relids. * * The given required_relids are attached to our top-level output, * but any OR-clause constituents are allowed to default to just the @@ -270,7 +263,6 @@ make_restrictinfo_internal(PlannerInfo *root, static Expr * make_sub_restrictinfos(PlannerInfo *root, Expr *clause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -288,7 +280,6 @@ make_sub_restrictinfos(PlannerInfo *root, orlist = lappend(orlist, make_sub_restrictinfos(root, lfirst(temp), - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -299,7 +290,6 @@ make_sub_restrictinfos(PlannerInfo *root, return (Expr *) make_restrictinfo_internal(root, clause, make_orclause(orlist), - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -317,7 +307,6 @@ make_sub_restrictinfos(PlannerInfo *root, andlist = lappend(andlist, make_sub_restrictinfos(root, lfirst(temp), - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -331,7 +320,6 @@ make_sub_restrictinfos(PlannerInfo *root, return (Expr *) make_restrictinfo_internal(root, clause, NULL, - is_pushed_down, has_clone, is_clone, pseudoconstant, @@ -521,6 +509,7 @@ extract_actual_clauses(List *restrictinfo_list, void extract_actual_join_clauses(List *restrictinfo_list, Relids joinrelids, + Relids ojrelids, List **joinquals, List **otherquals) { @@ -533,7 +522,7 @@ extract_actual_join_clauses(List *restrictinfo_list, { RestrictInfo *rinfo = lfirst_node(RestrictInfo, l); - if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)) + if (RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrelids)) { if (!rinfo->pseudoconstant && !rinfo_is_constant_true(rinfo)) diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 14ccfc1ac1c7..bb688ca91d9d 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2081,6 +2081,10 @@ typedef struct JoinPath * joinrestrictinfo is needed in JoinPath, and can't be merged into the * parent RelOptInfo. */ + + Relids ojrelids; /* the relid set of any outer joins that will be + * calculated at this join for outer join, or NULL + * for inner join */ } JoinPath; /* @@ -2456,27 +2460,16 @@ typedef struct LimitPath * level of the outer join, which is true except when it is a "degenerate" * condition that references only Vars from the nullable side of the join. * - * RestrictInfo nodes contain a flag to indicate whether a qual has been - * pushed down to a lower level than its original syntactic placement in the - * join tree would suggest. If an outer join prevents us from pushing a qual - * down to its "natural" semantic level (the level associated with just the - * base rels used in the qual) then we mark the qual with a "required_relids" - * value including more than just the base rels it actually uses. By - * pretending that the qual references all the rels required to form the outer - * join, we prevent it from being evaluated below the outer join's joinrel. - * When we do form the outer join's joinrel, we still need to distinguish - * those quals that are actually in that join's JOIN/ON condition from those - * that appeared elsewhere in the tree and were pushed down to the join rel - * because they used no other rels. That's what the is_pushed_down flag is - * for; it tells us that a qual is not an OUTER JOIN qual for the set of base - * rels listed in required_relids. A clause that originally came from WHERE - * or an INNER JOIN condition will *always* have its is_pushed_down flag set. - * It's possible for an OUTER JOIN clause to be marked is_pushed_down too, - * if we decide that it can be pushed down into the nullable side of the join. - * In that case it acts as a plain filter qual for wherever it gets evaluated. - * (In short, is_pushed_down is only false for non-degenerate outer join - * conditions. Possibly we should rename it to reflect that meaning? But - * see also the comments for RINFO_IS_PUSHED_DOWN, below.) + * If an outer join prevents us from pushing a qual down to its "natural" + * semantic level (the level associated with just the base rels used in the + * qual) then we mark the qual with a "required_relids" value including more + * than just the base rels it actually uses. By pretending that the qual + * references all the rels required to form the outer join, we prevent it from + * being evaluated below the outer join's joinrel. When we do form the outer + * join's joinrel, we still need to distinguish those quals that are actually + * in that join's JOIN/ON condition from those that appeared elsewhere in the + * tree and were pushed down to the join rel because they used no other rels. + * See the comments for RINFO_IS_PUSHED_DOWN for how we do that. * * There is also an incompatible_relids field, which is a set of outer-join * relids above which we cannot evaluate the clause (because they might null @@ -2563,9 +2556,6 @@ typedef struct RestrictInfo /* the represented clause of WHERE or JOIN */ Expr *clause; - /* true if clause was pushed down in level */ - bool is_pushed_down; - /* see comment above */ bool can_join pg_node_attr(equal_ignore); @@ -2712,16 +2702,20 @@ typedef struct RestrictInfo * This macro embodies the correct way to test whether a RestrictInfo is * "pushed down" to a given outer join, that is, should be treated as a filter * clause rather than a join clause at that outer join. This is certainly so - * if is_pushed_down is true; but examining that is not sufficient anymore, - * because outer-join clauses will get pushed down to lower outer joins when - * we generate a path for the lower outer join that is parameterized by the - * LHS of the upper one. We can detect such a clause by noting that its + * if the outer join clause references that outer join in any varnullingrels or + * phnullingrels set; but examining that is not sufficient anymore, because + * outer-join clauses will get pushed down to lower outer joins when we + * generate a path for the lower outer join that is parameterized by the LHS of + * the upper one. We can detect such a clause by noting that its * required_relids exceed the scope of the join. */ -#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \ - ((rinfo)->is_pushed_down || \ +#define RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrelids) \ + (bms_overlap((rinfo)->required_relids, (ojrelids)) || \ !bms_is_subset((rinfo)->required_relids, joinrelids)) +#define CALC_OUTER_JOIN_RELIDS(joinrelids, outerrelids, innerrelids) \ + (bms_difference((joinrelids), bms_union((outerrelids), (innerrelids)))) + /* * Since mergejoinscansel() is a relatively expensive function, and would * otherwise be invoked many times while planning a large join tree, @@ -3224,6 +3218,8 @@ typedef struct SemiAntiJoinFactors * sjinfo is extra info about special joins for selectivity estimation * semifactors is as shown above (only valid for SEMI/ANTI/inner_unique joins) * param_source_rels are OK targets for parameterization of result paths + * ojrelids is the relid set of any outer joins that will be calculated at this + * join, or NULL for inner join */ typedef struct JoinPathExtraData { @@ -3233,6 +3229,7 @@ typedef struct JoinPathExtraData SpecialJoinInfo *sjinfo; SemiAntiJoinFactors semifactors; Relids param_source_rels; + Relids ojrelids; } JoinPathExtraData; /* diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 57861bfb4460..7e707e0183fd 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -183,7 +183,7 @@ extern void compute_semi_anti_join_factors(PlannerInfo *root, JoinType jointype, SpecialJoinInfo *sjinfo, List *restrictlist, - SemiAntiJoinFactors *semifactors); + JoinPathExtraData *extra); extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel); extern double get_parameterized_baserel_size(PlannerInfo *root, RelOptInfo *rel, diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 1b42c832c592..295ce764d343 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -19,12 +19,11 @@ /* Convenience macro for the common case of a valid-everywhere qual */ #define make_simple_restrictinfo(root, clause) \ - make_restrictinfo(root, clause, true, false, false, false, 0, \ + make_restrictinfo(root, clause, false, false, false, 0, \ NULL, NULL, NULL) extern RestrictInfo *make_restrictinfo(PlannerInfo *root, Expr *clause, - bool is_pushed_down, bool has_clone, bool is_clone, bool pseudoconstant, @@ -41,6 +40,7 @@ extern List *extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant); extern void extract_actual_join_clauses(List *restrictinfo_list, Relids joinrelids, + Relids ojrelids, List **joinquals, List **otherquals); extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel);