1use std::fmt::Display;
5use std::fmt::Formatter;
6use std::hash::Hash;
7use std::sync::Arc;
8
9use itertools::Itertools as _;
10use vortex_error::VortexExpect;
11use vortex_error::VortexResult;
12use vortex_error::vortex_bail;
13use vortex_session::VortexSession;
14use vortex_session::registry::CachedId;
15use vortex_utils::aliases::hash_set::HashSet;
16
17use crate::ArrayRef;
18use crate::ExecutionCtx;
19use crate::IntoArray as _;
20use crate::arrays::StructArray;
21use crate::arrays::struct_::StructArrayExt;
22use crate::dtype::DType;
23use crate::dtype::FieldNames;
24use crate::dtype::Nullability;
25use crate::dtype::StructFields;
26use crate::expr::Expression;
27use crate::expr::lit;
28use crate::scalar_fn::Arity;
29use crate::scalar_fn::ChildName;
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::get_item::GetItem;
36use crate::scalar_fn::fns::pack::Pack;
37use crate::scalar_fn::fns::pack::PackOptions;
38use crate::validity::Validity;
39
40#[derive(Clone)]
47pub struct Merge;
48
49impl ScalarFnVTable for Merge {
50 type Options = DuplicateHandling;
51
52 fn id(&self) -> ScalarFnId {
53 static ID: CachedId = CachedId::new("vortex.merge");
54 *ID
55 }
56
57 fn serialize(&self, instance: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
58 Ok(Some(match instance {
59 DuplicateHandling::RightMost => vec![0x00],
60 DuplicateHandling::Error => vec![0x01],
61 }))
62 }
63
64 fn deserialize(
65 &self,
66 _metadata: &[u8],
67 _session: &VortexSession,
68 ) -> VortexResult<Self::Options> {
69 let instance = match _metadata {
70 [0x00] => DuplicateHandling::RightMost,
71 [0x01] => DuplicateHandling::Error,
72 _ => {
73 vortex_bail!("invalid metadata for Merge expression");
74 }
75 };
76 Ok(instance)
77 }
78
79 fn arity(&self, _options: &Self::Options) -> Arity {
80 Arity::Variadic { min: 0, max: None }
81 }
82
83 fn child_name(&self, _instance: &Self::Options, child_idx: usize) -> ChildName {
84 ChildName::from(Arc::from(format!("{}", child_idx)))
85 }
86
87 fn return_dtype(&self, options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
88 let mut field_names = Vec::new();
89 let mut arrays = Vec::new();
90 let mut merge_nullability = Nullability::NonNullable;
91 let mut duplicate_names = HashSet::<_>::new();
92
93 for dtype in arg_dtypes {
94 let Some(fields) = dtype.as_struct_fields_opt() else {
95 vortex_bail!("merge expects struct input");
96 };
97 if dtype.is_nullable() {
98 vortex_bail!("merge expects non-nullable input");
99 }
100
101 merge_nullability |= dtype.nullability();
102
103 for (field_name, field_dtype) in fields.names().iter().zip_eq(fields.fields()) {
104 if let Some(idx) = field_names.iter().position(|name| name == field_name) {
105 duplicate_names.insert(field_name.clone());
106 arrays[idx] = field_dtype;
107 } else {
108 field_names.push(field_name.clone());
109 arrays.push(field_dtype);
110 }
111 }
112 }
113
114 if options == &DuplicateHandling::Error && !duplicate_names.is_empty() {
115 vortex_bail!(
116 "merge: duplicate fields in children: {}",
117 duplicate_names.into_iter().format(", ")
118 )
119 }
120
121 Ok(DType::Struct(
122 StructFields::new(FieldNames::from(field_names), arrays),
123 merge_nullability,
124 ))
125 }
126
127 fn execute(
128 &self,
129 options: &Self::Options,
130 args: &dyn ExecutionArgs,
131 ctx: &mut ExecutionCtx,
132 ) -> VortexResult<ArrayRef> {
133 let mut field_names = Vec::new();
135 let mut arrays = Vec::new();
136 let mut duplicate_names = HashSet::<_>::new();
137
138 for i in 0..args.num_inputs() {
139 let array = args.get(i)?.execute::<StructArray>(ctx)?;
140 if array.dtype().is_nullable() {
141 vortex_bail!("merge expects non-nullable input");
142 }
143
144 for (field_name, field_array) in array
145 .names()
146 .iter()
147 .zip_eq(array.iter_unmasked_fields().cloned())
148 {
149 if let Some(idx) = field_names.iter().position(|name| name == field_name) {
151 duplicate_names.insert(field_name.clone());
152 arrays[idx] = field_array;
153 } else {
154 field_names.push(field_name.clone());
155 arrays.push(field_array);
156 }
157 }
158 }
159
160 if options == &DuplicateHandling::Error && !duplicate_names.is_empty() {
161 vortex_bail!(
162 "merge: duplicate fields in children: {}",
163 duplicate_names.into_iter().format(", ")
164 )
165 }
166
167 let validity = Validity::NonNullable;
169 let len = args.row_count();
170 Ok(
171 StructArray::try_new(FieldNames::from(field_names), arrays, len, validity)?
172 .into_array(),
173 )
174 }
175
176 fn reduce<T: ReduceNode>(&self, options: &Self::Options, node: &T) -> VortexResult<Option<T>> {
177 let mut names = Vec::with_capacity(node.child_count() * 2);
178 let mut children = Vec::with_capacity(node.child_count() * 2);
179 let mut duplicate_names = HashSet::<_>::new();
180
181 for child in (0..node.child_count()).map(|i| node.child(i)) {
182 let child_dtype = child.node_dtype()?;
183 if !child_dtype.is_struct() {
184 vortex_bail!(
185 "Merge child must return a non-nullable struct dtype, got {}",
186 child_dtype
187 )
188 }
189
190 let child_dtype = child_dtype
191 .as_struct_fields_opt()
192 .vortex_expect("expected struct");
193
194 for name in child_dtype.names().iter() {
195 if let Some(idx) = names.iter().position(|n| n == name) {
196 duplicate_names.insert(name.clone());
197 children[idx] = child.clone();
198 } else {
199 names.push(name.clone());
200 children.push(child.clone());
201 }
202 }
203
204 if options == &DuplicateHandling::Error && !duplicate_names.is_empty() {
205 vortex_bail!(
206 "merge: duplicate fields in children: {}",
207 duplicate_names.into_iter().format(", ")
208 )
209 }
210 }
211
212 let pack_children: Vec<_> = names
213 .iter()
214 .zip(children)
215 .map(|(name, child)| node.new_node(GetItem.bind(name.clone()), &[child]))
216 .try_collect()?;
217
218 let pack_expr = node.new_node(
219 Pack.bind(PackOptions {
220 names: FieldNames::from(names),
221 nullability: node.node_dtype()?.nullability(),
222 }),
223 &pack_children,
224 )?;
225
226 Ok(Some(pack_expr))
227 }
228
229 fn validity(
230 &self,
231 _options: &Self::Options,
232 _expression: &Expression,
233 ) -> VortexResult<Option<Expression>> {
234 Ok(Some(lit(true)))
235 }
236
237 fn is_strict(&self, _options: &Self::Options) -> bool {
238 true
239 }
240
241 fn is_infallible(&self, instance: &Self::Options) -> bool {
242 !matches!(instance, DuplicateHandling::Error)
243 }
244}
245
246#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
248pub enum DuplicateHandling {
249 RightMost,
251 #[default]
253 Error,
254}
255
256impl Display for DuplicateHandling {
257 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
258 match self {
259 DuplicateHandling::RightMost => write!(f, "RightMost"),
260 DuplicateHandling::Error => write!(f, "Error"),
261 }
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use vortex_buffer::buffer;
268 use vortex_error::VortexResult;
269 use vortex_error::vortex_bail;
270
271 use crate::ArrayRef;
272 use crate::IntoArray;
273 use crate::VortexSessionExecute;
274 use crate::array_session;
275 use crate::arrays::PrimitiveArray;
276 use crate::arrays::struct_::StructArrayExt;
277 use crate::assert_arrays_eq;
278 use crate::dtype::DType;
279 use crate::dtype::Nullability::NonNullable;
280 use crate::dtype::PType::I32;
281 use crate::dtype::PType::I64;
282 use crate::dtype::PType::U32;
283 use crate::dtype::PType::U64;
284 use crate::expr::Expression;
285 use crate::expr::get_item;
286 use crate::expr::merge;
287 use crate::expr::merge_opts;
288 use crate::expr::root;
289 use crate::scalar_fn::fns::merge::DuplicateHandling;
290 use crate::scalar_fn::fns::merge::StructArray;
291 use crate::scalar_fn::fns::pack::Pack;
292
293 fn primitive_field(array: &ArrayRef, field_path: &[&str]) -> VortexResult<PrimitiveArray> {
294 let mut ctx = array_session().create_execution_ctx();
295 let mut field_path = field_path.iter();
296
297 let Some(field) = field_path.next() else {
298 vortex_bail!("empty field path");
299 };
300
301 let mut array = array
302 .clone()
303 .execute::<StructArray>(&mut ctx)?
304 .unmasked_field_by_name(field)?
305 .clone();
306 for field in field_path {
307 let next = array
308 .clone()
309 .execute::<StructArray>(&mut ctx)?
310 .unmasked_field_by_name(field)?
311 .clone();
312 array = next;
313 }
314 let result = array.execute::<PrimitiveArray>(&mut ctx)?;
315 Ok(result)
316 }
317
318 #[test]
319 pub fn test_merge_right_most() {
320 let mut ctx = array_session().create_execution_ctx();
321 let expr = merge_opts(
322 vec![
323 get_item("0", root()),
324 get_item("1", root()),
325 get_item("2", root()),
326 ],
327 DuplicateHandling::RightMost,
328 );
329
330 let test_array = StructArray::from_fields(&[
331 (
332 "0",
333 StructArray::from_fields(&[
334 ("a", buffer![0, 0, 0].into_array()),
335 ("b", buffer![1, 1, 1].into_array()),
336 ])
337 .unwrap()
338 .into_array(),
339 ),
340 (
341 "1",
342 StructArray::from_fields(&[
343 ("b", buffer![2, 2, 2].into_array()),
344 ("c", buffer![3, 3, 3].into_array()),
345 ])
346 .unwrap()
347 .into_array(),
348 ),
349 (
350 "2",
351 StructArray::from_fields(&[
352 ("d", buffer![4, 4, 4].into_array()),
353 ("e", buffer![5, 5, 5].into_array()),
354 ])
355 .unwrap()
356 .into_array(),
357 ),
358 ])
359 .unwrap()
360 .into_array();
361 let actual_array = test_array.apply(&expr).unwrap();
362
363 assert_eq!(
364 actual_array.dtype().as_struct_fields().names(),
365 ["a", "b", "c", "d", "e"]
366 );
367
368 assert_arrays_eq!(
369 primitive_field(&actual_array, &["a"]).unwrap(),
370 PrimitiveArray::from_iter([0i32, 0, 0]),
371 &mut ctx
372 );
373 assert_arrays_eq!(
374 primitive_field(&actual_array, &["b"]).unwrap(),
375 PrimitiveArray::from_iter([2i32, 2, 2]),
376 &mut ctx
377 );
378 assert_arrays_eq!(
379 primitive_field(&actual_array, &["c"]).unwrap(),
380 PrimitiveArray::from_iter([3i32, 3, 3]),
381 &mut ctx
382 );
383 assert_arrays_eq!(
384 primitive_field(&actual_array, &["d"]).unwrap(),
385 PrimitiveArray::from_iter([4i32, 4, 4]),
386 &mut ctx
387 );
388 assert_arrays_eq!(
389 primitive_field(&actual_array, &["e"]).unwrap(),
390 PrimitiveArray::from_iter([5i32, 5, 5]),
391 &mut ctx
392 );
393 }
394
395 #[test]
396 #[should_panic(expected = "merge: duplicate fields in children")]
397 pub fn test_merge_error_on_dupe_return_dtype() {
398 let expr = merge_opts(
399 vec![get_item("0", root()), get_item("1", root())],
400 DuplicateHandling::Error,
401 );
402 let test_array = StructArray::try_from_iter([
403 (
404 "0",
405 StructArray::try_from_iter([("a", buffer![1]), ("b", buffer![1])]).unwrap(),
406 ),
407 (
408 "1",
409 StructArray::try_from_iter([("c", buffer![1]), ("b", buffer![1])]).unwrap(),
410 ),
411 ])
412 .unwrap()
413 .into_array();
414
415 expr.return_dtype(test_array.dtype()).unwrap();
416 }
417
418 #[test]
419 #[should_panic(expected = "merge: duplicate fields in children")]
420 pub fn test_merge_error_on_dupe_evaluate() {
421 let expr = merge_opts(
422 vec![get_item("0", root()), get_item("1", root())],
423 DuplicateHandling::Error,
424 );
425 let test_array = StructArray::try_from_iter([
426 (
427 "0",
428 StructArray::try_from_iter([("a", buffer![1]), ("b", buffer![1])]).unwrap(),
429 ),
430 (
431 "1",
432 StructArray::try_from_iter([("c", buffer![1]), ("b", buffer![1])]).unwrap(),
433 ),
434 ])
435 .unwrap()
436 .into_array();
437
438 test_array.apply(&expr).unwrap();
439 }
440
441 #[test]
442 pub fn test_empty_merge() {
443 let expr = merge(Vec::<Expression>::new());
444
445 let test_array = StructArray::from_fields(&[("a", buffer![0, 1, 2].into_array())])
446 .unwrap()
447 .into_array();
448 let actual_array = test_array.clone().apply(&expr).unwrap();
449 assert_eq!(actual_array.len(), test_array.len());
450 assert_eq!(actual_array.nchildren(), 0);
451 }
452
453 #[test]
454 pub fn test_nested_merge() {
455 let expr = merge_opts(
458 vec![get_item("0", root()), get_item("1", root())],
459 DuplicateHandling::RightMost,
460 );
461
462 let test_array = StructArray::from_fields(&[
463 (
464 "0",
465 StructArray::from_fields(&[(
466 "a",
467 StructArray::from_fields(&[
468 ("x", buffer![0, 0, 0].into_array()),
469 ("y", buffer![1, 1, 1].into_array()),
470 ])
471 .unwrap()
472 .into_array(),
473 )])
474 .unwrap()
475 .into_array(),
476 ),
477 (
478 "1",
479 StructArray::from_fields(&[(
480 "a",
481 StructArray::from_fields(&[("x", buffer![0, 0, 0].into_array())])
482 .unwrap()
483 .into_array(),
484 )])
485 .unwrap()
486 .into_array(),
487 ),
488 ])
489 .unwrap()
490 .into_array();
491 let mut ctx = array_session().create_execution_ctx();
492 let actual_array = test_array
493 .apply(&expr)
494 .unwrap()
495 .execute::<StructArray>(&mut ctx)
496 .unwrap();
497
498 let inner_struct = actual_array
499 .unmasked_field_by_name("a")
500 .unwrap()
501 .clone()
502 .execute::<StructArray>(&mut ctx)
503 .unwrap();
504 assert_eq!(
505 inner_struct
506 .names()
507 .iter()
508 .map(|name| name.as_ref())
509 .collect::<Vec<_>>(),
510 vec!["x"]
511 );
512 }
513
514 #[test]
515 pub fn test_merge_order() {
516 let mut ctx = array_session().create_execution_ctx();
517 let expr = merge(vec![get_item("0", root()), get_item("1", root())]);
518
519 let test_array = StructArray::from_fields(&[
520 (
521 "0",
522 StructArray::from_fields(&[
523 ("a", buffer![0, 0, 0].into_array()),
524 ("c", buffer![1, 1, 1].into_array()),
525 ])
526 .unwrap()
527 .into_array(),
528 ),
529 (
530 "1",
531 StructArray::from_fields(&[
532 ("b", buffer![2, 2, 2].into_array()),
533 ("d", buffer![3, 3, 3].into_array()),
534 ])
535 .unwrap()
536 .into_array(),
537 ),
538 ])
539 .unwrap()
540 .into_array();
541 let actual_array = test_array
542 .apply(&expr)
543 .unwrap()
544 .execute::<StructArray>(&mut ctx)
545 .unwrap();
546
547 assert_eq!(actual_array.names(), ["a", "c", "b", "d"]);
548 }
549
550 #[test]
551 pub fn test_display() {
552 let expr = merge([get_item("struct1", root()), get_item("struct2", root())]);
553 assert_eq!(
554 expr.to_string(),
555 "vortex.merge($.struct1, $.struct2, opts=Error)"
556 );
557
558 let expr2 = merge(vec![get_item("a", root())]);
559 assert_eq!(expr2.to_string(), "vortex.merge($.a, opts=Error)");
560 }
561
562 #[test]
563 fn test_remove_merge() {
564 let dtype = DType::struct_(
565 [
566 ("0", DType::struct_([("a", I32), ("b", I64)], NonNullable)),
567 ("1", DType::struct_([("b", U32), ("c", U64)], NonNullable)),
568 ],
569 NonNullable,
570 );
571
572 let e = merge_opts(
573 [get_item("0", root()), get_item("1", root())],
574 DuplicateHandling::RightMost,
575 );
576
577 let result = e.optimize(&dtype).unwrap();
578
579 assert!(result.is::<Pack>());
580 assert_eq!(
581 result.return_dtype(&dtype).unwrap(),
582 DType::struct_([("a", I32), ("b", U32), ("c", U64)], NonNullable)
583 );
584 }
585}