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