mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Fix mishandling of equivalence-class tests in parameterized plans.
Given a three-or-more-way equivalence class, such as X.Y = Y.Y = Z.Z, it was possible for the planner to omit one of the quals needed to enforce that all members of the equivalence class are actually equal. This only happened in the case of a parameterized join node for two of the relations, that is a plan tree like Nested Loop -> Scan X -> Nested Loop -> Scan Y -> Scan Z Filter: Z.Z = X.X The eclass machinery normally expects to apply X.X = Y.Y when those two relations are joined, but in this shape of plan tree they aren't joined until the top node --- and, if the lower nested loop is marked as parameterized by X, the top node will assume that the relevant eclass condition(s) got pushed down into the lower node. On the other hand, the scan of Z assumes that it's only responsible for constraining Z.Z to match any one of the other eclass members. So one or another of the required quals sometimes fell between the cracks, depending on whether consideration of the eclass in get_joinrel_parampathinfo() for the lower nested loop chanced to generate X.X = Y.Y or X.X = Z.Z as the appropriate constraint there. If it generated the latter, it'd erroneously suppose that the Z scan would take care of matters. To fix, force X.X = Y.Y to be generated and applied at that join node when this case occurs. This is *extremely* hard to hit in practice, because various planner behaviors conspire to mask the problem; starting with the fact that the planner doesn't really like to generate a parameterized plan of the above shape. (It might have been impossible to hit it before we tweaked things to allow this plan shape for star-schema cases.) Many thanks to Alexander Kirkouski for submitting a reproducible test case. The bug can be demonstrated in all branches back to 9.2 where parameterized paths were introduced, so back-patch that far.
This commit is contained in:
@ -2236,6 +2236,38 @@ where t4.thousand = t5.unique1 and ss.x1 = t4.tenthous and ss.x2 = t5.stringu1;
|
||||
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
|
||||
--
|
||||
|
@ -391,6 +391,23 @@ from
|
||||
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
|
||||
|
Reference in New Issue
Block a user