*
* 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,
Relids join_relids,
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;
nominal_join_relids = join_relids;
}
- foreach(lc, root->eq_classes)
+ foreach(lc, eclasses)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
List *sublist = NIL;
Relids inner_and_req;
List *pclauses;
List *eclauses;
+ List *dropped_ecs;
double rows;
ListCell *lc;
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);
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);
+ }
}
/*
1000
(1 row)
+--
+-- regression test: check a case where we formerly missed including an EC
+-- enforcement clause because it was expected to be handled at scan level
+--
+explain (costs off)
+select a.f1, b.f1, t.thousand, t.tenthous from
+ tenk1 t,
+ (select sum(f1)+1 as f1 from int4_tbl i4a) a,
+ (select sum(f1) as f1 from int4_tbl i4b) b
+where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------
+ Nested Loop
+ -> Aggregate
+ -> Seq Scan on int4_tbl i4b
+ -> Nested Loop
+ Join Filter: ((sum(i4b.f1)) = ((sum(i4a.f1) + 1)))
+ -> Aggregate
+ -> Seq Scan on int4_tbl i4a
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1 t
+ Index Cond: ((thousand = (sum(i4b.f1))) AND (tenthous = ((((sum(i4a.f1) + 1)) + (sum(i4b.f1))) + 999)))
+(9 rows)
+
+select a.f1, b.f1, t.thousand, t.tenthous from
+ tenk1 t,
+ (select sum(f1)+1 as f1 from int4_tbl i4a) a,
+ (select sum(f1) as f1 from int4_tbl i4b) b
+where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
+ f1 | f1 | thousand | tenthous
+----+----+----------+----------
+(0 rows)
+
--
-- Clean up
--
tenk1 t5
where t4.thousand = t5.unique1 and ss.x1 = t4.tenthous and ss.x2 = t5.stringu1;
+--
+-- regression test: check a case where we formerly missed including an EC
+-- enforcement clause because it was expected to be handled at scan level
+--
+explain (costs off)
+select a.f1, b.f1, t.thousand, t.tenthous from
+ tenk1 t,
+ (select sum(f1)+1 as f1 from int4_tbl i4a) a,
+ (select sum(f1) as f1 from int4_tbl i4b) b
+where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
+
+select a.f1, b.f1, t.thousand, t.tenthous from
+ tenk1 t,
+ (select sum(f1)+1 as f1 from int4_tbl i4a) a,
+ (select sum(f1) as f1 from int4_tbl i4b) b
+where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
+
--
-- Clean up