@@ -923,6 +923,123 @@ mod tests {
923923 }
924924 }
925925
926+ #[ test]
927+ fn residual_cmp_null_yields_unknown ( ) {
928+ let schema = Arc :: new ( Schema :: new ( vec ! [ Field :: new( "v" , DataType :: Int64 , true ) ] ) ) ;
929+ let evaluator = ResidualEvaluator :: new ( & schema) ;
930+
931+ let row_null = DynRow ( vec ! [ Some ( DynCell :: Null ) ] ) ;
932+ let predicate = Expr :: gt ( "v" , ScalarValue :: from ( 0i64 ) ) ;
933+ let outcome = evaluator
934+ . evaluate_expr ( & predicate, & row_null)
935+ . expect ( "evaluate predicate" ) ;
936+ assert_eq ! ( outcome, TriState :: Unknown ) ;
937+
938+ let row_value = DynRow ( vec ! [ Some ( DynCell :: I64 ( 5 ) ) ] ) ;
939+ let predicate_null = Expr :: eq ( "v" , ScalarValue :: Int64 ( None ) ) ;
940+ let outcome_null = evaluator
941+ . evaluate_expr ( & predicate_null, & row_value)
942+ . expect ( "evaluate predicate" ) ;
943+ assert_eq ! ( outcome_null, TriState :: Unknown ) ;
944+ }
945+
946+ #[ test]
947+ fn residual_in_list_null_semantics ( ) {
948+ let schema = Arc :: new ( Schema :: new ( vec ! [ Field :: new( "v" , DataType :: Int64 , true ) ] ) ) ;
949+ let evaluator = ResidualEvaluator :: new ( & schema) ;
950+
951+ let row_null = DynRow ( vec ! [ Some ( DynCell :: Null ) ] ) ;
952+ let predicate = Expr :: in_list ( "v" , vec ! [ ScalarValue :: from( 1i64 ) , ScalarValue :: from( 2i64 ) ] ) ;
953+ let outcome = evaluator
954+ . evaluate_expr ( & predicate, & row_null)
955+ . expect ( "evaluate predicate" ) ;
956+ assert_eq ! ( outcome, TriState :: Unknown ) ;
957+
958+ let row_value = DynRow ( vec ! [ Some ( DynCell :: I64 ( 5 ) ) ] ) ;
959+ let predicate_with_null =
960+ Expr :: in_list ( "v" , vec ! [ ScalarValue :: from( 1i64 ) , ScalarValue :: Int64 ( None ) ] ) ;
961+ let outcome_with_null = evaluator
962+ . evaluate_expr ( & predicate_with_null, & row_value)
963+ . expect ( "evaluate predicate" ) ;
964+ assert_eq ! ( outcome_with_null, TriState :: Unknown ) ;
965+
966+ let predicate_match =
967+ Expr :: in_list ( "v" , vec ! [ ScalarValue :: Int64 ( None ) , ScalarValue :: from( 5i64 ) ] ) ;
968+ let outcome_match = evaluator
969+ . evaluate_expr ( & predicate_match, & row_value)
970+ . expect ( "evaluate predicate" ) ;
971+ assert_eq ! ( outcome_match, TriState :: True ) ;
972+ }
973+
974+ #[ test]
975+ fn residual_boolean_composition_preserves_unknown ( ) {
976+ let schema = Arc :: new ( Schema :: new ( vec ! [ Field :: new( "v" , DataType :: Int64 , true ) ] ) ) ;
977+ let evaluator = ResidualEvaluator :: new ( & schema) ;
978+ let row = DynRow ( vec ! [ Some ( DynCell :: I64 ( 5 ) ) ] ) ;
979+
980+ let truthy = Expr :: gt ( "v" , ScalarValue :: from ( 0i64 ) ) ;
981+ let falsy = Expr :: lt ( "v" , ScalarValue :: from ( 0i64 ) ) ;
982+ let unknown = Expr :: eq ( "v" , ScalarValue :: Int64 ( None ) ) ;
983+
984+ let and_unknown = Expr :: and ( vec ! [ truthy. clone( ) , unknown. clone( ) ] ) ;
985+ let outcome = evaluator
986+ . evaluate_expr ( & and_unknown, & row)
987+ . expect ( "evaluate predicate" ) ;
988+ assert_eq ! ( outcome, TriState :: Unknown ) ;
989+
990+ let and_false = Expr :: and ( vec ! [ falsy. clone( ) , unknown. clone( ) ] ) ;
991+ let outcome = evaluator
992+ . evaluate_expr ( & and_false, & row)
993+ . expect ( "evaluate predicate" ) ;
994+ assert_eq ! ( outcome, TriState :: False ) ;
995+
996+ let or_unknown = Expr :: or ( vec ! [ falsy. clone( ) , unknown. clone( ) ] ) ;
997+ let outcome = evaluator
998+ . evaluate_expr ( & or_unknown, & row)
999+ . expect ( "evaluate predicate" ) ;
1000+ assert_eq ! ( outcome, TriState :: Unknown ) ;
1001+
1002+ let or_true = Expr :: or ( vec ! [ truthy, unknown. clone( ) ] ) ;
1003+ let outcome = evaluator
1004+ . evaluate_expr ( & or_true, & row)
1005+ . expect ( "evaluate predicate" ) ;
1006+ assert_eq ! ( outcome, TriState :: True ) ;
1007+
1008+ let not_unknown = Expr :: not ( unknown) ;
1009+ let outcome = evaluator
1010+ . evaluate_expr ( & not_unknown, & row)
1011+ . expect ( "evaluate predicate" ) ;
1012+ assert_eq ! ( outcome, TriState :: Unknown ) ;
1013+ }
1014+
1015+ #[ test]
1016+ fn residual_is_null_semantics ( ) {
1017+ let schema = Arc :: new ( Schema :: new ( vec ! [ Field :: new( "v" , DataType :: Int64 , true ) ] ) ) ;
1018+ let evaluator = ResidualEvaluator :: new ( & schema) ;
1019+
1020+ let row_null = DynRow ( vec ! [ Some ( DynCell :: Null ) ] ) ;
1021+ let is_null = Expr :: is_null ( "v" ) ;
1022+ let is_not_null = Expr :: is_not_null ( "v" ) ;
1023+ let outcome = evaluator
1024+ . evaluate_expr ( & is_null, & row_null)
1025+ . expect ( "evaluate predicate" ) ;
1026+ assert_eq ! ( outcome, TriState :: True ) ;
1027+ let outcome = evaluator
1028+ . evaluate_expr ( & is_not_null, & row_null)
1029+ . expect ( "evaluate predicate" ) ;
1030+ assert_eq ! ( outcome, TriState :: False ) ;
1031+
1032+ let row_value = DynRow ( vec ! [ Some ( DynCell :: I64 ( 1 ) ) ] ) ;
1033+ let outcome = evaluator
1034+ . evaluate_expr ( & is_null, & row_value)
1035+ . expect ( "evaluate predicate" ) ;
1036+ assert_eq ! ( outcome, TriState :: False ) ;
1037+ let outcome = evaluator
1038+ . evaluate_expr ( & is_not_null, & row_value)
1039+ . expect ( "evaluate predicate" ) ;
1040+ assert_eq ! ( outcome, TriState :: True ) ;
1041+ }
1042+
9261043 #[ test]
9271044 fn residual_numeric_compare_large_uint64_vs_float64 ( ) {
9281045 let schema = Arc :: new ( Schema :: new ( vec ! [ Field :: new( "v" , DataType :: UInt64 , true ) ] ) ) ;
0 commit comments