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