aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/optimizer/plan/analyzejoins.c68
-rw-r--r--src/test/regress/expected/join.out31
-rw-r--r--src/test/regress/sql/join.sql23
3 files changed, 122 insertions, 0 deletions
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 6476e55e568..9161c8a2964 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -39,6 +39,8 @@ static void remove_rel_from_query(PlannerInfo *root, int relid,
SpecialJoinInfo *sjinfo);
static void remove_rel_from_restrictinfo(RestrictInfo *rinfo,
int relid, int ojrelid);
+static void remove_rel_from_eclass(EquivalenceClass *ec,
+ int relid, int ojrelid);
static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
static bool rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel);
static bool rel_is_distinct_for(PlannerInfo *root, RelOptInfo *rel,
@@ -512,6 +514,18 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
}
/*
+ * Likewise remove references from EquivalenceClasses.
+ */
+ foreach(l, root->eq_classes)
+ {
+ EquivalenceClass *ec = (EquivalenceClass *) lfirst(l);
+
+ if (bms_is_member(relid, ec->ec_relids) ||
+ bms_is_member(ojrelid, ec->ec_relids))
+ remove_rel_from_eclass(ec, relid, ojrelid);
+ }
+
+ /*
* There may be references to the rel in root->fkey_list, but if so,
* match_foreign_keys_to_quals() will get rid of them.
*/
@@ -584,6 +598,60 @@ remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
}
/*
+ * Remove any references to relid or ojrelid from the EquivalenceClass.
+ *
+ * Like remove_rel_from_restrictinfo, we don't worry about cleaning out
+ * any nullingrel bits in contained Vars and PHVs. (This might have to be
+ * improved sometime.) We do need to fix the EC and EM relid sets to ensure
+ * that implied join equalities will be generated at the appropriate join
+ * level(s).
+ */
+static void
+remove_rel_from_eclass(EquivalenceClass *ec, int relid, int ojrelid)
+{
+ ListCell *lc;
+
+ /* Fix up the EC's overall relids */
+ ec->ec_relids = bms_del_member(ec->ec_relids, relid);
+ ec->ec_relids = bms_del_member(ec->ec_relids, ojrelid);
+
+ /*
+ * Fix up the member expressions. Any non-const member that ends with
+ * empty em_relids must be a Var or PHV of the removed relation. We don't
+ * need it anymore, so we can drop it.
+ */
+ foreach(lc, ec->ec_members)
+ {
+ EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+
+ if (bms_is_member(relid, cur_em->em_relids) ||
+ bms_is_member(ojrelid, cur_em->em_relids))
+ {
+ Assert(!cur_em->em_is_const);
+ cur_em->em_relids = bms_del_member(cur_em->em_relids, relid);
+ cur_em->em_relids = bms_del_member(cur_em->em_relids, ojrelid);
+ if (bms_is_empty(cur_em->em_relids))
+ ec->ec_members = foreach_delete_current(ec->ec_members, lc);
+ }
+ }
+
+ /* Fix up the source clauses, in case we can re-use them later */
+ foreach(lc, ec->ec_sources)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+ remove_rel_from_restrictinfo(rinfo, relid, ojrelid);
+ }
+
+ /*
+ * Rather than expend code on fixing up any already-derived clauses, just
+ * drop them. (At this point, any such clauses would be base restriction
+ * clauses, which we'd not need anymore anyway.)
+ */
+ ec->ec_derives = NIL;
+}
+
+/*
* Remove any occurrences of the target relid from a joinlist structure.
*
* It's easiest to build a whole new list structure, so we handle it that
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 98b2667821e..cc4c122fdd4 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -5882,6 +5882,37 @@ where ss.stringu2 !~* ss.case1;
(1 row)
rollback;
+-- another join removal bug: we must clean up EquivalenceClasses too
+begin;
+create temp table t (a int unique);
+insert into t values (1);
+explain (costs off)
+select 1
+from t t1
+ left join (select 2 as c
+ from t t2 left join t t3 on t2.a = t3.a) s
+ on true
+where t1.a = s.c;
+ QUERY PLAN
+------------------------------
+ Nested Loop Left Join
+ Filter: (t1.a = (2))
+ -> Seq Scan on t t1
+ -> Materialize
+ -> Seq Scan on t t2
+(5 rows)
+
+select 1
+from t t1
+ left join (select 2 as c
+ from t t2 left join t t3 on t2.a = t3.a) s
+ on true
+where t1.a = s.c;
+ ?column?
+----------
+(0 rows)
+
+rollback;
-- test cases where we can remove a join, but not a PHV computed at it
begin;
create temp table t (a int unique, b int);
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 7daa390b1d4..e77e4695709 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -2167,6 +2167,29 @@ where ss.stringu2 !~* ss.case1;
rollback;
+-- another join removal bug: we must clean up EquivalenceClasses too
+begin;
+
+create temp table t (a int unique);
+insert into t values (1);
+
+explain (costs off)
+select 1
+from t t1
+ left join (select 2 as c
+ from t t2 left join t t3 on t2.a = t3.a) s
+ on true
+where t1.a = s.c;
+
+select 1
+from t t1
+ left join (select 2 as c
+ from t t2 left join t t3 on t2.a = t3.a) s
+ on true
+where t1.a = s.c;
+
+rollback;
+
-- test cases where we can remove a join, but not a PHV computed at it
begin;