Skip to content

Commit 65dacbe

Browse files
authored
fix[vortex-expr]: implement stat_falsification for IsNullExpr (#4980)
Allows pruning chunks for these types of filters. Signed-off-by: Alfonso Subiotto Marques <[email protected]>
1 parent 3ca875d commit 65dacbe

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

vortex-expr/src/exprs/is_null.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
use std::ops::Not;
55

66
use vortex_array::arrays::{BoolArray, ConstantArray};
7+
use vortex_array::stats::Stat;
78
use vortex_array::{Array, ArrayRef, DeserializeMetadata, EmptyMetadata, IntoArray};
89
use vortex_dtype::{DType, Nullability};
910
use vortex_error::{VortexResult, vortex_bail};
1011
use vortex_mask::Mask;
1112

1213
use crate::display::{DisplayAs, DisplayFormat};
13-
use crate::{AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, VTable, vtable};
14+
use crate::{
15+
AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, StatsCatalog, VTable, eq, lit,
16+
vtable,
17+
};
1418

1519
vtable!(IsNull);
1620

@@ -101,7 +105,13 @@ impl DisplayAs for IsNullExpr {
101105
}
102106
}
103107

104-
impl AnalysisExpr for IsNullExpr {}
108+
impl AnalysisExpr for IsNullExpr {
109+
fn stat_falsification(&self, catalog: &mut dyn StatsCatalog) -> Option<ExprRef> {
110+
let field_path = self.child.field_path()?;
111+
let null_count_expr = catalog.stats_ref(&field_path, Stat::NullCount)?;
112+
Some(eq(null_count_expr, lit(0u64)))
113+
}
114+
}
105115

106116
/// Creates an expression that checks for null values.
107117
///
@@ -119,12 +129,15 @@ pub fn is_null(child: ExprRef) -> ExprRef {
119129
mod tests {
120130
use vortex_array::IntoArray;
121131
use vortex_array::arrays::{PrimitiveArray, StructArray};
132+
use vortex_array::stats::Stat;
122133
use vortex_buffer::buffer;
123-
use vortex_dtype::{DType, Nullability};
134+
use vortex_dtype::{DType, Field, FieldPath, FieldPathSet, Nullability};
124135
use vortex_scalar::Scalar;
136+
use vortex_utils::aliases::hash_map::HashMap;
125137

126138
use crate::is_null::is_null;
127-
use crate::{Scope, get_item, root, test_harness};
139+
use crate::pruning::checked_pruning_expr;
140+
use crate::{HashSet, Scope, col, eq, get_item, lit, root, test_harness};
128141

129142
#[test]
130143
fn dtype() {
@@ -229,4 +242,24 @@ mod tests {
229242
let expr2 = is_null(root());
230243
assert_eq!(expr2.to_string(), "is_null($)");
231244
}
245+
246+
#[test]
247+
fn test_is_null_falsification() {
248+
let expr = is_null(col("a"));
249+
250+
let (pruning_expr, st) = checked_pruning_expr(
251+
&expr,
252+
&FieldPathSet::from_iter([FieldPath::from_iter([
253+
Field::Name("a".into()),
254+
Field::Name("null_count".into()),
255+
])]),
256+
)
257+
.unwrap();
258+
259+
assert_eq!(&pruning_expr, &eq(col("a_null_count"), lit(0u64)));
260+
assert_eq!(
261+
st.map(),
262+
&HashMap::from_iter([(FieldPath::from_name("a"), HashSet::from([Stat::NullCount]))])
263+
);
264+
}
232265
}

0 commit comments

Comments
 (0)