vortex_array/scalar_fn/fns/
get_item.rs1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use prost::Message;
8use vortex_error::VortexResult;
9use vortex_error::vortex_err;
10use vortex_proto::expr as pb;
11use vortex_session::VortexSession;
12use vortex_session::registry::CachedId;
13
14use crate::ArrayRef;
15use crate::ExecutionCtx;
16use crate::arrays::ScalarFnArray;
17use crate::arrays::StructArray;
18use crate::arrays::struct_::StructArrayExt;
19use crate::builtins::ArrayBuiltins;
20use crate::builtins::ExprBuiltins;
21use crate::dtype::DType;
22use crate::dtype::FieldName;
23use crate::dtype::Nullability;
24use crate::expr::Expression;
25use crate::expr::display::ExprDisplay;
26use crate::expr::lit;
27use crate::scalar_fn::Arity;
28use crate::scalar_fn::ChildName;
29use crate::scalar_fn::EmptyOptions;
30use crate::scalar_fn::ExecutionArgs;
31use crate::scalar_fn::ReduceNode;
32use crate::scalar_fn::ScalarFnId;
33use crate::scalar_fn::ScalarFnVTable;
34use crate::scalar_fn::ScalarFnVTableExt;
35use crate::scalar_fn::fns::literal::Literal;
36use crate::scalar_fn::fns::mask::Mask;
37use crate::scalar_fn::fns::pack::Pack;
38
39#[derive(Clone)]
40pub struct GetItem;
41
42impl GetItem {
43 pub fn try_new(
49 input: ArrayRef,
50 field_name: impl Into<FieldName>,
51 ) -> VortexResult<ScalarFnArray> {
52 ScalarFnArray::try_new(GetItem.bind(field_name.into()), vec![input])
53 }
54}
55
56impl ScalarFnVTable for GetItem {
57 type Options = FieldName;
58
59 fn id(&self) -> ScalarFnId {
60 static ID: CachedId = CachedId::new("vortex.get_item");
61 *ID
62 }
63
64 fn serialize(&self, instance: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
65 Ok(Some(
66 pb::GetItemOpts {
67 path: instance.to_string(),
68 }
69 .encode_to_vec(),
70 ))
71 }
72
73 fn deserialize(
74 &self,
75 _metadata: &[u8],
76 _session: &VortexSession,
77 ) -> VortexResult<Self::Options> {
78 let opts = pb::GetItemOpts::decode(_metadata)?;
79 Ok(FieldName::from(opts.path))
80 }
81
82 fn arity(&self, _field_name: &FieldName) -> Arity {
83 Arity::Exact(1)
84 }
85
86 fn child_name(&self, _instance: &Self::Options, child_idx: usize) -> ChildName {
87 match child_idx {
88 0 => ChildName::from("input"),
89 _ => unreachable!("Invalid child index {} for GetItem expression", child_idx),
90 }
91 }
92
93 fn fmt_sql(
94 &self,
95 field_name: &FieldName,
96 expr: &dyn ExprDisplay,
97 f: &mut Formatter<'_>,
98 ) -> std::fmt::Result {
99 Display::fmt(expr.display_child(0), f)?;
100 write!(f, ".{}", field_name)
101 }
102
103 fn return_dtype(&self, field_name: &FieldName, arg_dtypes: &[DType]) -> VortexResult<DType> {
104 let struct_dtype = &arg_dtypes[0];
105 let field_dtype = struct_dtype
106 .as_struct_fields_opt()
107 .and_then(|st| st.field(field_name))
108 .ok_or_else(|| {
109 vortex_err!("Couldn't find the {} field in the input scope", field_name)
110 })?;
111
112 if matches!(
114 (struct_dtype.nullability(), field_dtype.nullability()),
115 (Nullability::Nullable, Nullability::NonNullable)
116 ) {
117 return Ok(field_dtype.with_nullability(Nullability::Nullable));
118 }
119
120 Ok(field_dtype)
121 }
122
123 fn execute(
124 &self,
125 field_name: &FieldName,
126 args: &dyn ExecutionArgs,
127 ctx: &mut ExecutionCtx,
128 ) -> VortexResult<ArrayRef> {
129 let input = args.get(0)?.execute::<StructArray>(ctx)?;
130 let field = input.unmasked_field_by_name(field_name).cloned()?;
131
132 match input.dtype().nullability() {
133 Nullability::NonNullable => Ok(field),
134 Nullability::Nullable => field.mask(input.validity()?.to_array(input.len())),
135 }
136 }
137
138 fn reduce<T: ReduceNode>(&self, field_name: &FieldName, node: &T) -> VortexResult<Option<T>> {
139 let child = node.child(0);
140 if let Some(child_fn) = child.scalar_fn()
141 && let Some(pack) = child_fn.as_opt::<Pack>()
142 && let Some(idx) = pack.names.find(field_name)
143 {
144 let mut field = child.child(idx);
145
146 if pack.nullability.is_nullable() {
148 field = node.new_node(
149 Mask.bind(EmptyOptions),
150 &[field, node.new_node(Literal.bind(true.into()), &[])?],
151 )?;
152 }
153
154 return Ok(Some(field));
155 }
156
157 Ok(None)
158 }
159
160 fn simplify_untyped(
161 &self,
162 field_name: &FieldName,
163 expr: &Expression,
164 ) -> VortexResult<Option<Expression>> {
165 let child = expr.child(0);
166
167 if let Some(pack) = child.as_opt::<Pack>() {
169 let idx = pack
170 .names
171 .iter()
172 .position(|name| name == field_name)
173 .ok_or_else(|| {
174 vortex_err!(
175 "Cannot find field {} in pack fields {:?}",
176 field_name,
177 pack.names
178 )
179 })?;
180
181 let mut field = child.child(idx).clone();
182
183 if pack.nullability.is_nullable() {
188 field = field.mask(lit(true))?;
190 }
191
192 return Ok(Some(field));
193 }
194
195 Ok(None)
196 }
197
198 fn is_strict(&self, _field_name: &FieldName) -> bool {
199 true
200 }
201
202 fn is_infallible(&self, _field_name: &FieldName) -> bool {
203 true
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use vortex_buffer::buffer;
211 use vortex_error::VortexResult;
212
213 use crate::IntoArray;
214 use crate::dtype::DType;
215 use crate::dtype::FieldNames;
216 use crate::dtype::Nullability;
217 use crate::dtype::Nullability::NonNullable;
218 use crate::dtype::PType;
219 use crate::dtype::StructFields;
220 use crate::expr::checked_add;
221 use crate::expr::get_item;
222 use crate::expr::lit;
223 use crate::expr::pack;
224 use crate::expr::root;
225 use crate::scalar_fn::fns::get_item::StructArray;
226 use crate::validity::Validity;
227
228 fn test_array() -> StructArray {
229 StructArray::from_fields(&[
230 ("a", buffer![0i32, 1, 2].into_array()),
231 ("b", buffer![4i64, 5, 6].into_array()),
232 ])
233 .unwrap()
234 }
235
236 #[test]
237 fn get_item_by_name() {
238 let st = test_array();
239 let get_item = get_item("a", root());
240 assert!(
241 get_item
242 .as_scalar()
243 .is_some_and(|f| f.signature().is_strict())
244 );
245 let item = st.into_array().apply(&get_item).unwrap();
246 assert_eq!(item.dtype(), &DType::from(PType::I32))
247 }
248
249 #[test]
250 fn get_item_by_name_none() {
251 let st = test_array();
252 let get_item = get_item("c", root());
253 assert!(st.into_array().apply(&get_item).is_err());
254 }
255
256 #[test]
257 fn get_nullable_field() {
258 let st = StructArray::try_new(
259 FieldNames::from(["a"]),
260 vec![buffer![1i32].into_array()],
261 1,
262 Validity::AllInvalid,
263 )
264 .unwrap()
265 .into_array();
266
267 let get_item_expr = get_item("a", root());
268 let item = st.apply(&get_item_expr).unwrap();
269 assert_eq!(
271 item.dtype(),
272 &DType::Primitive(PType::I32, Nullability::Nullable)
273 );
274 }
275
276 #[test]
277 fn get_non_nullable_field_from_all_valid_nullable_struct() -> VortexResult<()> {
278 let st = StructArray::try_new(
279 FieldNames::from(["a"]),
280 vec![buffer![1i32].into_array()],
281 1,
282 Validity::AllValid,
283 )?
284 .into_array();
285
286 let item = st.apply(&get_item("a", root()))?;
287 assert_eq!(
288 item.dtype(),
289 &DType::Primitive(PType::I32, Nullability::Nullable)
290 );
291 Ok(())
292 }
293
294 #[test]
295 fn test_pack_get_item_rule() {
296 let pack_expr = pack([("a", lit(1)), ("b", lit(2))], NonNullable);
298 let get_item_expr = get_item("b", pack_expr);
299
300 let result = get_item_expr
301 .optimize_recursive(&DType::Struct(StructFields::empty(), NonNullable))
302 .unwrap();
303
304 assert_eq!(result, lit(2));
305 }
306
307 #[test]
308 fn test_multi_level_pack_get_item_simplify() {
309 let inner_pack = pack([("a", lit(1)), ("b", lit(2))], NonNullable);
310 let get_a = get_item("a", inner_pack);
311
312 let outer_pack = pack([("x", get_a), ("y", lit(3)), ("z", lit(4))], NonNullable);
313 let get_z = get_item("z", outer_pack);
314
315 let dtype = DType::Primitive(PType::I32, NonNullable);
316
317 let result = get_z.optimize_recursive(&dtype).unwrap();
318 assert_eq!(result, lit(4));
319 }
320
321 #[test]
322 fn test_deeply_nested_pack_get_item() {
323 let innermost = pack([("a", lit(42))], NonNullable);
324 let get_a = get_item("a", innermost);
325
326 let level2 = pack([("b", get_a)], NonNullable);
327 let get_b = get_item("b", level2);
328
329 let level3 = pack([("c", get_b)], NonNullable);
330 let get_c = get_item("c", level3);
331
332 let outermost = pack([("final", get_c)], NonNullable);
333 let get_final = get_item("final", outermost);
334
335 let dtype = DType::Primitive(PType::I32, NonNullable);
336
337 let result = get_final.optimize_recursive(&dtype).unwrap();
338 assert_eq!(result, lit(42));
339 }
340
341 #[test]
342 fn test_partial_pack_get_item_simplify() {
343 let inner_pack = pack([("x", lit(1)), ("y", lit(2))], NonNullable);
344 let get_x = get_item("x", inner_pack);
345 let add_expr = checked_add(get_x, lit(10));
346
347 let outer_pack = pack([("result", add_expr)], NonNullable);
348 let get_result = get_item("result", outer_pack);
349
350 let dtype = DType::Primitive(PType::I32, NonNullable);
351
352 let result = get_result.optimize_recursive(&dtype).unwrap();
353 let expected = checked_add(lit(1), lit(10));
354 assert_eq!(&result, &expected);
355 }
356
357 #[test]
358 fn get_item_filter_list_field() {
359 use vortex_mask::Mask;
360
361 use crate::arrays::BoolArray;
362 use crate::arrays::FilterArray;
363 use crate::arrays::ListArray;
364
365 let list = ListArray::try_new(
366 buffer![0f32, 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.].into_array(),
367 buffer![2u64, 4, 6, 8, 10, 12].into_array(),
368 Validity::Array(BoolArray::from_iter([true, true, false, true, true]).into_array()),
369 )
370 .unwrap();
371
372 let filtered = FilterArray::try_new(
373 list.into_array(),
374 Mask::from_iter([true, true, false, false, false]),
375 )
376 .unwrap();
377
378 let st = StructArray::try_new(
379 FieldNames::from(["data"]),
380 vec![filtered.into_array()],
381 2,
382 Validity::AllValid,
383 )
384 .unwrap();
385
386 st.into_array().apply(&get_item("data", root())).unwrap();
387 }
388}