Skip to content

Commit 7e89ae7

Browse files
Hyper-FFclaude
authored andcommitted
[BugFix] Cast COALESCE children to common type in JOIN USING transformer (#72338)
Signed-off-by: Hyper-FF <253014618+Hyper-FF@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> (cherry picked from commit cb2fd87)
1 parent d9f4c90 commit 7e89ae7

4 files changed

Lines changed: 138 additions & 2 deletions

File tree

fe/fe-core/src/main/java/com/starrocks/sql/optimizer/transformer/RelationTransformer.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,14 +1386,19 @@ private ScalarOperator createCoalesceOperator(ScalarOperator leftOp, ScalarOpera
13861386
commonType = leftType;
13871387
}
13881388

1389-
Type[] argTypes = new Type[] {leftType, rightType};
1389+
ScalarOperator leftCasted = leftType.equals(commonType)
1390+
? leftOp : foldCast(new CastOperator(commonType, leftOp, true));
1391+
ScalarOperator rightCasted = rightType.equals(commonType)
1392+
? rightOp : foldCast(new CastOperator(commonType, rightOp, true));
1393+
1394+
Type[] argTypes = new Type[] {commonType, commonType};
13901395
com.starrocks.catalog.Function coalesceFunction =
13911396
com.starrocks.sql.ast.expression.ExprUtils.getBuiltinFunction(
13921397
FunctionSet.COALESCE, argTypes,
13931398
com.starrocks.catalog.Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
13941399

13951400
return new CallOperator(FunctionSet.COALESCE, commonType,
1396-
Lists.newArrayList(leftOp, rightOp), coalesceFunction);
1401+
Lists.newArrayList(leftCasted, rightCasted), coalesceFunction);
13971402
}
13981403

13991404
/**

fe/fe-core/src/test/java/com/starrocks/sql/plan/JoinTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,22 @@ public void testFullOuterJoinWithUsing() throws Exception {
495495
" | equal join conjunct: 3: v6 = 14: v6");
496496
}
497497

498+
@Test
499+
public void testFullOuterJoinUsingWithMismatchedTypes() throws Exception {
500+
// Two USING columns with the same name but different types (DATETIME vs VARCHAR).
501+
// The synthesized COALESCE must wrap the side whose type does not equal the
502+
// resolved common type in an explicit CAST, so that arg types match the function
503+
// signature.
504+
String sql = "select v from " +
505+
" (select id_datetime as v from test_all_type) a " +
506+
" full outer join " +
507+
" (select t1a as v from test_all_type) b using(v)";
508+
String plan = getFragmentPlan(sql);
509+
assertContains(plan, "join op: FULL OUTER JOIN");
510+
// The DATETIME side must be cast to the common (string) type inside the coalesce.
511+
assertContains(plan, "coalesce(8: id_datetime, CAST(11: t1a AS DATETIME))");
512+
}
513+
498514
@Test
499515
public void testJoinAssociativityConst() throws Exception {
500516
String sql = "SELECT x0.*\n" +
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
-- name: test_full_outer_join_using_mismatched_types
2+
DROP DATABASE IF EXISTS test_full_outer_join_using_mismatched_types_${uuid0};
3+
-- result:
4+
-- !result
5+
CREATE DATABASE test_full_outer_join_using_mismatched_types_${uuid0};
6+
-- result:
7+
-- !result
8+
USE test_full_outer_join_using_mismatched_types_${uuid0};
9+
-- result:
10+
-- !result
11+
CREATE TABLE left_t (
12+
k INT,
13+
dt DATETIME,
14+
region VARCHAR(255),
15+
city VARCHAR(255)
16+
) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 PROPERTIES ("replication_num" = "1");
17+
-- result:
18+
-- !result
19+
CREATE TABLE right_t (
20+
k INT,
21+
dt VARCHAR(255),
22+
region VARCHAR(255),
23+
city VARCHAR(255)
24+
) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 PROPERTIES ("replication_num" = "1");
25+
-- result:
26+
-- !result
27+
INSERT INTO left_t VALUES
28+
(1, '2024-01-01 00:00:00', 'us-east', 'NYC'),
29+
(2, '2024-02-01 00:00:00', 'us-west', 'SFO'),
30+
(3, NULL, 'eu', 'LON');
31+
-- result:
32+
-- !result
33+
INSERT INTO right_t VALUES
34+
(1, '2024-01-01 00:00:00', 'us-east', 'NYC'),
35+
(4, '2024-03-01 00:00:00', 'apac', 'TYO'),
36+
(5, NULL, NULL, 'BER');
37+
-- result:
38+
-- !result
39+
SELECT count(*) FROM left_t FULL OUTER JOIN right_t USING(dt, region, city);
40+
-- result:
41+
5
42+
-- !result
43+
SELECT count(*) FROM left_t FULL OUTER JOIN right_t USING(dt);
44+
-- result:
45+
5
46+
-- !result
47+
SELECT
48+
coalesce(left_t.k, right_t.k) AS k,
49+
region,
50+
city
51+
FROM left_t FULL OUTER JOIN right_t USING(region, city)
52+
ORDER BY k, region, city;
53+
-- result:
54+
1 us-east NYC
55+
2 us-west SFO
56+
3 eu LON
57+
4 apac TYO
58+
5 None BER
59+
-- !result
60+
DROP DATABASE test_full_outer_join_using_mismatched_types_${uuid0};
61+
-- result:
62+
-- !result
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-- name: test_full_outer_join_using_mismatched_types
2+
-- Regression test: FULL OUTER JOIN ... USING(col) where the same-named
3+
-- column has a different type on each side (e.g. DATETIME vs VARCHAR).
4+
-- The planner must wrap the side whose type differs from the resolved
5+
-- common type in an explicit CAST so that the synthesized COALESCE has
6+
-- children matching its function signature.
7+
DROP DATABASE IF EXISTS test_full_outer_join_using_mismatched_types_${uuid0};
8+
CREATE DATABASE test_full_outer_join_using_mismatched_types_${uuid0};
9+
USE test_full_outer_join_using_mismatched_types_${uuid0};
10+
11+
CREATE TABLE left_t (
12+
k INT,
13+
dt DATETIME,
14+
region VARCHAR(255),
15+
city VARCHAR(255)
16+
) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 PROPERTIES ("replication_num" = "1");
17+
18+
CREATE TABLE right_t (
19+
k INT,
20+
dt VARCHAR(255),
21+
region VARCHAR(255),
22+
city VARCHAR(255)
23+
) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 PROPERTIES ("replication_num" = "1");
24+
25+
INSERT INTO left_t VALUES
26+
(1, '2024-01-01 00:00:00', 'us-east', 'NYC'),
27+
(2, '2024-02-01 00:00:00', 'us-west', 'SFO'),
28+
(3, NULL, 'eu', 'LON');
29+
30+
INSERT INTO right_t VALUES
31+
(1, '2024-01-01 00:00:00', 'us-east', 'NYC'),
32+
(4, '2024-03-01 00:00:00', 'apac', 'TYO'),
33+
(5, NULL, NULL, 'BER');
34+
35+
-- The query must not error out: USING(dt, region, city) requires the
36+
-- planner to coalesce a DATETIME with a VARCHAR for the `dt` column.
37+
SELECT count(*) FROM left_t FULL OUTER JOIN right_t USING(dt, region, city);
38+
39+
-- Single-key cross-typed USING: dt is DATETIME on the left, VARCHAR on
40+
-- the right. Only the row whose datetime/string round-trip matches
41+
-- joins; the rest become outer rows.
42+
SELECT count(*) FROM left_t FULL OUTER JOIN right_t USING(dt);
43+
44+
-- Verify the synthesized COALESCE column has well-typed values for the
45+
-- matched / outer rows. Order by the deterministic key column.
46+
SELECT
47+
coalesce(left_t.k, right_t.k) AS k,
48+
region,
49+
city
50+
FROM left_t FULL OUTER JOIN right_t USING(region, city)
51+
ORDER BY k, region, city;
52+
53+
DROP DATABASE test_full_outer_join_using_mismatched_types_${uuid0};

0 commit comments

Comments
 (0)