aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/relnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/relnode.c')
-rw-r--r--src/backend/optimizer/util/relnode.c82
1 files changed, 75 insertions, 7 deletions
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);
+ }
}
/*