Skip to content

Commit 84ce8cf

Browse files
authored
Merge e5a1838 into 17886f3
2 parents 17886f3 + e5a1838 commit 84ce8cf

File tree

3 files changed

+204
-21
lines changed

3 files changed

+204
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
### Added
1212

1313
### Fixed
14+
- Evaluation of logical ops (AND, OR, NOT) with `MISSING` argument
1415

1516
## [0.7.1] - 2024-03-15
1617
### Changed

partiql-eval/src/eval/expr/operators.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,25 @@ impl BindEvalExpr for EvalOpUnary {
8080
args: Vec<Box<dyn EvalExpr>>,
8181
) -> Result<Box<dyn EvalExpr>, BindError> {
8282
let any_num = PartiqlType::any_of(TYPE_NUMERIC_TYPES);
83-
84-
let unop = |types, f: fn(&Value) -> Value| {
85-
UnaryValueExpr::create_typed::<{ STRICT }, _>(types, args, f)
86-
};
83+
type CheckNull<const STRICT: bool> = DefaultArgChecker<STRICT, PropagateNull<true>>;
84+
type CheckMissing<const STRICT: bool> = DefaultArgChecker<STRICT, PropagateMissing<true>>;
8785

8886
match self {
89-
EvalOpUnary::Pos => unop([any_num], std::clone::Clone::clone),
90-
EvalOpUnary::Neg => unop([any_num], |operand| -operand),
91-
EvalOpUnary::Not => unop([TYPE_BOOL], |operand| !operand),
87+
EvalOpUnary::Pos => UnaryValueExpr::create_checked::<
88+
{ STRICT },
89+
CheckMissing<STRICT>,
90+
fn(&Value) -> Value,
91+
>([any_num], args, std::clone::Clone::clone),
92+
EvalOpUnary::Neg => UnaryValueExpr::create_checked::<
93+
{ STRICT },
94+
CheckMissing<STRICT>,
95+
fn(&Value) -> Value,
96+
>([any_num], args, |operand| -operand),
97+
EvalOpUnary::Not => UnaryValueExpr::create_checked::<
98+
{ STRICT },
99+
CheckNull<STRICT>,
100+
fn(&Value) -> Value,
101+
>([TYPE_BOOL], args, |operand| !operand),
92102
}
93103
}
94104
}
@@ -138,7 +148,7 @@ impl<const TARGET: bool, OnMissing: ArgShortCircuit> ArgChecker
138148
) -> ArgCheckControlFlow<Value, Cow<'a, Value>> {
139149
match arg.borrow() {
140150
Boolean(b) if b == &TARGET => ArgCheckControlFlow::ShortCircuit(Value::Boolean(*b)),
141-
Missing => ArgCheckControlFlow::ShortCircuit(OnMissing::propagate()),
151+
Missing => ArgCheckControlFlow::Propagate(OnMissing::propagate()),
142152
Null => ArgCheckControlFlow::Propagate(Null),
143153
_ => ArgCheckControlFlow::Continue(arg),
144154
}

partiql-eval/src/lib.rs

Lines changed: 185 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ mod tests {
2323
use crate::plan::EvaluationMode;
2424
use partiql_logical::{
2525
BagExpr, BetweenExpr, BinaryOp, BindingsOp, CoalesceExpr, ExprQuery, IsTypeExpr, JoinKind,
26-
ListExpr, LogicalPlan, NullIfExpr, PathComponent, TupleExpr, Type, ValueExpr, VarRefType,
26+
ListExpr, LogicalPlan, NullIfExpr, PathComponent, TupleExpr, Type, UnaryOp, ValueExpr,
27+
VarRefType,
2728
};
2829
use partiql_value as value;
2930
use partiql_value::Value::{Missing, Null};
@@ -641,9 +642,23 @@ mod tests {
641642
}
642643

643644
#[test]
644-
fn and_or_null() {
645+
fn logical_ops() {
645646
#[track_caller]
646-
fn eval_to_null(op: BinaryOp, lhs: Value, rhs: Value) {
647+
fn eval_unary(op: UnaryOp, expr: Value, expected: Value) {
648+
let mut plan = LogicalPlan::new();
649+
let expq = plan.add_operator(BindingsOp::ExprQuery(ExprQuery {
650+
expr: ValueExpr::UnExpr(op, Box::new(ValueExpr::Lit(Box::new(expr)))),
651+
}));
652+
653+
let sink = plan.add_operator(BindingsOp::Sink);
654+
plan.add_flow(expq, sink);
655+
656+
let actual = evaluate(plan, MapBindings::default());
657+
assert_eq!(expected, actual);
658+
}
659+
660+
#[track_caller]
661+
fn eval_binary(op: BinaryOp, lhs: Value, rhs: Value, expected: Value) {
647662
let mut plan = LogicalPlan::new();
648663
let expq = plan.add_operator(BindingsOp::ExprQuery(ExprQuery {
649664
expr: ValueExpr::BinaryExpr(
@@ -656,18 +671,175 @@ mod tests {
656671
let sink = plan.add_operator(BindingsOp::Sink);
657672
plan.add_flow(expq, sink);
658673

659-
let result = evaluate(plan, MapBindings::default());
660-
assert_eq!(result, Value::Null);
674+
let actual = evaluate(plan, MapBindings::default());
675+
assert_eq!(expected, actual);
661676
}
662677

663-
eval_to_null(BinaryOp::And, Value::Null, Value::Boolean(true));
664-
eval_to_null(BinaryOp::And, Value::Missing, Value::Boolean(true));
665-
eval_to_null(BinaryOp::And, Value::Boolean(true), Value::Null);
666-
eval_to_null(BinaryOp::And, Value::Boolean(true), Value::Missing);
667-
eval_to_null(BinaryOp::Or, Value::Null, Value::Boolean(false));
668-
eval_to_null(BinaryOp::Or, Value::Missing, Value::Boolean(false));
669-
eval_to_null(BinaryOp::Or, Value::Boolean(false), Value::Null);
670-
eval_to_null(BinaryOp::Or, Value::Boolean(false), Value::Missing);
678+
// NOT bools only
679+
eval_unary(UnaryOp::Not, Value::Boolean(true), Value::Boolean(false));
680+
eval_unary(UnaryOp::Not, Value::Boolean(false), Value::Boolean(true));
681+
// NOT null propagation
682+
eval_unary(UnaryOp::Not, Value::Null, Value::Null);
683+
eval_unary(UnaryOp::Not, Value::Missing, Value::Null);
684+
685+
// AND/OR bools only
686+
eval_binary(
687+
BinaryOp::And,
688+
Value::Boolean(true),
689+
Value::Boolean(true),
690+
Value::Boolean(true),
691+
);
692+
eval_binary(
693+
BinaryOp::And,
694+
Value::Boolean(false),
695+
Value::Boolean(true),
696+
Value::Boolean(false),
697+
);
698+
eval_binary(
699+
BinaryOp::And,
700+
Value::Boolean(true),
701+
Value::Boolean(false),
702+
Value::Boolean(false),
703+
);
704+
eval_binary(
705+
BinaryOp::And,
706+
Value::Boolean(false),
707+
Value::Boolean(false),
708+
Value::Boolean(false),
709+
);
710+
eval_binary(
711+
BinaryOp::Or,
712+
Value::Boolean(true),
713+
Value::Boolean(true),
714+
Value::Boolean(true),
715+
);
716+
eval_binary(
717+
BinaryOp::Or,
718+
Value::Boolean(true),
719+
Value::Boolean(false),
720+
Value::Boolean(true),
721+
);
722+
eval_binary(
723+
BinaryOp::Or,
724+
Value::Boolean(false),
725+
Value::Boolean(true),
726+
Value::Boolean(true),
727+
);
728+
eval_binary(
729+
BinaryOp::Or,
730+
Value::Boolean(false),
731+
Value::Boolean(false),
732+
Value::Boolean(false),
733+
);
734+
735+
// AND/OR short circuit
736+
eval_binary(
737+
BinaryOp::And,
738+
Value::Boolean(false),
739+
Value::Null,
740+
Value::Boolean(false),
741+
);
742+
eval_binary(
743+
BinaryOp::And,
744+
Value::Boolean(false),
745+
Value::Missing,
746+
Value::Boolean(false),
747+
);
748+
eval_binary(
749+
BinaryOp::And,
750+
Value::Null,
751+
Value::Boolean(false),
752+
Value::Boolean(false),
753+
);
754+
eval_binary(
755+
BinaryOp::And,
756+
Value::Missing,
757+
Value::Boolean(false),
758+
Value::Boolean(false),
759+
);
760+
eval_binary(
761+
BinaryOp::Or,
762+
Value::Boolean(true),
763+
Value::Null,
764+
Value::Boolean(true),
765+
);
766+
eval_binary(
767+
BinaryOp::Or,
768+
Value::Boolean(true),
769+
Value::Missing,
770+
Value::Boolean(true),
771+
);
772+
eval_binary(
773+
BinaryOp::Or,
774+
Value::Null,
775+
Value::Boolean(true),
776+
Value::Boolean(true),
777+
);
778+
eval_binary(
779+
BinaryOp::Or,
780+
Value::Missing,
781+
Value::Boolean(true),
782+
Value::Boolean(true),
783+
);
784+
785+
// AND/OR propagate null
786+
eval_binary(
787+
BinaryOp::And,
788+
Value::Boolean(true),
789+
Value::Null,
790+
Value::Null,
791+
);
792+
eval_binary(
793+
BinaryOp::And,
794+
Value::Boolean(true),
795+
Value::Missing,
796+
Value::Null,
797+
);
798+
eval_binary(
799+
BinaryOp::And,
800+
Value::Null,
801+
Value::Boolean(true),
802+
Value::Null,
803+
);
804+
eval_binary(
805+
BinaryOp::And,
806+
Value::Missing,
807+
Value::Boolean(true),
808+
Value::Null,
809+
);
810+
eval_binary(
811+
BinaryOp::Or,
812+
Value::Boolean(false),
813+
Value::Null,
814+
Value::Null,
815+
);
816+
eval_binary(
817+
BinaryOp::Or,
818+
Value::Boolean(false),
819+
Value::Missing,
820+
Value::Null,
821+
);
822+
eval_binary(
823+
BinaryOp::Or,
824+
Value::Null,
825+
Value::Boolean(false),
826+
Value::Null,
827+
);
828+
eval_binary(
829+
BinaryOp::Or,
830+
Value::Missing,
831+
Value::Boolean(false),
832+
Value::Null,
833+
);
834+
835+
eval_binary(BinaryOp::And, Value::Null, Value::Null, Value::Null);
836+
eval_binary(BinaryOp::And, Value::Null, Value::Missing, Value::Null);
837+
eval_binary(BinaryOp::And, Value::Missing, Value::Null, Value::Null);
838+
eval_binary(BinaryOp::And, Value::Missing, Value::Missing, Value::Null);
839+
eval_binary(BinaryOp::Or, Value::Null, Value::Null, Value::Null);
840+
eval_binary(BinaryOp::Or, Value::Null, Value::Missing, Value::Null);
841+
eval_binary(BinaryOp::Or, Value::Missing, Value::Null, Value::Null);
842+
eval_binary(BinaryOp::Or, Value::Missing, Value::Missing, Value::Null);
671843
}
672844

673845
#[test]

0 commit comments

Comments
 (0)