44use std:: ops:: Not ;
55
66use vortex_array:: arrays:: { BoolArray , ConstantArray } ;
7+ use vortex_array:: stats:: Stat ;
78use vortex_array:: { Array , ArrayRef , DeserializeMetadata , EmptyMetadata , IntoArray } ;
89use vortex_dtype:: { DType , Nullability } ;
910use vortex_error:: { VortexResult , vortex_bail} ;
1011use vortex_mask:: Mask ;
1112
1213use 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
1519vtable ! ( 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 {
119129mod 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