mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-31 20:20:44 +08:00
Don't constrain self-join removal due to PHVs
Self-join removal appears to be safe to apply with placeholder variables as long as we handle PlaceHolderVar in replace_varno_walker() and replace relid in phinfo->ph_lateral. Discussion: https://postgr.es/m/18187-831da249cbd2ff8e%40postgresql.org Author: Richard Guo Reviewed-by: Andrei Lepikhov
This commit is contained in:
parent
8a8ed916f7
commit
b5fb6736ed
@ -453,7 +453,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
|
||||
|
||||
Assert(!bms_is_member(relid, phinfo->ph_lateral));
|
||||
Assert(sjinfo == NULL || !bms_is_member(relid, phinfo->ph_lateral));
|
||||
if (bms_is_subset(phinfo->ph_needed, joinrelids) &&
|
||||
bms_is_member(relid, phinfo->ph_eval_at) &&
|
||||
!bms_is_member(ojrelid, phinfo->ph_eval_at))
|
||||
@ -472,6 +472,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
|
||||
phinfo->ph_needed = replace_relid(phinfo->ph_needed, relid, subst);
|
||||
phinfo->ph_needed = replace_relid(phinfo->ph_needed, ojrelid, subst);
|
||||
/* ph_needed might or might not become empty */
|
||||
phinfo->ph_lateral = replace_relid(phinfo->ph_lateral, relid, subst);
|
||||
/* ph_lateral might or might not be empty */
|
||||
phv->phrels = replace_relid(phv->phrels, relid, subst);
|
||||
phv->phrels = replace_relid(phv->phrels, ojrelid, subst);
|
||||
Assert(!bms_is_empty(phv->phrels));
|
||||
@ -2115,20 +2117,8 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids)
|
||||
joinrelids = bms_add_member(joinrelids, k);
|
||||
|
||||
/*
|
||||
* Be safe to do not remove tables participated in complicated PH
|
||||
* PHVs should not impose any constraints on removing self joins.
|
||||
*/
|
||||
foreach(lc, root->placeholder_list)
|
||||
{
|
||||
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
|
||||
|
||||
/* there isn't any other place to eval PHV */
|
||||
if (bms_is_subset(phinfo->ph_eval_at, joinrelids) ||
|
||||
bms_is_subset(phinfo->ph_needed, joinrelids) ||
|
||||
bms_is_member(r, phinfo->ph_lateral))
|
||||
break;
|
||||
}
|
||||
if (lc)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* At this stage, joininfo lists of inner and outer can contain
|
||||
|
@ -6821,20 +6821,37 @@ on true;
|
||||
Filter: (id IS NOT NULL)
|
||||
(8 rows)
|
||||
|
||||
-- Check that SJE does not remove self joins if a PHV references the removed
|
||||
-- rel laterally.
|
||||
explain (costs off)
|
||||
-- Check that PHVs do not impose any constraints on removing self joins
|
||||
explain (verbose, costs off)
|
||||
select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
|
||||
lateral (select t1.id as t1id, * from generate_series(1,1) t3) s on true;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------
|
||||
Nested Loop Left Join
|
||||
-> Nested Loop
|
||||
-> Seq Scan on emp1 t1
|
||||
-> Index Scan using emp1_pkey on emp1 t2
|
||||
Index Cond: (id = t1.id)
|
||||
-> Function Scan on generate_series t3
|
||||
(6 rows)
|
||||
Output: t2.id, t2.code, t2.id, t2.code, (t2.id), t3.t3
|
||||
-> Seq Scan on public.emp1 t2
|
||||
Output: t2.id, t2.code
|
||||
Filter: (t2.id IS NOT NULL)
|
||||
-> Function Scan on pg_catalog.generate_series t3
|
||||
Output: t3.t3, t2.id
|
||||
Function Call: generate_series(1, 1)
|
||||
(8 rows)
|
||||
|
||||
explain (verbose, costs off)
|
||||
select * from generate_series(1,10) t1(id) left join
|
||||
lateral (select t1.id as t1id, t2.id from emp1 t2 join emp1 t3 on t2.id = t3.id)
|
||||
on true;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Nested Loop Left Join
|
||||
Output: t1.id, (t1.id), t3.id
|
||||
-> Function Scan on pg_catalog.generate_series t1
|
||||
Output: t1.id
|
||||
Function Call: generate_series(1, 10)
|
||||
-> Seq Scan on public.emp1 t3
|
||||
Output: t3.id, t1.id
|
||||
Filter: (t3.id IS NOT NULL)
|
||||
(8 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
|
||||
|
@ -2600,12 +2600,16 @@ select * from emp1 t1 left join
|
||||
on true)
|
||||
on true;
|
||||
|
||||
-- Check that SJE does not remove self joins if a PHV references the removed
|
||||
-- rel laterally.
|
||||
explain (costs off)
|
||||
-- Check that PHVs do not impose any constraints on removing self joins
|
||||
explain (verbose, costs off)
|
||||
select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
|
||||
lateral (select t1.id as t1id, * from generate_series(1,1) t3) s on true;
|
||||
|
||||
explain (verbose, costs off)
|
||||
select * from generate_series(1,10) t1(id) left join
|
||||
lateral (select t1.id as t1id, t2.id from emp1 t2 join emp1 t3 on t2.id = t3.id)
|
||||
on true;
|
||||
|
||||
-- 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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user