vortex_array/arrays/scalar_fn/vtable/
operations.rs1use vortex_error::VortexResult;
5
6use crate::ExecutionCtx;
7use crate::IntoArray;
8use crate::ProbeState;
9use crate::array::ArrayView;
10use crate::array::OperationsVTable;
11use crate::arrays::ConstantArray;
12use crate::arrays::scalar_fn::ScalarFnArrayExt;
13use crate::arrays::scalar_fn::vtable::ScalarFn;
14use crate::columnar::Columnar;
15use crate::scalar::Scalar;
16use crate::scalar_fn::VecExecutionArgs;
17
18impl OperationsVTable<ScalarFn> for ScalarFn {
19 type ProbeState = ();
20
21 fn probe_scalar(
23 state: &mut ProbeState<'_, ScalarFn>,
24 index: usize,
25 ctx: &mut ExecutionCtx,
26 ) -> VortexResult<Scalar> {
27 Self::scalar_at(state.array(), index, ctx)
28 }
29
30 fn scalar_at(
31 array: ArrayView<'_, ScalarFn>,
32 index: usize,
33 ctx: &mut ExecutionCtx,
34 ) -> VortexResult<Scalar> {
35 let inputs: Vec<_> = array
36 .children()
37 .iter()
38 .map(|child| Ok(ConstantArray::new(child.execute_scalar(index, ctx)?, 1).into_array()))
39 .collect::<VortexResult<_>>()?;
40
41 let args = VecExecutionArgs::new(inputs, 1);
42 let result = array.scalar_fn().execute(&args, ctx)?;
43
44 let scalar = match result.execute::<Columnar>(ctx)? {
45 Columnar::Canonical(arr) => {
46 tracing::info!(
47 "Scalar function {} returned non-constant array from execution over all scalar inputs",
48 array.scalar_fn(),
49 );
50 arr.into_array().execute_scalar(0, ctx)?
51 }
52 Columnar::Constant(constant) => constant.scalar().clone(),
53 };
54
55 debug_assert_eq!(
56 scalar.dtype(),
57 array.dtype(),
58 "Scalar function {} returned dtype {:?} but expected {:?}",
59 array.scalar_fn(),
60 scalar.dtype(),
61 array.dtype()
62 );
63
64 Ok(scalar)
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use vortex_buffer::buffer;
71 use vortex_error::VortexResult;
72
73 use crate::ArrayRef;
74 use crate::ArraySlots;
75 use crate::IntoArray;
76 use crate::VortexSessionExecute;
77 use crate::array::Array;
78 use crate::array::ArrayParts;
79 use crate::array_session;
80 use crate::arrays::BoolArray;
81 use crate::arrays::ConstantArray;
82 use crate::arrays::PrimitiveArray;
83 use crate::arrays::ScalarFnArray;
84 use crate::arrays::scalar_fn::ScalarFnArrayExt;
85 use crate::arrays::scalar_fn::array::ScalarFnData;
86 use crate::arrays::scalar_fn::vtable::ScalarFn;
87 use crate::assert_arrays_eq;
88 use crate::dtype::DType;
89 use crate::dtype::Nullability;
90 use crate::dtype::PType;
91 use crate::scalar::Scalar;
92 use crate::scalar_fn::TypedScalarFnInstance;
93 use crate::scalar_fn::fns::binary::Binary;
94 use crate::scalar_fn::fns::list_contains::PreparedSet;
95 use crate::scalar_fn::fns::list_contains::PreparedSetData;
96 use crate::scalar_fn::fns::literal::Literal;
97 use crate::scalar_fn::fns::operators::Operator;
98 use crate::validity::Validity;
99
100 #[test]
101 fn test_scalar_fn_add() -> VortexResult<()> {
102 let mut ctx = array_session().create_execution_ctx();
103 let lhs = buffer![1i32, 2, 3].into_array();
104 let rhs = buffer![10i32, 20, 30].into_array();
105
106 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
107 let scalar_fn_array = ScalarFnArray::try_new(scalar_fn, vec![lhs, rhs])?;
108
109 assert_eq!(scalar_fn_array.len(), 3);
110
111 let expected = buffer![11i32, 22, 33].into_array();
112 assert_arrays_eq!(scalar_fn_array, expected, &mut ctx);
113
114 Ok(())
115 }
116
117 #[test]
118 fn test_scalar_fn_inferred_len_rejects_mismatched_children() {
119 let lhs = buffer![1i32, 2, 3].into_array();
120 let rhs = buffer![10i32, 20].into_array();
121
122 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
123 let err = ScalarFnArray::try_new(scalar_fn, vec![lhs, rhs])
124 .expect_err("ScalarFnArray::try_new must reject mismatched child lengths");
125
126 assert!(
127 err.to_string()
128 .contains("ScalarFnArray must have children equal to the array length")
129 );
130 }
131
132 #[test]
133 fn rejects_wrong_arity() {
134 let child = buffer![1i32, 2, 3].into_array();
135 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
136
137 let Err(err) = ScalarFnArray::try_new(scalar_fn.clone(), vec![child.clone()]) else {
138 panic!("Binary must reject one child");
139 };
140 assert!(
141 err.to_string()
142 .contains("ScalarFnArray requires 2 children, got 1")
143 );
144
145 let Err(err) = ScalarFnArray::try_new(scalar_fn, vec![child.clone(), child.clone(), child])
146 else {
147 panic!("Binary must reject three children");
148 };
149 assert!(
150 err.to_string()
151 .contains("ScalarFnArray requires 2 children, got 3")
152 );
153 }
154
155 #[test]
156 fn rejects_missing_child_slot() {
157 let lhs = buffer![1i32, 2, 3].into_array();
158 let dtype = lhs.dtype().clone();
159 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
160 let vtable = ScalarFn { id: scalar_fn.id() };
161 let data = ScalarFnData { scalar_fn };
162 let slots = [Some(lhs), None].into_iter().collect::<ArraySlots>();
163
164 let Err(err) =
165 Array::<ScalarFn>::try_from_parts(ArrayParts::new(vtable, dtype, 3, data, slots))
166 else {
167 panic!("ScalarFnArray must reject missing child slots");
168 };
169
170 assert!(
171 err.to_string()
172 .contains("ScalarFnArray requires every child slot to be present")
173 );
174 }
175
176 #[test]
177 fn test_scalar_fn_without_children_requires_explicit_len() -> VortexResult<()> {
178 let list = Scalar::list(
179 DType::Primitive(PType::I32, Nullability::NonNullable),
180 vec![Scalar::from(1i32)],
181 Nullability::NonNullable,
182 );
183 let scalar_fn =
184 TypedScalarFnInstance::new(PreparedSet, PreparedSetData::try_new(list.clone())?)
185 .erased();
186
187 let Err(err) = ScalarFnArray::try_new(scalar_fn.clone(), vec![]) else {
188 panic!("ScalarFnArray::try_new should reject zero children");
189 };
190 assert!(
191 err.to_string()
192 .contains("ScalarFnArray length cannot be inferred without children")
193 );
194
195 let scalar_fn_array = ScalarFnArray::try_new_with_len(scalar_fn, vec![], 3)?;
196 assert_eq!(scalar_fn_array.len(), 3);
197 assert_eq!(scalar_fn_array.child_count(), 0);
198 assert_arrays_eq!(
199 scalar_fn_array,
200 ConstantArray::new(list, 3),
201 &mut array_session().create_execution_ctx()
202 );
203
204 Ok(())
205 }
206
207 #[test]
208 fn rejects_literal() {
209 let scalar_fn = TypedScalarFnInstance::new(Literal, Scalar::from(1i32)).erased();
210
211 let Err(err) = ScalarFnArray::try_new_with_len(scalar_fn.clone(), vec![], 3) else {
212 panic!("ScalarFnArray::try_new_with_len must reject a literal");
213 };
214 assert!(
215 err.to_string()
216 .contains("ScalarFnArray cannot wrap a literal")
217 );
218
219 let vtable = ScalarFn { id: scalar_fn.id() };
220 let data = ScalarFnData { scalar_fn };
221 let parts = ArrayParts::new(vtable, PType::I32.into(), 3, data, ArraySlots::new());
222
223 let Err(err) = Array::<ScalarFn>::try_from_parts(parts) else {
224 panic!("ScalarFnArray must reject a literal from parts");
225 };
226 assert!(
227 err.to_string()
228 .contains("ScalarFnArray cannot wrap a literal")
229 );
230 }
231
232 #[test]
233 fn test_scalar_fn_mul() -> VortexResult<()> {
234 let mut ctx = array_session().create_execution_ctx();
235 let lhs = buffer![2i32, 3, 4].into_array();
236 let rhs = buffer![5i32, 6, 7].into_array();
237
238 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Mul).erased();
239 let scalar_fn_array = ScalarFnArray::try_new(scalar_fn, vec![lhs, rhs])?;
240
241 let expected = buffer![10i32, 18, 28].into_array();
242 assert_arrays_eq!(scalar_fn_array, expected, &mut ctx);
243
244 Ok(())
245 }
246
247 #[test]
248 fn test_scalar_fn_with_nullable() -> VortexResult<()> {
249 let mut ctx = array_session().create_execution_ctx();
250 let lhs = PrimitiveArray::new(buffer![1i32, 2, 3], Validity::AllValid).into_array();
251 let rhs = PrimitiveArray::new(
252 buffer![10i32, 20, 30],
253 Validity::from_iter([true, false, true]),
254 )
255 .into_array();
256
257 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
258 let scalar_fn_array = ScalarFnArray::try_new(scalar_fn, vec![lhs, rhs])?;
259
260 let expected = PrimitiveArray::new(
261 buffer![11i32, 0, 33],
262 Validity::from_iter([true, false, true]),
263 )
264 .into_array();
265 assert_arrays_eq!(scalar_fn_array, expected, &mut ctx);
266
267 Ok(())
268 }
269
270 #[test]
271 fn test_scalar_fn_comparison() -> VortexResult<()> {
272 let mut ctx = array_session().create_execution_ctx();
273 let lhs = buffer![1i32, 5, 3].into_array();
274 let rhs = buffer![2i32, 5, 1].into_array();
275
276 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Eq).erased();
277 let scalar_fn_array = ScalarFnArray::try_new(scalar_fn, vec![lhs, rhs])?;
278
279 let expected = BoolArray::from_iter([false, true, false]).into_array();
280 assert_arrays_eq!(scalar_fn_array, expected, &mut ctx);
281
282 Ok(())
283 }
284
285 fn scalar_nested_add() -> VortexResult<ArrayRef> {
287 let lhs_n = PrimitiveArray::from_option_iter((0..10i64).map(|i| (i % 4 != 0).then_some(i)));
288 let rhs_r = PrimitiveArray::from_iter(0..10i64);
289
290 let scalar_fn_n = TypedScalarFnInstance::new(Binary, Operator::Add).erased();
291 let args_n = vec![lhs_n.into_array(), rhs_r.into_array()];
292
293 let lhs = ScalarFnArray::try_new(scalar_fn_n, args_n)?.into_array();
294 let rhs = PrimitiveArray::from_iter(0..10i64);
295
296 let scalar_fn = TypedScalarFnInstance::new(Binary, Operator::Mul).erased();
297 let args = vec![lhs, rhs.into_array()];
298
299 Ok(ScalarFnArray::try_new(scalar_fn, args)?.into_array())
300 }
301
302 #[test]
303 fn scalar_fn_probe() -> VortexResult<()> {
304 let ctx = &mut array_session().create_execution_ctx();
305 let array = scalar_nested_add()?;
306 let mut probe = array.probe();
307
308 assert!(!probe.execute_is_valid(0, ctx)?);
309 assert!(probe.execute_scalar(0, ctx)?.is_null());
310 assert!(probe.execute_is_valid(9, ctx)?);
311 assert_eq!(probe.execute_scalar(9, ctx)?, Scalar::from(Some(162i64)));
312
313 Ok(())
314 }
315
316 #[test]
317 fn scalar_fn_repeated_probe() -> VortexResult<()> {
318 let ctx = &mut array_session().create_execution_ctx();
319 let array = scalar_nested_add()?;
320 let mut probe = array.repeated_probe();
321
322 assert!(!probe.execute_is_valid(0, ctx)?);
323 assert!(probe.execute_scalar(0, ctx)?.is_null());
324 assert!(probe.execute_is_valid(9, ctx)?);
325 assert_eq!(probe.execute_scalar(9, ctx)?, Scalar::from(Some(162i64)));
326
327 Ok(())
328 }
329
330 #[test]
331 fn scalar_fn_all_valid() -> VortexResult<()> {
332 let ctx = &mut array_session().create_execution_ctx();
333 let array = scalar_nested_add()?;
334 assert!(!array.all_valid(ctx)?);
335 assert!(!array.all_invalid(ctx)?);
336 assert_eq!(array.valid_count(ctx)?, 7);
337 assert_eq!(array.invalid_count(ctx)?, 3);
338 Ok(())
339 }
340}