aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2023-05-25 10:28:33 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2023-05-25 10:28:33 -0400
commit991a3df227e9e8b16d7399df3961dfaae4ae677c (patch)
tree4b2fc9552e81e3d0c659df6970f9e2c4a631d767 /src/backend/optimizer/plan/initsplan.c
parent913b3da6aeda3f887b8796d8098d7227d32580b9 (diff)
downloadpostgresql-991a3df227e9e8b16d7399df3961dfaae4ae677c.tar.gz
postgresql-991a3df227e9e8b16d7399df3961dfaae4ae677c.zip
Fix filtering of "cloned" outer-join quals some more.
We've had multiple issues with the clause_is_computable_at logic that I introduced in 2489d76c4: it's been known to accept more than one clone of the same qual at the same plan node, and also to accept no clones at all. It's looking impractical to get it 100% right on the basis of the currently-stored information, so fix it by introducing a new RestrictInfo field "incompatible_relids" that explicitly shows which outer joins a given clone mustn't be pushed above. In principle we could populate this field in every RestrictInfo, but that would cost space and there doesn't presently seem to be a need for it in general. Also, while deconstruct_distribute_oj_quals can easily fill the field with the remaining members of the commutative join set that it's considering, computing it in the general case seems again pretty complicated. So for now, just fill it for clone quals. Along the way, fix a bug that may or may not be only latent: equivclass.c was generating replacement clauses with is_pushed_down and has_clone/is_clone markings that didn't match their required_relids. This led me to conclude that leaving the clone flags out of make_restrictinfo's purview wasn't such a great idea after all, so add them. Per report from Richard Guo. Discussion: https://postgr.es/m/CAMbWs48EYi_9-pSd0ORes1kTmTeAjT4Q3gu49hJtYCbSn2JyeA@mail.gmail.com
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c55
1 files changed, 47 insertions, 8 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 5cbb7b9a86b..69ef483d283 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -109,6 +109,7 @@ static void distribute_quals_to_rels(PlannerInfo *root, List *clauses,
Relids qualscope,
Relids ojscope,
Relids outerjoin_nonnullable,
+ Relids incompatible_relids,
bool allow_equivalence,
bool has_clone,
bool is_clone,
@@ -120,6 +121,7 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope,
Relids ojscope,
Relids outerjoin_nonnullable,
+ Relids incompatible_relids,
bool allow_equivalence,
bool has_clone,
bool is_clone,
@@ -1132,7 +1134,8 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem)
jtitem,
NULL,
root->qual_security_level,
- jtitem->qualscope, NULL, NULL,
+ jtitem->qualscope,
+ NULL, NULL, NULL,
true, false, false,
NULL);
@@ -1143,7 +1146,8 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem)
jtitem,
NULL,
root->qual_security_level,
- jtitem->qualscope, NULL, NULL,
+ jtitem->qualscope,
+ NULL, NULL, NULL,
true, false, false,
NULL);
}
@@ -1226,6 +1230,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem)
root->qual_security_level,
jtitem->qualscope,
ojscope, jtitem->nonnullable_rels,
+ NULL, /* incompatible_relids */
true, /* allow_equivalence */
false, false, /* not clones */
postponed_oj_qual_list);
@@ -1285,6 +1290,7 @@ process_security_barrier_quals(PlannerInfo *root,
jtitem->qualscope,
jtitem->qualscope,
NULL,
+ NULL,
true,
false, false, /* not clones */
NULL);
@@ -1887,6 +1893,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
{
Relids joins_above;
Relids joins_below;
+ Relids incompatible_joins;
Relids joins_so_far;
List *quals;
int save_last_rinfo_serial;
@@ -1921,6 +1928,15 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
NULL);
/*
+ * We'll need to mark the lower versions of the quals as not safe to
+ * apply above not-yet-processed joins of the stack. This prevents
+ * possibly applying a cloned qual at the wrong join level.
+ */
+ incompatible_joins = bms_union(joins_below, joins_above);
+ incompatible_joins = bms_add_member(incompatible_joins,
+ sjinfo->ojrelid);
+
+ /*
* Each time we produce RestrictInfo(s) from these quals, reset the
* last_rinfo_serial counter, so that the RestrictInfos for the "same"
* qual condition get identical serial numbers. (This relies on the
@@ -1979,13 +1995,19 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
* relation B will appear nulled by the syntactically-upper OJ
* within the Pbc clause, but those of relation C will not. (In
* the notation used by optimizer/README, we're converting a qual
- * of the form Pbc to Pb*c.)
+ * of the form Pbc to Pb*c.) Of course, we must also remove that
+ * bit from the incompatible_joins value, else we'll make a qual
+ * that can't be placed anywhere.
*/
if (above_sjinfo)
+ {
quals = (List *)
add_nulling_relids((Node *) quals,
sjinfo->syn_lefthand,
bms_make_singleton(othersj->ojrelid));
+ incompatible_joins = bms_del_member(incompatible_joins,
+ othersj->ojrelid);
+ }
/* Compute qualscope and ojscope for this join level */
this_qualscope = bms_union(qualscope, joins_so_far);
@@ -2027,6 +2049,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
root->qual_security_level,
this_qualscope,
this_ojscope, nonnullable_rels,
+ bms_copy(incompatible_joins),
allow_equivalence,
has_clone,
is_clone,
@@ -2039,13 +2062,17 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
* Vars coming from the lower join's RHS. (Again, we are
* converting a qual of the form Pbc to Pb*c, but now we are
* putting back bits that were there in the parser output and were
- * temporarily stripped above.)
+ * temporarily stripped above.) Update incompatible_joins too.
*/
if (below_sjinfo)
+ {
quals = (List *)
add_nulling_relids((Node *) quals,
othersj->syn_righthand,
bms_make_singleton(othersj->ojrelid));
+ incompatible_joins = bms_del_member(incompatible_joins,
+ othersj->ojrelid);
+ }
/* ... and track joins processed so far */
joins_so_far = bms_add_member(joins_so_far, othersj->ojrelid);
@@ -2060,6 +2087,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
root->qual_security_level,
qualscope,
ojscope, nonnullable_rels,
+ NULL, /* incompatible_relids */
true, /* allow_equivalence */
false, false, /* not clones */
NULL); /* no more postponement */
@@ -2086,6 +2114,7 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
Relids qualscope,
Relids ojscope,
Relids outerjoin_nonnullable,
+ Relids incompatible_relids,
bool allow_equivalence,
bool has_clone,
bool is_clone,
@@ -2104,6 +2133,7 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
qualscope,
ojscope,
outerjoin_nonnullable,
+ incompatible_relids,
allow_equivalence,
has_clone,
is_clone,
@@ -2135,6 +2165,9 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
* base+OJ rels appearing on the outer (nonnullable) side of the join
* (for FULL JOIN this includes both sides of the join, and must in fact
* equal qualscope)
+ * 'incompatible_relids': the set of outer-join relid(s) that must not be
+ * computed below this qual. We only bother to compute this for
+ * "clone" quals, otherwise it can be left NULL.
* 'allow_equivalence': true if it's okay to convert clause into an
* EquivalenceClass
* 'has_clone': has_clone property to assign to the qual
@@ -2159,6 +2192,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope,
Relids ojscope,
Relids outerjoin_nonnullable,
+ Relids incompatible_relids,
bool allow_equivalence,
bool has_clone,
bool is_clone,
@@ -2377,15 +2411,14 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
restrictinfo = make_restrictinfo(root,
(Expr *) clause,
is_pushed_down,
+ has_clone,
+ is_clone,
pseudoconstant,
security_level,
relids,
+ incompatible_relids,
outerjoin_nonnullable);
- /* Apply appropriate clone marking, too */
- restrictinfo->has_clone = has_clone;
- restrictinfo->is_clone = is_clone;
-
/*
* If it's a join clause, add vars used in the clause to targetlists of
* their relations, so that they will be emitted by the plan nodes that
@@ -2750,9 +2783,12 @@ process_implied_equality(PlannerInfo *root,
restrictinfo = make_restrictinfo(root,
(Expr *) clause,
true, /* is_pushed_down */
+ false, /* !has_clone */
+ false, /* !is_clone */
pseudoconstant,
security_level,
relids,
+ NULL, /* incompatible_relids */
NULL); /* outer_relids */
/*
@@ -2841,9 +2877,12 @@ build_implied_join_equality(PlannerInfo *root,
restrictinfo = make_restrictinfo(root,
clause,
true, /* is_pushed_down */
+ false, /* !has_clone */
+ false, /* !is_clone */
false, /* pseudoconstant */
security_level, /* security_level */
qualscope, /* required_relids */
+ NULL, /* incompatible_relids */
NULL); /* outer_relids */
/* Set mergejoinability/hashjoinability flags */