mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Further fixes for degenerate outer join clauses.
Further testing revealed that commit f69b4b9495
was still a few
bricks shy of a load: minor tweaking of the previous test cases resulted
in the same wrong-outer-join-order problem coming back. After study
I concluded that my previous changes in make_outerjoininfo() were just
accidentally masking the problem, and should be reverted in favor of
forcing syntactic join order whenever an upper outer join's predicate
doesn't mention a lower outer join's LHS. This still allows the
chained-outer-joins style that is the normally optimizable case.
I also tightened things up some more in join_is_legal(). It seems to me
on review that what's really happening in the exception case where we
ignore a mismatched special join is that we're allowing the proposed join
to associate into the RHS of the outer join we're comparing it to. As
such, we should *always* insist that the proposed join be a left join,
which eliminates a bunch of rather dubious argumentation. The case where
we weren't enforcing that was the one that was already known buggy anyway
(it had a violatable Assert before the aforesaid commit) so it hardly
deserves a lot of deference.
Back-patch to all active branches, like the previous patch. The added
regression test case failed in all branches back to 9.1, and I think it's
only an unrelated change in costing calculations that kept 9.0 from
choosing a broken plan.
This commit is contained in:
@ -3425,7 +3425,7 @@ select t1.* from
|
||||
Output: t1.f1
|
||||
Hash Cond: (i8.q2 = i4.f1)
|
||||
-> Nested Loop Left Join
|
||||
Output: i8.q2, t1.f1
|
||||
Output: t1.f1, i8.q2
|
||||
Join Filter: (t1.f1 = '***'::text)
|
||||
-> Seq Scan on public.text_tbl t1
|
||||
Output: t1.f1
|
||||
@ -3473,6 +3473,76 @@ select t1.* from
|
||||
hi de ho neighbor
|
||||
(2 rows)
|
||||
|
||||
explain (verbose, costs off)
|
||||
select t1.* from
|
||||
text_tbl t1
|
||||
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
||||
left join int8_tbl i8
|
||||
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
||||
where q1 = f1) b2
|
||||
on (i8.q1 = b2.q1)
|
||||
on (b2.d2 = b1.q2)
|
||||
on (t1.f1 = b1.d1)
|
||||
left join int4_tbl i4
|
||||
on (i8.q2 = i4.f1);
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------
|
||||
Hash Left Join
|
||||
Output: t1.f1
|
||||
Hash Cond: (i8.q2 = i4.f1)
|
||||
-> Nested Loop Left Join
|
||||
Output: t1.f1, i8.q2
|
||||
Join Filter: (t1.f1 = '***'::text)
|
||||
-> Seq Scan on public.text_tbl t1
|
||||
Output: t1.f1
|
||||
-> Materialize
|
||||
Output: i8.q2
|
||||
-> Hash Right Join
|
||||
Output: i8.q2
|
||||
Hash Cond: ((NULL::integer) = i8b1.q2)
|
||||
-> Hash Right Join
|
||||
Output: i8.q2, (NULL::integer)
|
||||
Hash Cond: (i8b2.q1 = i8.q1)
|
||||
-> Hash Join
|
||||
Output: i8b2.q1, NULL::integer
|
||||
Hash Cond: (i8b2.q1 = i4b2.f1)
|
||||
-> Seq Scan on public.int8_tbl i8b2
|
||||
Output: i8b2.q1, i8b2.q2
|
||||
-> Hash
|
||||
Output: i4b2.f1
|
||||
-> Seq Scan on public.int4_tbl i4b2
|
||||
Output: i4b2.f1
|
||||
-> Hash
|
||||
Output: i8.q1, i8.q2
|
||||
-> Seq Scan on public.int8_tbl i8
|
||||
Output: i8.q1, i8.q2
|
||||
-> Hash
|
||||
Output: i8b1.q2
|
||||
-> Seq Scan on public.int8_tbl i8b1
|
||||
Output: i8b1.q2
|
||||
-> Hash
|
||||
Output: i4.f1
|
||||
-> Seq Scan on public.int4_tbl i4
|
||||
Output: i4.f1
|
||||
(37 rows)
|
||||
|
||||
select t1.* from
|
||||
text_tbl t1
|
||||
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
||||
left join int8_tbl i8
|
||||
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
||||
where q1 = f1) b2
|
||||
on (i8.q1 = b2.q1)
|
||||
on (b2.d2 = b1.q2)
|
||||
on (t1.f1 = b1.d1)
|
||||
left join int4_tbl i4
|
||||
on (i8.q2 = i4.f1);
|
||||
f1
|
||||
-------------------
|
||||
doh!
|
||||
hi de ho neighbor
|
||||
(2 rows)
|
||||
|
||||
--
|
||||
-- test ability to push constants through outer join clauses
|
||||
--
|
||||
|
@ -1065,6 +1065,31 @@ select t1.* from
|
||||
left join int4_tbl i4
|
||||
on (i8.q2 = i4.f1);
|
||||
|
||||
explain (verbose, costs off)
|
||||
select t1.* from
|
||||
text_tbl t1
|
||||
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
||||
left join int8_tbl i8
|
||||
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
||||
where q1 = f1) b2
|
||||
on (i8.q1 = b2.q1)
|
||||
on (b2.d2 = b1.q2)
|
||||
on (t1.f1 = b1.d1)
|
||||
left join int4_tbl i4
|
||||
on (i8.q2 = i4.f1);
|
||||
|
||||
select t1.* from
|
||||
text_tbl t1
|
||||
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
||||
left join int8_tbl i8
|
||||
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
||||
where q1 = f1) b2
|
||||
on (i8.q1 = b2.q1)
|
||||
on (b2.d2 = b1.q2)
|
||||
on (t1.f1 = b1.d1)
|
||||
left join int4_tbl i4
|
||||
on (i8.q2 = i4.f1);
|
||||
|
||||
--
|
||||
-- test ability to push constants through outer join clauses
|
||||
--
|
||||
|
Reference in New Issue
Block a user