Skip to content

Commit 1a824de

Browse files
committed
Support NULL literals in where clause (apache#11266)
* Try fix where clause incorrectly reject NULL literal * check null in filter
1 parent c3426c0 commit 1a824de

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

datafusion/expr/src/logical_plan/plan.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2123,7 +2123,8 @@ impl Filter {
21232123
// construction (such as with correlated subqueries) so we make a best effort here and
21242124
// ignore errors resolving the expression against the schema.
21252125
if let Ok(predicate_type) = predicate.get_type(input.schema()) {
2126-
if predicate_type != DataType::Boolean {
2126+
// Interpret NULL as a missing boolean value.
2127+
if predicate_type != DataType::Boolean && predicate_type != DataType::Null {
21272128
return plan_err!(
21282129
"Cannot create filter with non-boolean predicate '{predicate}' returning {predicate_type}"
21292130
);

datafusion/physical-plan/src/filter.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ use crate::{
3131
metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet},
3232
DisplayFormatType, ExecutionPlan,
3333
};
34-
3534
use arrow::compute::filter_record_batch;
3635
use arrow::datatypes::{DataType, SchemaRef};
3736
use arrow::record_batch::RecordBatch;
38-
use datafusion_common::cast::as_boolean_array;
37+
use arrow_array::{Array, BooleanArray};
38+
use datafusion_common::cast::{as_boolean_array, as_null_array};
3939
use datafusion_common::stats::Precision;
40-
use datafusion_common::{plan_err, DataFusionError, Result};
40+
use datafusion_common::{internal_err, plan_err, DataFusionError, Result};
4141
use datafusion_execution::TaskContext;
4242
use datafusion_expr::Operator;
4343
use datafusion_physical_expr::expressions::BinaryExpr;
@@ -84,6 +84,19 @@ impl FilterExec {
8484
cache,
8585
})
8686
}
87+
DataType::Null => {
88+
let default_selectivity = 0;
89+
let cache =
90+
Self::compute_properties(&input, &predicate, default_selectivity)?;
91+
92+
Ok(Self {
93+
predicate,
94+
input: input.clone(),
95+
metrics: ExecutionPlanMetricsSet::new(),
96+
default_selectivity,
97+
cache,
98+
})
99+
}
87100
other => {
88101
plan_err!("Filter predicate must return boolean values, not {other:?}")
89102
}
@@ -355,9 +368,23 @@ pub(crate) fn batch_filter(
355368
.evaluate(batch)
356369
.and_then(|v| v.into_array(batch.num_rows()))
357370
.and_then(|array| {
358-
Ok(as_boolean_array(&array)?)
359-
// apply filter array to record batch
360-
.and_then(|filter_array| Ok(filter_record_batch(batch, filter_array)?))
371+
let filter_array = match as_boolean_array(&array) {
372+
Ok(boolean_array) => {
373+
Ok(boolean_array.to_owned())
374+
},
375+
Err(_) => {
376+
let Ok(null_array) = as_null_array(&array) else {
377+
return internal_err!("Cannot create filter_array from non-boolean predicates, unable to continute");
378+
};
379+
380+
// if the predicate is null, then the result is also null
381+
Ok::<BooleanArray, DataFusionError>(BooleanArray::new_null(
382+
null_array.len(),
383+
))
384+
}
385+
}?;
386+
387+
Ok(filter_record_batch(batch, &filter_array)?)
361388
})
362389
}
363390

datafusion/sqllogictest/test_files/misc.slt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,17 @@ query TT?
2424
select 'foo', '', NULL
2525
----
2626
foo (empty) NULL
27+
28+
# Where clause accept NULL literal
29+
query I
30+
select 1 where NULL
31+
----
32+
33+
query I
34+
select 1 where NULL and 1 = 1
35+
----
36+
37+
query I
38+
select 1 where NULL or 1 = 1
39+
----
40+
1

0 commit comments

Comments
 (0)