From 6a601edac93c20545f3db628bed286e40fcd7ac9 Mon Sep 17 00:00:00 2001 From: chaotian <108248800+charliettxx@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:36:50 +0800 Subject: [PATCH] Fix join condition lost after pull up sublink to join After pulling up the sublink to join, the raw join condition may get lost in the rewritten query, potentially leading to incorrect results. Within the SubqueryToJoinWalker() function, we address this issue by adding an 'else' branch to prevent the loss of join clauses and keep them in their original positions. The expected files were regenerated on Cloudberry: the plans the new test prints differ from the ones the upstream commit carried. (cherry picked from open-gpdb commit c06d16b86316e1d66182f5c75a6c3abe6c30acd4) --- src/backend/cdb/cdbsubselect.c | 7 ++ src/test/regress/expected/subselect.out | 85 +++++++++++++++++++ .../regress/expected/subselect_optimizer.out | 84 ++++++++++++++++++ src/test/regress/sql/subselect.sql | 43 ++++++++++ 4 files changed, 219 insertions(+) diff --git a/src/backend/cdb/cdbsubselect.c b/src/backend/cdb/cdbsubselect.c index 43a46eace4b..4e1571ce41c 100644 --- a/src/backend/cdb/cdbsubselect.c +++ b/src/backend/cdb/cdbsubselect.c @@ -451,6 +451,13 @@ SubqueryToJoinWalker(Node *node, ConvertSubqueryToJoinContext *context) */ context->safeToConvert = false; } + else + { + /* + * For other expressions, we should keep them in original place. + */ + context->innerQual = make_and_qual(context->innerQual, node); + } return; } diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index 8c3f7dfe580..ff3f55f3c04 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -2098,3 +2098,88 @@ select (select max((select t.i))) from t; (1 row) drop table t; +-- Fix join condition expression lost as pull up sublink to join. +create table tl1(a int, b int, c int, d int) distributed by (a); +create table tl2(a int, b int, c int, d int) distributed by (a); +create table tl3(a int, b int, c int, d int) distributed by (a); +create table tl4(a int, b int, c int, d int) distributed by (a); +insert into tl1 values (-1, 3, 1, 0); +insert into tl2 values (2, 1, 1, 0); +insert into tl2 values (3, 1, 1, 0); +insert into tl2 values (1, 1, 1, 0); +insert into tl3 values (9, 9, 1, 9); +insert into tl4 values (-1, -1, -1, -1); +explain(costs off, verbose on) +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: tl1.a, tl1.b, tl1.c, tl1.d + -> Hash Join + Output: tl1.a, tl1.b, tl1.c, tl1.d + Inner Unique: true + Hash Cond: ((tl1.b = "Expr_SUBQUERY".csq_c1) AND (tl1.c = "Expr_SUBQUERY".csq_c0)) + -> Seq Scan on public.tl1 + Output: tl1.a, tl1.b, tl1.c, tl1.d + -> Hash + Output: "Expr_SUBQUERY".csq_c1, "Expr_SUBQUERY".csq_c0 + -> Broadcast Motion 3:3 (slice2; segments: 3) + Output: "Expr_SUBQUERY".csq_c1, "Expr_SUBQUERY".csq_c0 + -> Subquery Scan on "Expr_SUBQUERY" + Output: "Expr_SUBQUERY".csq_c1, "Expr_SUBQUERY".csq_c0 + -> Finalize GroupAggregate + Output: tl2.b, max(tl2.a) + Group Key: tl2.b + -> Sort + Output: tl2.b, (PARTIAL max(tl2.a)) + Sort Key: tl2.b + -> Redistribute Motion 3:3 (slice3; segments: 3) + Output: tl2.b, (PARTIAL max(tl2.a)) + Hash Key: tl2.b + -> Streaming Partial HashAggregate + Output: tl2.b, PARTIAL max(tl2.a) + Group Key: tl2.b + -> Hash Join + Output: tl2.b, tl2.a + Hash Cond: (tl4.d = tl2.d) + -> Broadcast Motion 3:3 (slice4; segments: 3) + Output: tl4.d + -> Seq Scan on public.tl4 + Output: tl4.d + -> Hash + Output: tl2.b, tl2.a, tl2.d + -> Seq Scan on public.tl2 + Output: tl2.b, tl2.a, tl2.d + Settings: gp_cte_sharing = 'off', optimizer = 'off' + Optimizer: Postgres query optimizer +(39 rows) + +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + a | b | c | d +---+---+---+--- +(0 rows) + +drop table tl1; +drop table tl2; +drop table tl3; +drop table tl4; diff --git a/src/test/regress/expected/subselect_optimizer.out b/src/test/regress/expected/subselect_optimizer.out index 5b20a89461a..0419221a747 100644 --- a/src/test/regress/expected/subselect_optimizer.out +++ b/src/test/regress/expected/subselect_optimizer.out @@ -2185,3 +2185,87 @@ select (select max((select t.i))) from t; (1 row) drop table t; +-- Fix join condition expression lost as pull up sublink to join. +create table tl1(a int, b int, c int, d int) distributed by (a); +create table tl2(a int, b int, c int, d int) distributed by (a); +create table tl3(a int, b int, c int, d int) distributed by (a); +create table tl4(a int, b int, c int, d int) distributed by (a); +insert into tl1 values (-1, 3, 1, 0); +insert into tl2 values (2, 1, 1, 0); +insert into tl2 values (3, 1, 1, 0); +insert into tl2 values (1, 1, 1, 0); +insert into tl3 values (9, 9, 1, 9); +insert into tl4 values (-1, -1, -1, -1); +explain(costs off, verbose on) +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + QUERY PLAN +------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: tl1.a, tl1.b, tl1.c, tl1.d + -> Hash Join + Output: tl1.a, tl1.b, tl1.c, tl1.d + Hash Cond: ((tl1.b = (max(tl2.a))) AND (tl1.c = tl2.b)) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Output: tl1.a, tl1.b, tl1.c, tl1.d + Hash Key: tl1.c + -> Seq Scan on public.tl1 + Output: tl1.a, tl1.b, tl1.c, tl1.d + -> Hash + Output: (max(tl2.a)), tl2.b + -> GroupAggregate + Output: max(tl2.a), tl2.b + Group Key: tl2.b + -> Sort + Output: tl2.a, tl2.b + Sort Key: tl2.b + -> Redistribute Motion 3:3 (slice3; segments: 3) + Output: tl2.a, tl2.b + Hash Key: tl2.b + -> Hash Join + Output: tl2.a, tl2.b + Hash Cond: (tl2.d = tl4.d) + -> Redistribute Motion 3:3 (slice4; segments: 3) + Output: tl2.a, tl2.b, tl2.d + Hash Key: tl2.d + -> Seq Scan on public.tl2 + Output: tl2.a, tl2.b, tl2.d + -> Hash + Output: tl4.d + -> Redistribute Motion 3:3 (slice5; segments: 3) + Output: tl4.d + Hash Key: tl4.d + -> Seq Scan on public.tl4 + Output: tl4.d + Settings: gp_cte_sharing = 'off', optimizer = 'on' + Optimizer: GPORCA +(38 rows) + +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + a | b | c | d +---+---+---+--- +(0 rows) + +drop table tl1; +drop table tl2; +drop table tl3; +drop table tl4; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 02c11c1aacf..403480a4983 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -1035,3 +1035,46 @@ select (select max((select t.i))) from t; select (select max((select t.i))) from t; drop table t; + +-- Fix join condition expression lost as pull up sublink to join. +create table tl1(a int, b int, c int, d int) distributed by (a); +create table tl2(a int, b int, c int, d int) distributed by (a); +create table tl3(a int, b int, c int, d int) distributed by (a); +create table tl4(a int, b int, c int, d int) distributed by (a); + +insert into tl1 values (-1, 3, 1, 0); +insert into tl2 values (2, 1, 1, 0); +insert into tl2 values (3, 1, 1, 0); +insert into tl2 values (1, 1, 1, 0); +insert into tl3 values (9, 9, 1, 9); +insert into tl4 values (-1, -1, -1, -1); + +explain(costs off, verbose on) +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + +select * from tl1 +where + tl1.b = ( + select + max(tl2.a) + from + tl2 join tl4 + on tl4.d = tl2.d + where + tl2.b = tl1.c + ); + +drop table tl1; +drop table tl2; +drop table tl3; +drop table tl4;