v1-0001-Retiring-is_pushed_down.patch
application/octet-stream
Filename: v1-0001-Retiring-is_pushed_down.patch
Type: application/octet-stream
Part: 0
Message:
Retiring is_pushed_down
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v1-0001
Subject: Retiring is_pushed_down
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/postgres_fdw.c | 1 | 2 |
| src/backend/optimizer/path/costsize.c | 8 | 5 |
| src/backend/optimizer/path/equivclass.c | 0 | 4 |
| src/backend/optimizer/path/joinpath.c | 11 | 3 |
| src/backend/optimizer/path/joinrels.c | 16 | 9 |
| src/backend/optimizer/plan/analyzejoins.c | 10 | 3 |
| src/backend/optimizer/plan/createplan.c | 18 | 0 |
| src/backend/optimizer/plan/initsplan.c | 10 | 52 |
| src/backend/optimizer/plan/subselect.c | 27 | 1 |
| src/backend/optimizer/util/inherit.c | 0 | 2 |
| src/backend/optimizer/util/orclauses.c | 0 | 1 |
| src/backend/optimizer/util/relnode.c | 18 | 5 |
| src/backend/optimizer/util/restrictinfo.c | 10 | 21 |
| src/include/nodes/pathnodes.h | 23 | 30 |
| src/include/optimizer/cost.h | 1 | 1 |
| src/include/optimizer/restrictinfo.h | 2 | 2 |
From 7c4f5b4625a23e69f650cfd206ad6b33adced460 Mon Sep 17 00:00:00 2001
From: pgsql-guo <richard.guo@openpie.com>
Date: Thu, 23 Mar 2023 11:53:40 +0800
Subject: [PATCH v1] Retiring is_pushed_down
When forming an outer join's joinrel, we have the is_pushed_down flag in
RestrictInfo nodes to distinguish those quals that are in that join's
JOIN/ON condition from those that were pushed down to the joinrel and
thus act as filter quals. Since now we have the outer-join-aware-Var
infrastructure, we can check to see whether a qual clause's
required_relids reference the outer join(s) being formed, in order to
tell if it's a join or filter clause. This seems like a more principled
way.
This patch is an attempt to retire the is_pushed_down flag.
---
contrib/postgres_fdw/postgres_fdw.c | 3 +-
src/backend/optimizer/path/costsize.c | 13 +++--
src/backend/optimizer/path/equivclass.c | 4 --
src/backend/optimizer/path/joinpath.c | 14 +++--
src/backend/optimizer/path/joinrels.c | 25 +++++----
src/backend/optimizer/plan/analyzejoins.c | 13 +++--
src/backend/optimizer/plan/createplan.c | 18 +++++++
src/backend/optimizer/plan/initsplan.c | 62 ++++-------------------
src/backend/optimizer/plan/subselect.c | 28 +++++++++-
src/backend/optimizer/util/inherit.c | 2 -
src/backend/optimizer/util/orclauses.c | 1 -
src/backend/optimizer/util/relnode.c | 23 +++++++--
src/backend/optimizer/util/restrictinfo.c | 31 ++++--------
src/include/nodes/pathnodes.h | 53 +++++++++----------
src/include/optimizer/cost.h | 2 +-
src/include/optimizer/restrictinfo.h | 4 +-
16 files changed, 155 insertions(+), 141 deletions(-)
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..60159e58d0 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5798,7 +5798,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;
@@ -6519,7 +6519,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 ef475d95a1..be80a8b327 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4738,7 +4738,7 @@ compute_semi_anti_join_factors(PlannerInfo *root,
JoinType jointype,
SpecialJoinInfo *sjinfo,
List *restrictlist,
- SemiAntiJoinFactors *semifactors)
+ JoinPathExtraData *extra)
{
Selectivity jselec;
Selectivity nselec;
@@ -4761,7 +4761,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);
}
}
@@ -4829,8 +4829,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;
}
/*
@@ -5195,13 +5195,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 7fa502d6e2..96a22f1389 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,
@@ -2005,7 +2004,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 */
@@ -2033,7 +2031,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 */
@@ -2061,7 +2058,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 f047ad9ba4..01f2e4fbb2 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -83,6 +83,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,
@@ -149,6 +150,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.
@@ -213,6 +217,7 @@ add_paths_to_joinrel(PlannerInfo *root,
innerrel,
restrictlist,
jointype,
+ &extra,
&mergejoin_allowed);
/*
@@ -222,7 +227,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
@@ -2090,7 +2095,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 ||
@@ -2322,6 +2328,7 @@ select_mergejoin_clauses(PlannerInfo *root,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype,
+ JoinPathExtraData *extra,
bool *mergejoin_allowed)
{
List *result_list = NIL;
@@ -2339,7 +2346,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 015a0b3cbe..b283d13bf0 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,
@@ -895,6 +896,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
@@ -917,7 +922,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;
@@ -931,12 +936,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,
@@ -948,7 +953,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;
@@ -984,7 +989,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;
@@ -1007,7 +1012,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;
@@ -1022,12 +1027,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,
@@ -1424,6 +1429,7 @@ mark_dummy_rel(RelOptInfo *rel)
static bool
restriction_is_constant_false(List *restrictlist,
RelOptInfo *joinrel,
+ Relids ojrelids,
bool only_pushed_down)
{
ListCell *lc;
@@ -1438,7 +1444,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 5f3cce873a..4c8c5d672b 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -284,7 +284,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 */
@@ -484,7 +486,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
@@ -1286,6 +1290,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
@@ -1303,7 +1310,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 af48109058..13ab1d3eeb 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4347,8 +4347,14 @@ create_nestloop_plan(PlannerInfo *root,
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
+ Relids ojrelids =
+ CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+ best_path->jpath.outerjoinpath->parent->relids,
+ best_path->jpath.innerjoinpath->parent->relids);
+
extract_actual_join_clauses(joinrestrictclauses,
best_path->jpath.path.parent->relids,
+ ojrelids,
&joinclauses, &otherclauses);
}
else
@@ -4435,8 +4441,14 @@ create_mergejoin_plan(PlannerInfo *root,
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
+ Relids ojrelids =
+ CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+ best_path->jpath.outerjoinpath->parent->relids,
+ best_path->jpath.innerjoinpath->parent->relids);
+
extract_actual_join_clauses(joinclauses,
best_path->jpath.path.parent->relids,
+ ojrelids,
&joinclauses, &otherclauses);
}
else
@@ -4737,8 +4749,14 @@ create_hashjoin_plan(PlannerInfo *root,
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
+ Relids ojrelids =
+ CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+ best_path->jpath.outerjoinpath->parent->relids,
+ best_path->jpath.innerjoinpath->parent->relids);
+
extract_actual_join_clauses(joinclauses,
best_path->jpath.path.parent->relids,
+ ojrelids,
&joinclauses, &otherclauses);
}
else
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index b31d892121..c7e0369905 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -963,19 +963,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;
@@ -2209,7 +2206,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;
@@ -2319,37 +2315,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))
{
@@ -2373,7 +2341,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;
@@ -2390,12 +2357,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
@@ -2420,7 +2381,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
*/
restrictinfo = make_restrictinfo(root,
(Expr *) clause,
- is_pushed_down,
has_clone,
is_clone,
pseudoconstant,
@@ -2792,7 +2752,6 @@ process_implied_equality(PlannerInfo *root,
*/
restrictinfo = make_restrictinfo(root,
(Expr *) clause,
- true, /* is_pushed_down */
false, /* !has_clone */
false, /* !is_clone */
pseudoconstant,
@@ -2886,7 +2845,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 7a9fe88fec..f63afec871 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1519,7 +1519,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 94de855a22..46ce5692cf 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -893,7 +893,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
childquals = lappend(childquals,
make_restrictinfo(root,
(Expr *) onecq,
- rinfo->is_pushed_down,
rinfo->has_clone,
rinfo->is_clone,
pseudoconstant,
@@ -931,7 +930,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/orclauses.c b/src/backend/optimizer/util/orclauses.c
index 6ef9d14b90..bc0ce171cc 100644
--- a/src/backend/optimizer/util/orclauses.c
+++ b/src/backend/optimizer/util/orclauses.c
@@ -265,7 +265,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/relnode.c b/src/backend/optimizer/util/relnode.c
index 76dad17e33..9542954f0c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -57,6 +57,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,
@@ -1286,9 +1287,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
@@ -1328,6 +1329,7 @@ subbuild_joinrel_restrictlist(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *input_rel,
Relids both_input_relids,
+ SpecialJoinInfo *sjinfo,
List *new_restrictlist)
{
ListCell *l;
@@ -1349,11 +1351,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
{
@@ -1364,7 +1370,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));
}
@@ -2059,6 +2069,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
@@ -2079,7 +2092,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 d6d26a2b51..75945c3ab9 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 c17b53f7ad..d1dc27be6f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2408,27 +2408,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
@@ -2515,9 +2504,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);
@@ -2661,16 +2647,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,
@@ -3170,6 +3160,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
{
@@ -3179,6 +3171,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 6cf49705d3..11bdd120c2 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 e140e619ac..04658f0067 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);
--
2.32.0