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::IntoArray;
11use crate::arrays::Constant;
12use crate::arrays::ConstantArray;
13use crate::arrow::ArrowArrayExecutor;
14use crate::arrow::FromArrowArray;
15use crate::builtins::ArrayBuiltins;
16use crate::dtype::DType;
17use crate::executor::ExecutionCtx;
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.clone().binary(rhs.clone(), Operator::And)
25}
26
27#[deprecated(note = "Use `ArrayBuiltins::binary` instead")]
29pub fn or_kleene(lhs: &ArrayRef, rhs: &ArrayRef) -> VortexResult<ArrayRef> {
30 lhs.clone().binary(rhs.clone(), Operator::Or)
31}
32
33pub(crate) fn execute_boolean(
38 lhs: &ArrayRef,
39 rhs: &ArrayRef,
40 op: Operator,
41 ctx: &mut ExecutionCtx,
42) -> VortexResult<ArrayRef> {
43 if let Some(result) = constant_boolean(lhs, rhs, op)? {
44 return Ok(result);
45 }
46 arrow_execute_boolean(lhs.clone(), rhs.clone(), op, ctx)
47}
48
49fn arrow_execute_boolean(
51 lhs: ArrayRef,
52 rhs: ArrayRef,
53 op: Operator,
54 ctx: &mut ExecutionCtx,
55) -> VortexResult<ArrayRef> {
56 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
57
58 let lhs = lhs
59 .execute_arrow(Some(&DataType::Boolean), ctx)?
60 .as_boolean()
61 .clone();
62 let rhs = rhs
63 .execute_arrow(Some(&DataType::Boolean), ctx)?
64 .as_boolean()
65 .clone();
66
67 let array = match op {
68 Operator::And => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
69 Operator::Or => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
70 other => return Err(vortex_err!("Not a boolean operator: {other}")),
71 };
72
73 ArrayRef::from_arrow(&array, nullable)
74}
75
76fn constant_boolean(
78 lhs: &ArrayRef,
79 rhs: &ArrayRef,
80 op: Operator,
81) -> VortexResult<Option<ArrayRef>> {
82 let (Some(lhs), Some(rhs)) = (lhs.as_opt::<Constant>(), rhs.as_opt::<Constant>()) else {
83 return Ok(None);
84 };
85
86 let length = lhs.len();
87 let nullable = lhs.dtype().is_nullable() || rhs.dtype().is_nullable();
88 let lhs_val = lhs.scalar().as_bool().value();
89 let rhs_val = rhs
90 .scalar()
91 .as_bool_opt()
92 .ok_or_else(|| vortex_err!("expected rhs to be boolean"))?
93 .value();
94
95 let result = match op {
96 Operator::And => match (lhs_val, rhs_val) {
97 (Some(false), _) | (_, Some(false)) => Some(false),
98 (None, _) | (_, None) => None,
99 (Some(l), Some(r)) => Some(l & r),
100 },
101 Operator::Or => match (lhs_val, rhs_val) {
102 (Some(true), _) | (_, Some(true)) => Some(true),
103 (None, _) | (_, None) => None,
104 (Some(l), Some(r)) => Some(l | r),
105 },
106 other => return Err(vortex_err!("Not a boolean operator: {other}")),
107 };
108
109 let scalar = result
110 .map(|b| Scalar::bool(b, nullable.into()))
111 .unwrap_or_else(|| Scalar::null(DType::Bool(nullable.into())));
112
113 Ok(Some(ConstantArray::new(scalar, length).into_array()))
114}
115
116#[cfg(test)]
117mod tests {
118 use rstest::rstest;
119
120 use crate::ArrayRef;
121 use crate::IntoArray;
122 use crate::LEGACY_SESSION;
123 use crate::VortexSessionExecute;
124 use crate::arrays::BoolArray;
125 use crate::builtins::ArrayBuiltins;
126 #[expect(deprecated)]
127 use crate::canonical::ToCanonical as _;
128 use crate::scalar_fn::fns::operators::Operator;
129
130 #[rstest]
131 #[case(
132 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
133 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
134 )]
135 #[case(
136 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
137 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
138 )]
139 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
140 let r = lhs.binary(rhs, Operator::Or).unwrap();
141 #[expect(deprecated)]
142 let r = r.to_bool().into_array();
143
144 let v0 = r
145 .execute_scalar(0, &mut LEGACY_SESSION.create_execution_ctx())
146 .unwrap()
147 .as_bool()
148 .value();
149 let v1 = r
150 .execute_scalar(1, &mut LEGACY_SESSION.create_execution_ctx())
151 .unwrap()
152 .as_bool()
153 .value();
154 let v2 = r
155 .execute_scalar(2, &mut LEGACY_SESSION.create_execution_ctx())
156 .unwrap()
157 .as_bool()
158 .value();
159 let v3 = r
160 .execute_scalar(3, &mut LEGACY_SESSION.create_execution_ctx())
161 .unwrap()
162 .as_bool()
163 .value();
164
165 assert!(v0.unwrap());
166 assert!(v1.unwrap());
167 assert!(v2.unwrap());
168 assert!(!v3.unwrap());
169 }
170
171 #[rstest]
172 #[case(
173 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
174 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
175 )]
176 #[case(
177 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
178 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
179 )]
180 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
181 #[expect(deprecated)]
182 let r = lhs
183 .binary(rhs, Operator::And)
184 .unwrap()
185 .to_bool()
186 .into_array();
187
188 let v0 = r
189 .execute_scalar(0, &mut LEGACY_SESSION.create_execution_ctx())
190 .unwrap()
191 .as_bool()
192 .value();
193 let v1 = r
194 .execute_scalar(1, &mut LEGACY_SESSION.create_execution_ctx())
195 .unwrap()
196 .as_bool()
197 .value();
198 let v2 = r
199 .execute_scalar(2, &mut LEGACY_SESSION.create_execution_ctx())
200 .unwrap()
201 .as_bool()
202 .value();
203 let v3 = r
204 .execute_scalar(3, &mut LEGACY_SESSION.create_execution_ctx())
205 .unwrap()
206 .as_bool()
207 .value();
208
209 assert!(v0.unwrap());
210 assert!(!v1.unwrap());
211 assert!(!v2.unwrap());
212 assert!(!v3.unwrap());
213 }
214}