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