iterative_elimination.diff
text/plain
Filename: iterative_elimination.diff
Type: text/plain
Part: 0
Message:
Re: Removing unneeded self joins
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/plan/analyzejoins.c | 16 | 2 |
| src/test/regress/expected/join.out | 30 | 0 |
| src/test/regress/sql/join.sql | 10 | 0 |
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 7b8dc7a2b7..f7ccda5231 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -2298,6 +2298,7 @@ remove_self_joins_recurse(PlannerInfo *root, List *joinlist, Relids toRemove)
{
/* Create group of relation indexes with the same oid */
Relids group = NULL;
+ Relids removed;
while (i < j)
{
@@ -2306,8 +2307,21 @@ remove_self_joins_recurse(PlannerInfo *root, List *joinlist, Relids toRemove)
}
relids = bms_del_members(relids, group);
- toRemove = bms_add_members(toRemove,
- remove_self_joins_one_group(root, group));
+
+ /*
+ * Try to remove self-joins from a group of identical entries.
+ * Make next attempt iteratively - if something is deleted from
+ * a group, changes in clauses and equivalence classes can give
+ * us a chance to find more candidates.
+ */
+ do {
+ Assert(!bms_overlap(group, toRemove));
+ removed = remove_self_joins_one_group(root, group);
+ toRemove = bms_add_members(toRemove, removed);
+ group = bms_del_members(group, removed);
+ } while (!bms_is_empty(removed) &&
+ bms_membership(group) == BMS_MULTIPLE);
+ bms_free(removed);
bms_free(group);
}
else
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 12a90bd42e..cb2429645c 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -6786,6 +6786,36 @@ SELECT * FROM emp1 e1, emp1 e2 WHERE e1.id = e2.id AND e2.code <> e1.code;
Filter: ((e2.id IS NOT NULL) AND (e2.code <> e2.code))
(3 rows)
+-- Shuffle self-joined relations. Only in the case of iterative deletion
+-- attempts explains of these queries will be identical.
+CREATE UNIQUE INDEX ON emp1((id*id));
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c1.id=c2.id AND c1.id*c2.id=c3.id*c3.id;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Aggregate (cost=43.84..43.85 rows=1 width=8)
+ -> Seq Scan on emp1 c3 (cost=0.00..38.25 rows=2237 width=0)
+ Filter: ((id IS NOT NULL) AND ((id * id) IS NOT NULL))
+(3 rows)
+
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c1.id=c3.id AND c1.id*c3.id=c2.id*c2.id;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Aggregate (cost=43.84..43.85 rows=1 width=8)
+ -> Seq Scan on emp1 c3 (cost=0.00..38.25 rows=2237 width=0)
+ Filter: ((id IS NOT NULL) AND ((id * id) IS NOT NULL))
+(3 rows)
+
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c3.id=c2.id AND c3.id*c2.id=c1.id*c1.id;
+ QUERY PLAN
+-----------------------------------------------------------------
+ Aggregate (cost=43.84..43.85 rows=1 width=8)
+ -> Seq Scan on emp1 c3 (cost=0.00..38.25 rows=2237 width=0)
+ Filter: ((id IS NOT NULL) AND ((id * id) IS NOT NULL))
+(3 rows)
+
-- We can remove the join even if we find the join can't duplicate rows and
-- the base quals of each side are different. In the following case we end up
-- moving quals over to s1 to make it so it can't match any rows.
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 4d49c0767a..55147263ca 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -2576,6 +2576,16 @@ CREATE TABLE emp1 ( id SERIAL PRIMARY KEY NOT NULL, code int);
explain (verbose, costs off)
SELECT * FROM emp1 e1, emp1 e2 WHERE e1.id = e2.id AND e2.code <> e1.code;
+-- Shuffle self-joined relations. Only in the case of iterative deletion
+-- attempts explains of these queries will be identical.
+CREATE UNIQUE INDEX ON emp1((id*id));
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c1.id=c2.id AND c1.id*c2.id=c3.id*c3.id;
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c1.id=c3.id AND c1.id*c3.id=c2.id*c2.id;
+explain SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3
+WHERE c3.id=c2.id AND c3.id*c2.id=c1.id*c1.id;
+
-- We can remove the join even if we find the join can't duplicate rows and
-- the base quals of each side are different. In the following case we end up
-- moving quals over to s1 to make it so it can't match any rows.