aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c95
1 files changed, 77 insertions, 18 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 9cfc56ea541..24185a6cecb 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -52,9 +52,12 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype,
Relids qualscope,
Relids ojscope,
- Relids outerjoin_nonnullable);
+ Relids outerjoin_nonnullable,
+ Relids deduced_nullable_relids);
static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
Relids *nullable_relids_p, bool is_pushed_down);
+static bool check_equivalence_delay(PlannerInfo *root,
+ RestrictInfo *restrictinfo);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
static void check_mergejoinable(RestrictInfo *restrictinfo);
static void check_hashjoinable(RestrictInfo *restrictinfo);
@@ -353,7 +356,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels(root, qual,
false, below_outer_join, JOIN_INNER,
- *qualscope, NULL, NULL);
+ *qualscope, NULL, NULL, NULL);
}
}
else if (IsA(jtnode, JoinExpr))
@@ -477,7 +480,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels(root, qual,
false, below_outer_join, j->jointype,
*qualscope,
- ojscope, nonnullable_rels);
+ ojscope, nonnullable_rels, NULL);
}
/* Now we can add the SpecialJoinInfo to join_info_list */
@@ -786,13 +789,19 @@ make_outerjoininfo(PlannerInfo *root,
* baserels 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)
+ * 'deduced_nullable_relids': if is_deduced is TRUE, the nullable relids to
+ * impute to the clause; otherwise NULL
*
* 'qualscope' identifies what level of JOIN the qual came from syntactically.
* 'ojscope' is needed if we decide to force the qual up to the outer-join
* level, which will be ojscope not necessarily qualscope.
*
- * At the time this is called, root->join_info_list must contain entries for
- * all and only those special joins that are syntactically below this qual.
+ * In normal use (when is_deduced is FALSE), at the time this is called,
+ * root->join_info_list must contain entries for all and only those special
+ * joins that are syntactically below this qual. But when is_deduced is TRUE,
+ * we are adding new deduced clauses after completion of deconstruct_jointree,
+ * so it cannot be assumed that root->join_info_list has anything to do with
+ * qual placement.
*/
static void
distribute_qual_to_rels(PlannerInfo *root, Node *clause,
@@ -801,7 +810,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype,
Relids qualscope,
Relids ojscope,
- Relids outerjoin_nonnullable)
+ Relids outerjoin_nonnullable,
+ Relids deduced_nullable_relids)
{
Relids relids;
bool is_pushed_down;
@@ -914,12 +924,13 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* If the qual came from implied-equality deduction, it should not be
* outerjoin-delayed, else deducer blew it. But we can't check this
* because the join_info_list may now contain OJs above where the qual
- * belongs.
+ * belongs. For the same reason, we must rely on caller to supply the
+ * correct nullable_relids set.
*/
Assert(!ojscope);
is_pushed_down = true;
outerjoin_delayed = false;
- nullable_relids = NULL;
+ nullable_relids = deduced_nullable_relids;
/* Don't feed it back for more deductions */
maybe_equivalence = false;
maybe_outer_join = false;
@@ -1089,7 +1100,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
{
if (maybe_equivalence)
{
- if (process_equivalence(root, restrictinfo, below_outer_join))
+ if (check_equivalence_delay(root, restrictinfo) &&
+ process_equivalence(root, restrictinfo, below_outer_join))
return;
/* EC rejected it, so set left_ec/right_ec the hard way ... */
initialize_mergeclause_eclasses(root, restrictinfo);
@@ -1262,6 +1274,44 @@ check_outerjoin_delay(PlannerInfo *root,
}
/*
+ * check_equivalence_delay
+ * Detect whether a potential equivalence clause is rendered unsafe
+ * by outer-join-delay considerations. Return TRUE if it's safe.
+ *
+ * The initial tests in distribute_qual_to_rels will consider a mergejoinable
+ * clause to be a potential equivalence clause if it is not outerjoin_delayed.
+ * But since the point of equivalence processing is that we will recombine the
+ * two sides of the clause with others, we have to check that each side
+ * satisfies the not-outerjoin_delayed condition on its own; otherwise it might
+ * not be safe to evaluate everywhere we could place a derived equivalence
+ * condition.
+ */
+static bool
+check_equivalence_delay(PlannerInfo *root,
+ RestrictInfo *restrictinfo)
+{
+ Relids relids;
+ Relids nullable_relids;
+
+ /* fast path if no special joins */
+ if (root->join_info_list == NIL)
+ return true;
+
+ /* must copy restrictinfo's relids to avoid changing it */
+ relids = bms_copy(restrictinfo->left_relids);
+ /* check left side does not need delay */
+ if (check_outerjoin_delay(root, &relids, &nullable_relids, true))
+ return false;
+
+ /* and similarly for the right side */
+ relids = bms_copy(restrictinfo->right_relids);
+ if (check_outerjoin_delay(root, &relids, &nullable_relids, true))
+ return false;
+
+ return true;
+}
+
+/*
* check_redundant_nullability_qual
* Check to see if the qual is an IS NULL qual that is redundant with
* a lower JOIN_ANTI join.
@@ -1371,11 +1421,20 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
* variable-free. Otherwise the qual is applied at the lowest join level
* that provides all its variables.
*
+ * "nullable_relids" is the set of relids used in the expressions that are
+ * potentially nullable below the expressions. (This has to be supplied by
+ * caller because this function is used after deconstruct_jointree, so we
+ * don't have knowledge of where the clause items came from.)
+ *
* "both_const" indicates whether both items are known pseudo-constant;
* in this case it is worth applying eval_const_expressions() in case we
* can produce constant TRUE or constant FALSE. (Otherwise it's not,
* because the expressions went through eval_const_expressions already.)
*
+ * Note: this function will copy item1 and item2, but it is caller's
+ * responsibility to make sure that the Relids parameters are fresh copies
+ * not shared with other uses.
+ *
* This is currently used only when an EquivalenceClass is found to
* contain pseudoconstants. See path/pathkeys.c for more details.
*/
@@ -1386,6 +1445,7 @@ process_implied_equality(PlannerInfo *root,
Expr *item1,
Expr *item2,
Relids qualscope,
+ Relids nullable_relids,
bool below_outer_join,
bool both_const)
{
@@ -1419,15 +1479,12 @@ process_implied_equality(PlannerInfo *root,
}
}
- /* Make a copy of qualscope to avoid problems if source EC changes */
- qualscope = bms_copy(qualscope);
-
/*
* Push the new clause into all the appropriate restrictinfo lists.
*/
distribute_qual_to_rels(root, (Node *) clause,
true, below_outer_join, JOIN_INNER,
- qualscope, NULL, NULL);
+ qualscope, NULL, NULL, nullable_relids);
}
/*
@@ -1436,6 +1493,10 @@ process_implied_equality(PlannerInfo *root,
* This overlaps the functionality of process_implied_equality(), but we
* must return the RestrictInfo, not push it into the joininfo tree.
*
+ * Note: this function will copy item1 and item2, but it is caller's
+ * responsibility to make sure that the Relids parameters are fresh copies
+ * not shared with other uses.
+ *
* Note: we do not do initialize_mergeclause_eclasses() here. It is
* caller's responsibility that left_ec/right_ec be set as necessary.
*/
@@ -1444,7 +1505,8 @@ build_implied_join_equality(Oid opno,
Oid collation,
Expr *item1,
Expr *item2,
- Relids qualscope)
+ Relids qualscope,
+ Relids nullable_relids)
{
RestrictInfo *restrictinfo;
Expr *clause;
@@ -1461,9 +1523,6 @@ build_implied_join_equality(Oid opno,
InvalidOid,
collation);
- /* Make a copy of qualscope to avoid problems if source EC changes */
- qualscope = bms_copy(qualscope);
-
/*
* Build the RestrictInfo node itself.
*/
@@ -1472,7 +1531,7 @@ build_implied_join_equality(Oid opno,
false, /* outerjoin_delayed */
false, /* pseudoconstant */
qualscope, /* required_relids */
- NULL); /* nullable_relids */
+ nullable_relids); /* nullable_relids */
/* Set mergejoinability/hashjoinability flags */
check_mergejoinable(restrictinfo);