Skip to content

Commit 02c3618

Browse files
authored
Merge pull request #7662 from xudong963/fix_left_join
fix: left join returns wrong answer
2 parents 6aa586b + f0e5888 commit 02c3618

File tree

8 files changed

+47
-15
lines changed

8 files changed

+47
-15
lines changed

src/query/service/src/sql/planner/binder/join.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use common_exception::Result;
2727
use crate::sessions::TableContext;
2828
use crate::sql::binder::scalar_common::split_conjunctions;
2929
use crate::sql::binder::scalar_common::split_equivalent_predicate;
30-
use crate::sql::binder::scalar_common::wrap_cast_if_needed;
30+
use crate::sql::binder::wrap_cast;
3131
use crate::sql::binder::Visibility;
3232
use crate::sql::normalize_identifier;
3333
use crate::sql::optimizer::ColumnSet;
@@ -448,9 +448,13 @@ impl<'a> JoinConditionResolver<'a> {
448448
// Bump types of left conditions and right conditions
449449
let left_type = left.data_type();
450450
let right_type = right.data_type();
451-
let least_super_type = compare_coercion(&left_type, &right_type)?;
452-
left = wrap_cast_if_needed(left, &least_super_type);
453-
right = wrap_cast_if_needed(right, &least_super_type);
451+
if left_type.ne(&right_type) {
452+
let least_super_type = compare_coercion(&left_type, &right_type)?;
453+
// Wrap cast for both left and right, `cast` can change the physical type of the data block
454+
// Related issue: https://github.com/datafuselabs/databend/issues/7650
455+
left = wrap_cast(left, &least_super_type);
456+
right = wrap_cast(right, &least_super_type);
457+
}
454458

455459
if left_used_columns.is_subset(&left_columns)
456460
&& right_used_columns.is_subset(&right_columns)

src/query/service/src/sql/planner/binder/scalar_common.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ pub fn wrap_cast_if_needed(scalar: Scalar, target_type: &DataTypeImpl) -> Scalar
9898
}
9999
}
100100

101+
pub fn wrap_cast(scalar: Scalar, target_type: &DataTypeImpl) -> Scalar {
102+
CastExpr {
103+
from_type: Box::new(scalar.data_type()),
104+
argument: Box::new(scalar),
105+
target_type: Box::new(target_type.clone()),
106+
}
107+
.into()
108+
}
109+
101110
pub fn satisfied_by(scalar: &Scalar, prop: &RelationalProperty) -> bool {
102111
scalar.used_columns().is_subset(&prop.output_columns)
103112
}

src/query/service/src/sql/planner/optimizer/heuristic/decorrelate.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use common_datavalues::NullableType;
2121
use common_exception::ErrorCode;
2222
use common_exception::Result;
2323

24-
use crate::sql::binder::wrap_cast_if_needed;
24+
use crate::sql::binder::wrap_cast;
2525
use crate::sql::binder::JoinPredicate;
2626
use crate::sql::binder::Visibility;
2727
use crate::sql::optimizer::heuristic::subquery_rewriter::FlattenInfo;
@@ -172,9 +172,14 @@ impl SubqueryRewriter {
172172
}
173173

174174
JoinPredicate::Both { left, right } => {
175+
if left.data_type().eq(&right.data_type()) {
176+
left_conditions.push(left.clone());
177+
right_conditions.push(right.clone());
178+
continue;
179+
}
175180
let join_type = compare_coercion(&left.data_type(), &right.data_type())?;
176-
let left = wrap_cast_if_needed(left.clone(), &join_type);
177-
let right = wrap_cast_if_needed(right.clone(), &join_type);
181+
let left = wrap_cast(left.clone(), &join_type);
182+
let right = wrap_cast(right.clone(), &join_type);
178183
left_conditions.push(left);
179184
right_conditions.push(right);
180185
}

src/query/service/src/sql/planner/optimizer/rule/rewrite/rule_push_down_filter_join.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use common_datavalues::type_coercion::compare_coercion;
1616
use common_exception::Result;
1717

18-
use crate::sql::binder::wrap_cast_if_needed;
18+
use crate::sql::binder::wrap_cast;
1919
use crate::sql::binder::JoinPredicate;
2020
use crate::sql::optimizer::rule::Rule;
2121
use crate::sql::optimizer::rule::TransformState;
@@ -117,10 +117,15 @@ impl Rule for RulePushDownFilterJoin {
117117
if join.join_type == JoinType::Cross {
118118
join.join_type = JoinType::Inner;
119119
}
120-
let left = wrap_cast_if_needed(left.clone(), &join_key_type);
121-
let right = wrap_cast_if_needed(right.clone(), &join_key_type);
122-
join.left_conditions.push(left);
123-
join.right_conditions.push(right);
120+
if left.data_type().ne(&right.data_type()) {
121+
let left = wrap_cast(left.clone(), &join_key_type);
122+
let right = wrap_cast(right.clone(), &join_key_type);
123+
join.left_conditions.push(left);
124+
join.right_conditions.push(right);
125+
} else {
126+
join.left_conditions.push(left.clone());
127+
join.right_conditions.push(right.clone());
128+
}
124129
need_push = true;
125130
} else {
126131
original_predicates.push(predicate);

tests/logictest/suites/crdb/join

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,15 @@ B NULL B
661661
C NULL C
662662
E NULL E
663663

664+
statement query ITIT
665+
SELECT * FROM str1 LEFT OUTER JOIN str2 ON str1.s = str2.s order by str1.a;
666+
667+
----
668+
1 a NULL NULL
669+
2 A 1 A
670+
3 c NULL NULL
671+
4 D NULL NULL
672+
664673
-- statement query
665674
-- SELECT s, str1.s, str2.s FROM str1 FULL OUTER JOIN str2 USING(s);
666675

tests/logictest/suites/mode/cluster/exchange.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Exchange
129129
└── HashJoin
130130
├── join type: INNER
131131
├── build keys: [CAST(numbers.number (#4) AS BIGINT UNSIGNED NULL)]
132-
├── probe keys: [number (#1)]
132+
├── probe keys: [CAST(number (#1) AS BIGINT UNSIGNED NULL)]
133133
├── filters: []
134134
├── Exchange(Build)
135135
│ ├── exchange type: Hash(CAST(numbers.number (#4) AS BIGINT UNSIGNED NULL))

tests/logictest/suites/mode/standalone/explain/limit.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Limit
148148
├── sort keys: [c1 ASC NULLS LAST]
149149
└── HashJoin
150150
├── join type: LEFT OUTER
151-
├── build keys: [c (#5)]
151+
├── build keys: [CAST(c (#5) AS BIGINT UNSIGNED NULL)]
152152
├── probe keys: [CAST(c1 (#1) AS BIGINT UNSIGNED NULL)]
153153
├── filters: []
154154
├── Project(Build)

tests/logictest/suites/mode/standalone/explain/select_limit_offset.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ Limit
141141
├── offset: 1
142142
└── HashJoin
143143
├── join type: LEFT OUTER
144-
├── build keys: [t1.a (#1)]
144+
├── build keys: [CAST(t1.a (#1) AS INT NULL)]
145145
├── probe keys: [CAST(t.a (#0) AS INT NULL)]
146146
├── filters: []
147147
├── Limit(Build)

0 commit comments

Comments
 (0)