Fix for issue with EXISTS subquery with CTE
When we have a correlated subquery with EXISTS and CTE, the planner
produces wrong plan as:
```
pivotal=# explain with t as (select 1) select * from foo where exists (select * from bar where foo.a = 'a' and foo.b = 'b');
QUERY PLAN
----------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice3; segments: 3) (cost=0.01..1522.03 rows=5055210000 width=16)
-> Nested Loop (cost=0.01..1522.03 rows=1685070000 width=16)
-> Broadcast Motion 1:3 (slice2; segments: 1) (cost=0.01..0.03 rows=1 width=0)
-> Limit (cost=0.01..0.01 rows=1 width=0)
-> Gather Motion 3:1 (slice1; segments: 3) (cost=0.01..0.03 rows=1 width=0)
-> Limit (cost=0.01..0.01 rows=1 width=0)
-> Result (cost=0.01..811.00 rows=23700 width=0)
One-Time Filter: $0 = 'a'::bpchar AND $1 = 'b'::bpchar
-> Seq Scan on bar (cost=0.01..811.00 rows=23700 width=0)
-> Seq Scan on foo (cost=0.00..811.00 rows=23700 width=16)
Optimizer status: legacy query optimizer
(11 rows)
```
This failed during execution because $0 is referenced across the slices.
Root Cause:
That's because planner produce a plan with `$0` aka `param` but without
`subplan`. The `param` is created by `replace_outer_var()`, when planner
detects a query referring to relations from its outer/parent query. Such `var`
is created when removing `sublink` in `convert_EXISTS_to_join()` function. In
that function, when handling the `EXISTS` query, we convert the `EXISTS_sublink`
to a `subquery RTE` (and expect it to get pulled up later by
`pull_up_subquery()`. However the subquery cannot be pulled-up by
`pull_up_subquery()` since it is not a simple subquery (`is_simple_subquery()`
returns false because of CTE in this case). In this case, the `sublink` got
removed, hence it cannot produce the `subplan` (which is an valid option). And
the `var` left behind as outer-reference, and then covered to param, which is
blowed up during query execution. There is a mismatching conditions between
`convert_EXISTS_to_join()` and `pull_up_subquery()` about whether this subquery
can be pulled-up.
The fix is to reuse `is_simple_subquery()` checking when
`convert_EXISTS_to_join()`, so that it can be consistent with
`pull_up_subquery()` on whether subquery can be pulled or not.
The correct plan after fix is:
```
pivotal=# explain with t as (select 1) select * from foo where exists (select * from bar where foo.a = 'a' and foo.b = 'b');
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1977.50 rows=35550 width=16)
-> Seq Scan on foo (cost=0.00..1977.50 rows=11850 width=16)
Filter: (subplan)
SubPlan 1
-> Result (cost=0.01..811.00 rows=23700 width=16)
One-Time Filter: $0 = 'a'::bpchar AND $1 = 'b'::bpchar
-> Result (cost=882.11..1593.11 rows=23700 width=16)
-> Materialize (cost=882.11..1593.11 rows=23700 width=16)
-> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.01..811.00 rows=23700 width=16)
-> Seq Scan on bar (cost=0.01..811.00 rows=23700 width=16)
Optimizer status: legacy query optimizer
(11 rows)
```
Signed-off-by: NDhanashree Kashid <dkashid@pivotal.io>
Showing
想要评论请 注册 或 登录