aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/equivclass.c22
-rw-r--r--src/backend/optimizer/util/relnode.c82
2 files changed, 95 insertions, 9 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 48be45dffc9..6e41fd039f1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -987,7 +987,7 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
*
* join_relids should always equal bms_union(outer_relids, inner_rel->relids).
* We could simplify this function's API by computing it internally, but in
- * all current uses, the caller has the value at hand anyway.
+ * most current uses, the caller has the value at hand anyway.
*/
List *
generate_join_implied_equalities(PlannerInfo *root,
@@ -995,6 +995,24 @@ generate_join_implied_equalities(PlannerInfo *root,
Relids outer_relids,
RelOptInfo *inner_rel)
{
+ return generate_join_implied_equalities_for_ecs(root,
+ root->eq_classes,
+ join_relids,
+ outer_relids,
+ inner_rel);
+}
+
+/*
+ * generate_join_implied_equalities_for_ecs
+ * As above, but consider only the listed ECs.
+ */
+List *
+generate_join_implied_equalities_for_ecs(PlannerInfo *root,
+ List *eclasses,
+ Relids join_relids,
+ Relids outer_relids,
+ RelOptInfo *inner_rel)
+{
List *result = NIL;
Relids inner_relids = inner_rel->relids;
Relids nominal_inner_relids;
@@ -1015,7 +1033,7 @@ generate_join_implied_equalities(PlannerInfo *root,
nominal_join_relids = join_relids;
}
- foreach(lc, root->eq_classes)
+ foreach(lc, eclasses)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
List *sublist = NIL;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 3c34711ab3b..127f65076f5 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -982,6 +982,7 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel,
Relids inner_and_req;
List *pclauses;
List *eclauses;
+ List *dropped_ecs;
double rows;
ListCell *lc;
@@ -1035,6 +1036,7 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel,
required_outer,
joinrel);
/* We only want ones that aren't movable to lower levels */
+ dropped_ecs = NIL;
foreach(lc, eclauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
@@ -1051,13 +1053,79 @@ get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel,
joinrel->relids,
join_and_req));
#endif
- if (!join_clause_is_movable_into(rinfo,
- outer_path->parent->relids,
- outer_and_req) &&
- !join_clause_is_movable_into(rinfo,
- inner_path->parent->relids,
- inner_and_req))
- pclauses = lappend(pclauses, rinfo);
+ if (join_clause_is_movable_into(rinfo,
+ outer_path->parent->relids,
+ outer_and_req))
+ continue; /* drop if movable into LHS */
+ if (join_clause_is_movable_into(rinfo,
+ inner_path->parent->relids,
+ inner_and_req))
+ {
+ /* drop if movable into RHS, but remember EC for use below */
+ Assert(rinfo->left_ec == rinfo->right_ec);
+ dropped_ecs = lappend(dropped_ecs, rinfo->left_ec);
+ continue;
+ }
+ pclauses = lappend(pclauses, rinfo);
+ }
+
+ /*
+ * EquivalenceClasses are harder to deal with than we could wish, because
+ * of the fact that a given EC can generate different clauses depending on
+ * context. Suppose we have an EC {X.X, Y.Y, Z.Z} where X and Y are the
+ * LHS and RHS of the current join and Z is in required_outer, and further
+ * suppose that the inner_path is parameterized by both X and Z. The code
+ * above will have produced either Z.Z = X.X or Z.Z = Y.Y from that EC,
+ * and in the latter case will have discarded it as being movable into the
+ * RHS. However, the EC machinery might have produced either Y.Y = X.X or
+ * Y.Y = Z.Z as the EC enforcement clause within the inner_path; it will
+ * not have produced both, and we can't readily tell from here which one
+ * it did pick. If we add no clause to this join, we'll end up with
+ * insufficient enforcement of the EC; either Z.Z or X.X will fail to be
+ * constrained to be equal to the other members of the EC. (When we come
+ * to join Z to this X/Y path, we will certainly drop whichever EC clause
+ * is generated at that join, so this omission won't get fixed later.)
+ *
+ * To handle this, for each EC we discarded such a clause from, try to
+ * generate a clause connecting the required_outer rels to the join's LHS
+ * ("Z.Z = X.X" in the terms of the above example). If successful, and if
+ * the clause can't be moved to the LHS, add it to the current join's
+ * restriction clauses. (If an EC cannot generate such a clause then it
+ * has nothing that needs to be enforced here, while if the clause can be
+ * moved into the LHS then it should have been enforced within that path.)
+ *
+ * Note that we don't need similar processing for ECs whose clause was
+ * considered to be movable into the LHS, because the LHS can't refer to
+ * the RHS so there is no comparable ambiguity about what it might
+ * actually be enforcing internally.
+ */
+ if (dropped_ecs)
+ {
+ Relids real_outer_and_req;
+
+ real_outer_and_req = bms_union(outer_path->parent->relids,
+ required_outer);
+ eclauses =
+ generate_join_implied_equalities_for_ecs(root,
+ dropped_ecs,
+ real_outer_and_req,
+ required_outer,
+ outer_path->parent);
+ foreach(lc, eclauses)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+ /* As above, can't quite assert this here */
+#ifdef NOT_USED
+ Assert(join_clause_is_movable_into(rinfo,
+ outer_path->parent->relids,
+ real_outer_and_req));
+#endif
+ if (!join_clause_is_movable_into(rinfo,
+ outer_path->parent->relids,
+ outer_and_req))
+ pclauses = lappend(pclauses, rinfo);
+ }
}
/*