Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b5fb673

Browse files
committed
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
1 parent 8a8ed91 commit b5fb673

File tree

3 files changed

+39
-28
lines changed

3 files changed

+39
-28
lines changed

src/backend/optimizer/plan/analyzejoins.c

+4-14
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
453453
{
454454
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
455455

456-
Assert(!bms_is_member(relid, phinfo->ph_lateral));
456+
Assert(sjinfo == NULL || !bms_is_member(relid, phinfo->ph_lateral));
457457
if (bms_is_subset(phinfo->ph_needed, joinrelids) &&
458458
bms_is_member(relid, phinfo->ph_eval_at) &&
459459
!bms_is_member(ojrelid, phinfo->ph_eval_at))
@@ -472,6 +472,8 @@ remove_rel_from_query(PlannerInfo *root, RelOptInfo *rel,
472472
phinfo->ph_needed = replace_relid(phinfo->ph_needed, relid, subst);
473473
phinfo->ph_needed = replace_relid(phinfo->ph_needed, ojrelid, subst);
474474
/* ph_needed might or might not become empty */
475+
phinfo->ph_lateral = replace_relid(phinfo->ph_lateral, relid, subst);
476+
/* ph_lateral might or might not be empty */
475477
phv->phrels = replace_relid(phv->phrels, relid, subst);
476478
phv->phrels = replace_relid(phv->phrels, ojrelid, subst);
477479
Assert(!bms_is_empty(phv->phrels));
@@ -2115,20 +2117,8 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids)
21152117
joinrelids = bms_add_member(joinrelids, k);
21162118

21172119
/*
2118-
* Be safe to do not remove tables participated in complicated PH
2120+
* PHVs should not impose any constraints on removing self joins.
21192121
*/
2120-
foreach(lc, root->placeholder_list)
2121-
{
2122-
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
2123-
2124-
/* there isn't any other place to eval PHV */
2125-
if (bms_is_subset(phinfo->ph_eval_at, joinrelids) ||
2126-
bms_is_subset(phinfo->ph_needed, joinrelids) ||
2127-
bms_is_member(r, phinfo->ph_lateral))
2128-
break;
2129-
}
2130-
if (lc)
2131-
continue;
21322122

21332123
/*
21342124
* At this stage, joininfo lists of inner and outer can contain

src/test/regress/expected/join.out

+28-11
Original file line numberDiff line numberDiff line change
@@ -6821,20 +6821,37 @@ on true;
68216821
Filter: (id IS NOT NULL)
68226822
(8 rows)
68236823

6824-
-- Check that SJE does not remove self joins if a PHV references the removed
6825-
-- rel laterally.
6826-
explain (costs off)
6824+
-- Check that PHVs do not impose any constraints on removing self joins
6825+
explain (verbose, costs off)
68276826
select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
68286827
lateral (select t1.id as t1id, * from generate_series(1,1) t3) s on true;
6829-
QUERY PLAN
6830-
---------------------------------------------------
6828+
QUERY PLAN
6829+
----------------------------------------------------------
68316830
Nested Loop Left Join
6832-
-> Nested Loop
6833-
-> Seq Scan on emp1 t1
6834-
-> Index Scan using emp1_pkey on emp1 t2
6835-
Index Cond: (id = t1.id)
6836-
-> Function Scan on generate_series t3
6837-
(6 rows)
6831+
Output: t2.id, t2.code, t2.id, t2.code, (t2.id), t3.t3
6832+
-> Seq Scan on public.emp1 t2
6833+
Output: t2.id, t2.code
6834+
Filter: (t2.id IS NOT NULL)
6835+
-> Function Scan on pg_catalog.generate_series t3
6836+
Output: t3.t3, t2.id
6837+
Function Call: generate_series(1, 1)
6838+
(8 rows)
6839+
6840+
explain (verbose, costs off)
6841+
select * from generate_series(1,10) t1(id) left join
6842+
lateral (select t1.id as t1id, t2.id from emp1 t2 join emp1 t3 on t2.id = t3.id)
6843+
on true;
6844+
QUERY PLAN
6845+
------------------------------------------------------
6846+
Nested Loop Left Join
6847+
Output: t1.id, (t1.id), t3.id
6848+
-> Function Scan on pg_catalog.generate_series t1
6849+
Output: t1.id
6850+
Function Call: generate_series(1, 10)
6851+
-> Seq Scan on public.emp1 t3
6852+
Output: t3.id, t1.id
6853+
Filter: (t3.id IS NOT NULL)
6854+
(8 rows)
68386855

68396856
-- We can remove the join even if we find the join can't duplicate rows and
68406857
-- the base quals of each side are different. In the following case we end up

src/test/regress/sql/join.sql

+7-3
Original file line numberDiff line numberDiff line change
@@ -2600,12 +2600,16 @@ select * from emp1 t1 left join
26002600
on true)
26012601
on true;
26022602

2603-
-- Check that SJE does not remove self joins if a PHV references the removed
2604-
-- rel laterally.
2605-
explain (costs off)
2603+
-- Check that PHVs do not impose any constraints on removing self joins
2604+
explain (verbose, costs off)
26062605
select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
26072606
lateral (select t1.id as t1id, * from generate_series(1,1) t3) s on true;
26082607

2608+
explain (verbose, costs off)
2609+
select * from generate_series(1,10) t1(id) left join
2610+
lateral (select t1.id as t1id, t2.id from emp1 t2 join emp1 t3 on t2.id = t3.id)
2611+
on true;
2612+
26092613
-- We can remove the join even if we find the join can't duplicate rows and
26102614
-- the base quals of each side are different. In the following case we end up
26112615
-- moving quals over to s1 to make it so it can't match any rows.

0 commit comments

Comments
 (0)