vortex_array/scalar_fn/fns/binary/
boolean.rs1use arrow_array::cast::AsArray;
5use arrow_schema::DataType;
6use vortex_error::VortexResult;
7use vortex_error::vortex_err;
8
9use crate::ArrayRef;
10use crate::DynArray;
11use crate::IntoArray;
12use crate::arrays::Constant;
13use crate::arrays::ConstantArray;
14use crate::arrow::FromArrowArray;
15use crate::arrow::IntoArrowArray;
16use crate::builtins::ArrayBuiltins;
17use crate::dtype::DType;
18use crate::scalar::Scalar;
19use crate::scalar_fn::fns::operators::Operator;
20
21#[deprecated(note = "Use `ArrayBuiltins::binary` instead")]
23pub fn and_kleene(lhs: &ArrayRef, rhs: &ArrayRef) -> VortexResult<ArrayRef> {
24 lhs.to_array().binary(rhs.to_array(), Operator::And)
25}
26
27#[deprecated(note = "Use `ArrayBuiltins::binary` instead")]
29pub fn or_kleene(lhs: &ArrayRef, rhs: &ArrayRef) -> VortexResult<ArrayRef> {
30 lhs.to_array().binary(rhs.to_array(), Operator::Or)
31}
32
33pub(crate) fn execute_boolean(
38 lhs: &ArrayRef,
39 rhs: &ArrayRef,
40 op: Operator,
41) -> VortexResult<ArrayRef> {
42 if let Some(result) = constant_boolean(lhs, rhs, op)? {
43 return Ok(result);
44 }
45 arrow_execute_boolean(lhs.to_array(), rhs.to_array(), op)
46}
47
48fn arrow_execute_boolean(lhs: ArrayRef, rhs: ArrayRef, op: Operator) -> VortexResult<ArrayRef> {
50 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
51
52 let lhs = lhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
53 let rhs = rhs.into_arrow(&DataType::Boolean)?.as_boolean().clone();
54
55 let array = match op {
56 Operator::And => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
57 Operator::Or => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
58 other => return Err(vortex_err!("Not a boolean operator: {other}")),
59 };
60
61 ArrayRef::from_arrow(&array, nullable)
62}
63
64fn constant_boolean(
66 lhs: &ArrayRef,
67 rhs: &ArrayRef,
68 op: Operator,
69) -> VortexResult<Option<ArrayRef>> {
70 let (Some(lhs), Some(rhs)) = (lhs.as_opt::<Constant>(), rhs.as_opt::<Constant>()) else {
71 return Ok(None);
72 };
73
74 let length = lhs.len();
75 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
76 let lhs_val = lhs.scalar().as_bool().value();
77 let rhs_val = rhs
78 .scalar()
79 .as_bool_opt()
80 .ok_or_else(|| vortex_err!("expected rhs to be boolean"))?
81 .value();
82
83 let result = match op {
84 Operator::And => match (lhs_val, rhs_val) {
85 (Some(false), _) | (_, Some(false)) => Some(false),
86 (None, _) | (_, None) => None,
87 (Some(l), Some(r)) => Some(l & r),
88 },
89 Operator::Or => match (lhs_val, rhs_val) {
90 (Some(true), _) | (_, Some(true)) => Some(true),
91 (None, _) | (_, None) => None,
92 (Some(l), Some(r)) => Some(l | r),
93 },
94 other => return Err(vortex_err!("Not a boolean operator: {other}")),
95 };
96
97 let scalar = result
98 .map(|b| Scalar::bool(b, nullable.into()))
99 .unwrap_or_else(|| Scalar::null(DType::Bool(nullable.into())));
100
101 Ok(Some(ConstantArray::new(scalar, length).into_array()))
102}
103
104#[cfg(test)]
105mod tests {
106 use rstest::rstest;
107
108 use crate::ArrayRef;
109 use crate::IntoArray;
110 use crate::arrays::BoolArray;
111 use crate::builtins::ArrayBuiltins;
112 use crate::canonical::ToCanonical;
113 use crate::scalar_fn::fns::operators::Operator;
114
115 #[rstest]
116 #[case(
117 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
118 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
119 )]
120 #[case(
121 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
122 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
123 )]
124 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
125 let r = lhs.binary(rhs, Operator::Or).unwrap();
126 let r = r.to_bool().into_array();
127
128 let v0 = r.scalar_at(0).unwrap().as_bool().value();
129 let v1 = r.scalar_at(1).unwrap().as_bool().value();
130 let v2 = r.scalar_at(2).unwrap().as_bool().value();
131 let v3 = r.scalar_at(3).unwrap().as_bool().value();
132
133 assert!(v0.unwrap());
134 assert!(v1.unwrap());
135 assert!(v2.unwrap());
136 assert!(!v3.unwrap());
137 }
138
139 #[rstest]
140 #[case(
141 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
142 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
143 )]
144 #[case(
145 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
146 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
147 )]
148 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
149 let r = lhs
150 .binary(rhs, Operator::And)
151 .unwrap()
152 .to_bool()
153 .into_array();
154
155 let v0 = r.scalar_at(0).unwrap().as_bool().value();
156 let v1 = r.scalar_at(1).unwrap().as_bool().value();
157 let v2 = r.scalar_at(2).unwrap().as_bool().value();
158 let v3 = r.scalar_at(3).unwrap().as_bool().value();
159
160 assert!(v0.unwrap());
161 assert!(!v1.unwrap());
162 assert!(!v2.unwrap());
163 assert!(!v3.unwrap());
164 }
165}