Skip to content

Commit 5e20099

Browse files
committed
chore: add residual predicate NULL semantics tests
1 parent 06d5239 commit 5e20099

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

src/query/stream/package.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)