Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX : Incorrect NULL handling in BETWEEN expression #14007

Merged
merged 5 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions datafusion/optimizer/src/analyzer/type_coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
))
})?;
let high_type = high.get_type(self.schema)?;
let high_coerced_type = comparison_coercion(&expr_type, &low_type)
let high_coerced_type = comparison_coercion(&expr_type, &high_type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 -- nice fix

.ok_or_else(|| {
DataFusionError::Internal(format!(
"Failed to coerce types {expr_type} and {high_type} in BETWEEN expression"
Expand Down Expand Up @@ -1458,7 +1458,7 @@ mod test {
let empty = empty_with_type(Utf8);
let plan = LogicalPlan::Filter(Filter::try_new(expr, empty)?);
let expected =
"Filter: a BETWEEN Utf8(\"2002-05-08\") AND CAST(CAST(Utf8(\"2002-05-08\") AS Date32) + IntervalYearMonth(\"1\") AS Utf8)\
"Filter: CAST(a AS Date32) BETWEEN CAST(Utf8(\"2002-05-08\") AS Date32) AND CAST(Utf8(\"2002-05-08\") AS Date32) + IntervalYearMonth(\"1\")\
\n EmptyRelation";
assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, expected)
}
Expand All @@ -1480,6 +1480,17 @@ mod test {
assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, expected)
}

#[test]
fn between_null() -> Result<()> {
let expr = lit(ScalarValue::Null).between(lit(ScalarValue::Null), lit(2i64));
let empty = empty();
let plan = LogicalPlan::Filter(Filter::try_new(expr, empty)?);
let expected =
"Filter: CAST(NULL AS Int64) BETWEEN CAST(NULL AS Int64) AND Int64(2)\
\n EmptyRelation";
assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()), plan, expected)
}

#[test]
fn is_bool_for_type_coercion() -> Result<()> {
// is true
Expand Down
5 changes: 3 additions & 2 deletions datafusion/optimizer/tests/optimizer_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ fn subquery_filter_with_cast() -> Result<()> {
\n SubqueryAlias: __scalar_sq_1\
\n Aggregate: groupBy=[[]], aggr=[[avg(CAST(test.col_int32 AS Float64))]]\
\n Projection: test.col_int32\
\n Filter: test.col_utf8 >= Utf8(\"2002-05-08\") AND test.col_utf8 <= Utf8(\"2002-05-13\")\
\n TableScan: test projection=[col_int32, col_utf8]";
\n Filter: __common_expr_5 >= Date32(\"2002-05-08\") AND __common_expr_5 <= Date32(\"2002-05-13\")\
\n Projection: CAST(test.col_utf8 AS Date32) AS __common_expr_5, test.col_int32\
\n TableScan: test projection=[col_int32, col_utf8]";
assert_eq!(expected, format!("{plan}"));
Ok(())
}
Expand Down
17 changes: 17 additions & 0 deletions datafusion/sqllogictest/test_files/expr.slt
Original file line number Diff line number Diff line change
Expand Up @@ -1963,3 +1963,20 @@ drop table t;

statement ok
set datafusion.sql_parser.dialect = 'Generic';

# test between expression with null
query I
select 1 where null between null and null;
----

query T
select 'A' where null between null and null;
----

query I
select 1 where null between null and 2;
----

query T
select 'A' where null between 2 and null;
---
Loading