1use crate::error::{_plan_err, Result};
19use arrow::{
20 array::{
21 Array, ArrayRef, AsArray, DictionaryArray, FixedSizeListArray, GenericListArray,
22 GenericListViewArray, RecordBatch, StructArray, UnionArray, downcast_integer,
23 make_array, new_null_array,
24 },
25 buffer::NullBuffer,
26 compute::{CastOptions, can_cast_types, cast_with_options},
27 datatypes::{
28 DataType, DataType::Struct, Field, FieldRef, SchemaRef, UnionFields, UnionMode,
29 },
30};
31use std::{collections::HashSet, sync::Arc};
32
33fn cast_struct_column(
61 source_col: &ArrayRef,
62 target_fields: &[Arc<Field>],
63 cast_options: &CastOptions,
64) -> Result<ArrayRef> {
65 if source_col.data_type() == &DataType::Null {
66 return Ok(new_null_array(
67 &Struct(target_fields.to_vec().into()),
68 source_col.len(),
69 ));
70 }
71
72 if let Some(source_struct) = source_col.as_any().downcast_ref::<StructArray>() {
73 let source_fields = source_struct.fields();
74 validate_struct_compatibility(source_fields, target_fields)?;
75
76 if !source_col.is_empty() && source_col.null_count() == source_col.len() {
77 return Ok(new_null_array(
78 &Struct(target_fields.to_vec().into()),
79 source_col.len(),
80 ));
81 }
82
83 let mut fields: Vec<Arc<Field>> = Vec::with_capacity(target_fields.len());
84 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(target_fields.len());
85 let num_rows = source_col.len();
86
87 for target_child_field in target_fields.iter() {
89 fields.push(Arc::clone(target_child_field));
90
91 let source_child_opt =
92 source_struct.column_by_name(target_child_field.name());
93
94 match source_child_opt {
95 Some(source_child_col) => {
96 let adapted_child = cast_column(
97 source_child_col,
98 target_child_field.data_type(),
99 cast_options,
100 )
101 .map_err(|e| {
102 e.context(format!(
103 "While casting struct field '{}'",
104 target_child_field.name()
105 ))
106 })?;
107 arrays.push(adapted_child);
108 }
109 None => {
110 arrays.push(new_null_array(target_child_field.data_type(), num_rows));
111 }
112 }
113 }
114
115 let struct_array =
116 StructArray::new(fields.into(), arrays, source_struct.nulls().cloned());
117 Ok(Arc::new(struct_array))
118 } else {
119 _plan_err!(
121 "Cannot cast column of type {} to struct type. Source must be a struct to cast to struct.",
122 source_col.data_type()
123 )
124 }
125}
126
127fn cast_union_column(
135 source_col: &ArrayRef,
136 source_fields: &UnionFields,
137 source_mode: &UnionMode,
138 target_fields: &UnionFields,
139 target_mode: &UnionMode,
140 cast_options: &CastOptions,
141) -> Result<ArrayRef> {
142 validate_union_schema_compatibility(
143 source_fields,
144 source_mode,
145 target_fields,
146 target_mode,
147 )?;
148
149 let source_union = source_col
150 .as_any()
151 .downcast_ref::<UnionArray>()
152 .ok_or_else(|| {
153 crate::error::DataFusionError::Plan(format!(
154 "Expected UnionArray for Union data type, got {}",
155 source_col.data_type()
156 ))
157 })?;
158
159 let mut children = Vec::with_capacity(target_fields.len());
160
161 for (target_type_id, target_field) in target_fields.iter() {
162 let source_child = source_union.child(target_type_id);
163
164 children.push(
165 cast_column(source_child, target_field.data_type(), cast_options).map_err(
166 |e| {
167 e.context(format!(
168 "While adapting Union child type ID {target_type_id} ('{}')",
169 target_field.name()
170 ))
171 },
172 )?,
173 );
174 }
175
176 Ok(Arc::new(UnionArray::try_new(
177 target_fields.clone(),
178 source_union.type_ids().clone(),
179 source_union.offsets().cloned(),
180 children,
181 )?))
182}
183
184pub fn cast_column(
239 source_col: &ArrayRef,
240 target_type: &DataType,
241 cast_options: &CastOptions,
242) -> Result<ArrayRef> {
243 match (source_col.data_type(), target_type) {
244 (_, Struct(target_fields)) => {
245 cast_struct_column(source_col, target_fields, cast_options)
246 }
247 (DataType::List(_), DataType::List(target_inner)) => {
248 cast_list_column::<i32>(source_col, target_inner, cast_options)
249 }
250 (DataType::LargeList(_), DataType::LargeList(target_inner)) => {
251 cast_list_column::<i64>(source_col, target_inner, cast_options)
252 }
253 (
254 DataType::FixedSizeList(_, source_list_size),
255 DataType::FixedSizeList(target_inner, target_list_size),
256 ) if source_list_size == target_list_size => cast_fixed_size_list_column(
257 source_col,
258 target_inner,
259 *target_list_size,
260 cast_options,
261 ),
262 (DataType::ListView(_), DataType::ListView(target_inner)) => {
263 cast_list_view_column::<i32>(source_col, target_inner, cast_options)
264 }
265 (DataType::LargeListView(_), DataType::LargeListView(target_inner)) => {
266 cast_list_view_column::<i64>(source_col, target_inner, cast_options)
267 }
268 (
269 DataType::Dictionary(source_key_type, _),
270 DataType::Dictionary(target_key_type, target_value_type),
271 ) => cast_dictionary_column(
272 source_col,
273 source_key_type,
274 target_key_type,
275 target_value_type,
276 cast_options,
277 ),
278 (
279 DataType::Union(source_fields, source_mode),
280 DataType::Union(target_fields, target_mode),
281 ) => cast_union_column(
282 source_col,
283 source_fields,
284 source_mode,
285 target_fields,
286 target_mode,
287 cast_options,
288 ),
289 _ => Ok(cast_with_options(source_col, target_type, cast_options)?),
290 }
291}
292
293fn cast_list_column<O: arrow::array::OffsetSizeTrait>(
294 source_col: &ArrayRef,
295 target_inner_field: &FieldRef,
296 cast_options: &CastOptions,
297) -> Result<ArrayRef> {
298 let source_list = source_col.as_list::<O>();
299
300 let cast_values = cast_column(
301 source_list.values(),
302 target_inner_field.data_type(),
303 cast_options,
304 )?;
305
306 let result = GenericListArray::<O>::new(
307 Arc::clone(target_inner_field),
308 source_list.offsets().clone(),
309 cast_values,
310 source_list.nulls().cloned(),
311 );
312 Ok(Arc::new(result))
313}
314
315fn cast_list_view_column<O: arrow::array::OffsetSizeTrait>(
316 source_col: &ArrayRef,
317 target_inner_field: &FieldRef,
318 cast_options: &CastOptions,
319) -> Result<ArrayRef> {
320 let source_list = source_col.as_list_view::<O>();
321
322 let cast_values = cast_column(
323 source_list.values(),
324 target_inner_field.data_type(),
325 cast_options,
326 )?;
327
328 let result = GenericListViewArray::<O>::try_new(
329 Arc::clone(target_inner_field),
330 source_list.offsets().clone(),
331 source_list.sizes().clone(),
332 cast_values,
333 source_list.nulls().cloned(),
334 )?;
335 Ok(Arc::new(result))
336}
337
338fn cast_fixed_size_list_column(
339 source_col: &ArrayRef,
340 target_inner_field: &FieldRef,
341 target_list_size: i32,
342 cast_options: &CastOptions,
343) -> Result<ArrayRef> {
344 let source_list = source_col.as_fixed_size_list();
345
346 let source_values = source_list.values();
347 let target_type = target_inner_field.data_type();
348
349 let cast_values = match cast_column(source_values, target_type, cast_options) {
350 Ok(cast_values) => cast_values,
351 Err(error) => match cast_fixed_size_list_values_with_parent_nulls(
352 source_values,
353 target_type,
354 cast_options,
355 source_list.nulls(),
356 target_list_size,
357 ) {
358 Some(masked_cast) => masked_cast?,
359 None => return Err(error),
360 },
361 };
362
363 Ok(Arc::new(FixedSizeListArray::try_new(
364 Arc::clone(target_inner_field),
365 target_list_size,
366 cast_values,
367 source_list.nulls().cloned(),
368 )?))
369}
370
371fn cast_fixed_size_list_values_with_parent_nulls(
372 source_values: &ArrayRef,
373 target_type: &DataType,
374 cast_options: &CastOptions,
375 parent_nulls: Option<&NullBuffer>,
376 list_size: i32,
377) -> Option<Result<ArrayRef>> {
378 let parent_nulls = parent_nulls.filter(|nulls| nulls.null_count() > 0)?;
379
380 let hidden_child_nulls = parent_nulls.expand(list_size as usize);
383 let masked_values = mask_array_values(source_values, &hidden_child_nulls);
384 Some(masked_values.and_then(|values| cast_column(&values, target_type, cast_options)))
385}
386
387fn mask_array_values(
388 values: &ArrayRef,
389 additional_nulls: &NullBuffer,
390) -> Result<ArrayRef> {
391 let nulls = NullBuffer::union(values.nulls(), Some(additional_nulls));
392
393 if let Some(struct_array) = values.as_any().downcast_ref::<StructArray>() {
394 let struct_nulls = nulls
395 .as_ref()
396 .expect("additional nulls always produce nulls");
397 let arrays = struct_array
398 .columns()
399 .iter()
400 .map(|child| mask_array_values(child, struct_nulls))
401 .collect::<Result<Vec<_>>>()?;
402 return Ok(Arc::new(StructArray::new(
403 struct_array.fields().clone(),
404 arrays,
405 nulls,
406 )));
407 }
408
409 Ok(make_array(
410 values.to_data().into_builder().nulls(nulls).build()?,
411 ))
412}
413
414fn cast_dictionary_column(
415 source_col: &ArrayRef,
416 source_key_type: &DataType,
417 target_key_type: &DataType,
418 target_value_type: &DataType,
419 cast_options: &CastOptions,
420) -> Result<ArrayRef> {
421 macro_rules! cast_dict_values {
424 ($t:ty) => {{
425 let source_dict = source_col
426 .as_any()
427 .downcast_ref::<DictionaryArray<$t>>()
428 .expect("downcast must succeed");
429 let cast_values =
430 cast_column(source_dict.values(), target_value_type, cast_options)?;
431 Ok(Arc::new(DictionaryArray::<$t>::new(
432 source_dict.keys().clone(),
433 cast_values,
434 )) as ArrayRef)
435 }};
436 }
437
438 let result: Result<ArrayRef> = downcast_integer! {
439 source_key_type => (cast_dict_values),
440 k => _plan_err!("Unsupported dictionary key type: {k}")
441 };
442 let result = result?;
443
444 if source_key_type != target_key_type {
446 let target_dict_type = DataType::Dictionary(
447 Box::new(target_key_type.clone()),
448 Box::new(target_value_type.clone()),
449 );
450 Ok(cast_with_options(&result, &target_dict_type, cast_options)?)
451 } else {
452 Ok(result)
453 }
454}
455
456pub fn validate_struct_compatibility(
493 source_fields: &[FieldRef],
494 target_fields: &[FieldRef],
495) -> Result<()> {
496 let has_overlap = has_one_of_more_common_fields(source_fields, target_fields);
497 if !has_overlap {
498 return _plan_err!(
499 "Cannot cast struct with {} fields to {} fields because there is no field name overlap",
500 source_fields.len(),
501 target_fields.len()
502 );
503 }
504
505 for target_field in target_fields {
507 if let Some(source_field) = source_fields
509 .iter()
510 .find(|f| f.name() == target_field.name())
511 {
512 validate_field_compatibility(source_field, target_field)?;
513 } else {
514 if !target_field.is_nullable() {
517 return _plan_err!(
518 "Cannot cast struct: target field '{}' is non-nullable but missing from source. \
519 Cannot fill with NULL.",
520 target_field.name()
521 );
522 }
523 }
524 }
525
526 Ok(())
528}
529
530fn validate_field_compatibility(
531 source_field: &Field,
532 target_field: &Field,
533) -> Result<()> {
534 if source_field.data_type() == &DataType::Null {
535 if !target_field.is_nullable() {
538 return _plan_err!(
539 "Cannot cast NULL struct field '{}' to non-nullable field '{}'",
540 source_field.name(),
541 target_field.name()
542 );
543 }
544 return Ok(());
545 }
546
547 if source_field.is_nullable() && !target_field.is_nullable() {
551 return _plan_err!(
552 "Cannot cast nullable struct field '{}' to non-nullable field",
553 target_field.name()
554 );
555 }
556
557 validate_data_type_compatibility(
558 target_field.name(),
559 source_field.data_type(),
560 target_field.data_type(),
561 )
562}
563
564fn validate_union_schema_compatibility(
565 source_fields: &UnionFields,
566 source_mode: &UnionMode,
567 target_fields: &UnionFields,
568 target_mode: &UnionMode,
569) -> Result<()> {
570 if source_mode != target_mode {
571 return _plan_err!(
572 "Cannot adapt Union from mode {source_mode:?} to {target_mode:?}"
573 );
574 }
575
576 if source_fields.len() != target_fields.len() {
578 return _plan_err!(
579 "Cannot adapt Union schema with different field sets: source has {} fields, target has {}",
580 source_fields.len(),
581 target_fields.len()
582 );
583 }
584
585 for (target_type_id, target_field) in target_fields.iter() {
586 let Some((_, source_field)) = source_fields
587 .iter()
588 .find(|(source_type_id, _)| *source_type_id == target_type_id)
589 else {
590 return _plan_err!(
591 "Cannot adapt Union schema: target type ID {target_type_id} ('{}') is missing from source",
592 target_field.name()
593 );
594 };
595
596 if !target_field.contains(source_field) {
597 return _plan_err!(
598 "Cannot adapt Union child with type ID {target_type_id}: source field {source_field} is not contained by target field {target_field}"
599 );
600 }
601 }
602
603 Ok(())
604}
605
606pub fn validate_data_type_compatibility(
609 field_name: &str,
610 source_type: &DataType,
611 target_type: &DataType,
612) -> Result<()> {
613 match (source_type, target_type) {
614 (Struct(source_nested), Struct(target_nested)) => {
615 validate_struct_compatibility(source_nested, target_nested)?;
616 }
617 (
618 DataType::FixedSizeList(s, source_list_size),
619 DataType::FixedSizeList(t, target_list_size),
620 ) if source_list_size == target_list_size => {
621 validate_field_compatibility(s, t)?;
622 }
623 (DataType::List(s), DataType::List(t))
624 | (DataType::LargeList(s), DataType::LargeList(t))
625 | (DataType::ListView(s), DataType::ListView(t))
626 | (DataType::LargeListView(s), DataType::LargeListView(t)) => {
627 validate_field_compatibility(s, t)?;
628 }
629 (DataType::Dictionary(s_key, s_val), DataType::Dictionary(t_key, t_val)) => {
630 if !can_cast_types(s_key, t_key) {
631 return _plan_err!(
632 "Cannot cast dictionary key type {} to {} for field '{}'",
633 s_key,
634 t_key,
635 field_name
636 );
637 }
638 validate_data_type_compatibility(field_name, s_val, t_val)?;
639 }
640 (
641 DataType::Union(source_fields, source_mode),
642 DataType::Union(target_fields, target_mode),
643 ) => {
644 validate_union_schema_compatibility(
645 source_fields,
646 source_mode,
647 target_fields,
648 target_mode,
649 )?;
650 }
651 _ => {
652 if !can_cast_types(source_type, target_type) {
653 return _plan_err!(
654 "Cannot cast struct field '{}' from type {} to type {}",
655 field_name,
656 source_type,
657 target_type
658 );
659 }
660 }
661 }
662 Ok(())
663}
664
665pub fn requires_nested_struct_cast(
676 source_type: &DataType,
677 target_type: &DataType,
678) -> bool {
679 match (source_type, target_type) {
680 (Struct(_), Struct(_)) => true,
681 (
682 DataType::FixedSizeList(s, source_list_size),
683 DataType::FixedSizeList(t, target_list_size),
684 ) if source_list_size == target_list_size => {
685 requires_nested_struct_cast(s.data_type(), t.data_type())
686 }
687 (DataType::List(s), DataType::List(t))
688 | (DataType::LargeList(s), DataType::LargeList(t))
689 | (DataType::ListView(s), DataType::ListView(t))
690 | (DataType::LargeListView(s), DataType::LargeListView(t)) => {
691 requires_nested_struct_cast(s.data_type(), t.data_type())
692 }
693 (DataType::Dictionary(_, s_val), DataType::Dictionary(_, t_val)) => {
694 requires_nested_struct_cast(s_val, t_val)
695 }
696 _ => false,
697 }
698}
699
700pub fn has_one_of_more_common_fields(
705 source_fields: &[FieldRef],
706 target_fields: &[FieldRef],
707) -> bool {
708 let source_names: HashSet<&str> = source_fields
709 .iter()
710 .map(|field| field.name().as_str())
711 .collect();
712 target_fields
713 .iter()
714 .any(|field| source_names.contains(field.name().as_str()))
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use crate::{assert_contains, format::DEFAULT_CAST_OPTIONS};
721 use arrow::{
722 array::{
723 BinaryArray, FixedSizeListArray, Int32Array, Int32Builder, Int64Array,
724 ListArray, ListViewArray, MapArray, MapBuilder, NullArray, StringArray,
725 StringBuilder,
726 },
727 buffer::{NullBuffer, ScalarBuffer},
728 datatypes::{DataType, Field, FieldRef, Int32Type},
729 };
730 macro_rules! get_column_as {
732 ($struct_array:expr, $column_name:expr, $array_type:ty) => {
733 $struct_array
734 .column_by_name($column_name)
735 .unwrap()
736 .as_any()
737 .downcast_ref::<$array_type>()
738 .unwrap()
739 };
740 }
741
742 fn field(name: &str, data_type: DataType) -> Field {
743 Field::new(name, data_type, true)
744 }
745
746 fn non_null_field(name: &str, data_type: DataType) -> Field {
747 Field::new(name, data_type, false)
748 }
749
750 fn arc_field(name: &str, data_type: DataType) -> FieldRef {
751 Arc::new(field(name, data_type))
752 }
753
754 fn struct_type(fields: Vec<Field>) -> DataType {
755 Struct(fields.into())
756 }
757
758 fn struct_field(name: &str, fields: Vec<Field>) -> Field {
759 field(name, struct_type(fields))
760 }
761
762 fn arc_struct_field(name: &str, fields: Vec<Field>) -> FieldRef {
763 Arc::new(struct_field(name, fields))
764 }
765
766 #[test]
767 fn test_cast_simple_column() {
768 let source = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
769 let target_field = field("ints", DataType::Int64);
770 let result =
771 cast_column(&source, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
772 .unwrap();
773 let result = result.as_any().downcast_ref::<Int64Array>().unwrap();
774 assert_eq!(result.len(), 3);
775 assert_eq!(result.value(0), 1);
776 assert_eq!(result.value(1), 2);
777 assert_eq!(result.value(2), 3);
778 }
779
780 #[test]
781 fn test_cast_column_with_options() {
782 let source = Arc::new(Int64Array::from(vec![1, i64::MAX])) as ArrayRef;
783 let target_field = field("ints", DataType::Int32);
784
785 let safe_opts = CastOptions {
786 safe: false,
788 ..DEFAULT_CAST_OPTIONS
789 };
790 assert!(cast_column(&source, target_field.data_type(), &safe_opts).is_err());
791
792 let unsafe_opts = CastOptions {
793 safe: true,
795 ..DEFAULT_CAST_OPTIONS
796 };
797 let result =
798 cast_column(&source, target_field.data_type(), &unsafe_opts).unwrap();
799 let result = result.as_any().downcast_ref::<Int32Array>().unwrap();
800 assert_eq!(result.value(0), 1);
801 assert!(result.is_null(1));
802 }
803
804 #[test]
805 fn test_cast_struct_with_missing_field() {
806 let a_array = Arc::new(Int32Array::from(vec![1, 2])) as ArrayRef;
807 let source_struct = StructArray::from(vec![(
808 arc_field("a", DataType::Int32),
809 Arc::clone(&a_array),
810 )]);
811 let source_col = Arc::new(source_struct) as ArrayRef;
812
813 let target_field = struct_field(
814 "s",
815 vec![field("a", DataType::Int32), field("b", DataType::Utf8)],
816 );
817
818 let result =
819 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
820 .unwrap();
821 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
822 assert_eq!(struct_array.fields().len(), 2);
823 let a_result = get_column_as!(&struct_array, "a", Int32Array);
824 assert_eq!(a_result.value(0), 1);
825 assert_eq!(a_result.value(1), 2);
826
827 let b_result = get_column_as!(&struct_array, "b", StringArray);
828 assert_eq!(b_result.len(), 2);
829 assert!(b_result.is_null(0));
830 assert!(b_result.is_null(1));
831 }
832
833 #[test]
834 fn test_cast_struct_source_not_struct() {
835 let source = Arc::new(Int32Array::from(vec![10, 20])) as ArrayRef;
836 let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
837
838 let result =
839 cast_column(&source, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
840 assert!(result.is_err());
841 let error_msg = result.unwrap_err().to_string();
842 assert!(error_msg.contains("Cannot cast column of type"));
843 assert!(error_msg.contains("to struct type"));
844 assert!(error_msg.contains("Source must be a struct"));
845 }
846
847 #[test]
848 fn test_cast_struct_incompatible_child_type() {
849 let a_array = Arc::new(BinaryArray::from(vec![
850 Some(b"a".as_ref()),
851 Some(b"b".as_ref()),
852 ])) as ArrayRef;
853 let source_struct =
854 StructArray::from(vec![(arc_field("a", DataType::Binary), a_array)]);
855 let source_col = Arc::new(source_struct) as ArrayRef;
856
857 let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
858
859 let result =
860 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
861 assert!(result.is_err());
862 let error_msg = result.unwrap_err().to_string();
863 assert!(error_msg.contains("Cannot cast struct field 'a'"));
864 }
865
866 #[test]
867 fn test_validate_struct_compatibility_incompatible_types() {
868 let source_fields = vec![
870 arc_field("field1", DataType::Binary),
871 arc_field("field2", DataType::Utf8),
872 ];
873
874 let target_fields = vec![arc_field("field1", DataType::Int32)];
876
877 let result = validate_struct_compatibility(&source_fields, &target_fields);
878 assert!(result.is_err());
879 let error_msg = result.unwrap_err().to_string();
880 assert!(error_msg.contains("Cannot cast struct field 'field1'"));
881 assert!(error_msg.contains("Binary"));
882 assert!(error_msg.contains("Int32"));
883 }
884
885 #[test]
886 fn test_validate_struct_compatibility_compatible_types() {
887 let source_fields = vec![
889 arc_field("field1", DataType::Int32),
890 arc_field("field2", DataType::Utf8),
891 ];
892
893 let target_fields = vec![arc_field("field1", DataType::Int64)];
895
896 let result = validate_struct_compatibility(&source_fields, &target_fields);
897 assert!(result.is_ok());
898 }
899
900 #[test]
901 fn test_validate_struct_compatibility_missing_field_in_source() {
902 let source_fields = vec![arc_field("field1", DataType::Int32)];
904
905 let target_fields = vec![
907 arc_field("field1", DataType::Int32),
908 arc_field("field2", DataType::Utf8),
909 ];
910
911 let result = validate_struct_compatibility(&source_fields, &target_fields);
913 assert!(result.is_ok());
914 }
915
916 #[test]
917 fn test_validate_struct_compatibility_additional_field_in_source() {
918 let source_fields = vec![
920 arc_field("field1", DataType::Int32),
921 arc_field("field2", DataType::Utf8),
922 ];
923
924 let target_fields = vec![arc_field("field1", DataType::Int32)];
926
927 let result = validate_struct_compatibility(&source_fields, &target_fields);
929 assert!(result.is_ok());
930 }
931
932 #[test]
933 fn test_validate_struct_compatibility_no_overlap_mismatch_len() {
934 let source_fields = vec![
935 arc_field("left", DataType::Int32),
936 arc_field("right", DataType::Int32),
937 ];
938 let target_fields = vec![arc_field("alpha", DataType::Int32)];
939
940 let result = validate_struct_compatibility(&source_fields, &target_fields);
941 assert!(result.is_err());
942 let error_msg = result.unwrap_err().to_string();
943 assert_contains!(error_msg, "no field name overlap");
944 }
945
946 #[test]
947 fn test_cast_struct_parent_nulls_retained() {
948 let a_array = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
949 let fields = vec![arc_field("a", DataType::Int32)];
950 let nulls = Some(NullBuffer::from(vec![true, false]));
951 let source_struct = StructArray::new(fields.clone().into(), vec![a_array], nulls);
952 let source_col = Arc::new(source_struct) as ArrayRef;
953
954 let target_field = struct_field("s", vec![field("a", DataType::Int64)]);
955
956 let result =
957 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
958 .unwrap();
959 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
960 assert_eq!(struct_array.null_count(), 1);
961 assert!(struct_array.is_valid(0));
962 assert!(struct_array.is_null(1));
963
964 let a_result = get_column_as!(&struct_array, "a", Int64Array);
965 assert_eq!(a_result.value(0), 1);
966 assert_eq!(a_result.value(1), 2);
967 }
968
969 #[test]
970 fn test_validate_struct_compatibility_nullable_to_non_nullable() {
971 let source_fields = vec![arc_field("field1", DataType::Int32)];
973
974 let target_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
976
977 let result = validate_struct_compatibility(&source_fields, &target_fields);
978 assert!(result.is_err());
979 let error_msg = result.unwrap_err().to_string();
980 assert!(error_msg.contains("field1"));
981 assert!(error_msg.contains("non-nullable"));
982 }
983
984 #[test]
985 fn test_validate_struct_compatibility_non_nullable_to_nullable() {
986 let source_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
988
989 let target_fields = vec![arc_field("field1", DataType::Int32)];
991
992 let result = validate_struct_compatibility(&source_fields, &target_fields);
993 assert!(result.is_ok());
994 }
995
996 #[test]
997 fn test_validate_struct_compatibility_nested_nullable_to_non_nullable() {
998 let source_fields = vec![Arc::new(non_null_field(
1000 "field1",
1001 struct_type(vec![field("nested", DataType::Int32)]),
1002 ))];
1003
1004 let target_fields = vec![Arc::new(non_null_field(
1006 "field1",
1007 struct_type(vec![non_null_field("nested", DataType::Int32)]),
1008 ))];
1009
1010 let result = validate_struct_compatibility(&source_fields, &target_fields);
1011 assert!(result.is_err());
1012 let error_msg = result.unwrap_err().to_string();
1013 assert!(error_msg.contains("nested"));
1014 assert!(error_msg.contains("non-nullable"));
1015 }
1016
1017 #[test]
1018 fn test_validate_struct_compatibility_by_name() {
1019 let source_fields = vec![
1021 arc_field("field1", DataType::Int32),
1022 arc_field("field2", DataType::Utf8),
1023 ];
1024
1025 let target_fields = vec![
1027 arc_field("field2", DataType::Utf8),
1028 arc_field("field1", DataType::Int64),
1029 ];
1030
1031 let result = validate_struct_compatibility(&source_fields, &target_fields);
1032 assert!(result.is_ok());
1033 }
1034
1035 #[test]
1036 fn test_validate_struct_compatibility_by_name_with_type_mismatch() {
1037 let source_fields = vec![arc_field("field1", DataType::Binary)];
1039
1040 let target_fields = vec![arc_field("field1", DataType::Int32)];
1042
1043 let result = validate_struct_compatibility(&source_fields, &target_fields);
1044 assert!(result.is_err());
1045 let error_msg = result.unwrap_err().to_string();
1046 assert_contains!(
1047 error_msg,
1048 "Cannot cast struct field 'field1' from type Binary to type Int32"
1049 );
1050 }
1051
1052 #[test]
1053 fn test_validate_struct_compatibility_no_overlap_equal_len() {
1054 let source_fields = vec![
1055 arc_field("left", DataType::Int32),
1056 arc_field("right", DataType::Utf8),
1057 ];
1058
1059 let target_fields = vec![
1060 arc_field("alpha", DataType::Int32),
1061 arc_field("beta", DataType::Utf8),
1062 ];
1063
1064 let result = validate_struct_compatibility(&source_fields, &target_fields);
1065 assert!(result.is_err());
1066 let error_msg = result.unwrap_err().to_string();
1067 assert_contains!(error_msg, "no field name overlap");
1068 }
1069
1070 #[test]
1071 fn test_validate_struct_compatibility_mixed_name_overlap() {
1072 let source_fields = vec![
1074 arc_field("a", DataType::Int32),
1075 arc_field("b", DataType::Utf8),
1076 arc_field("extra", DataType::Boolean),
1077 ];
1078
1079 let target_fields = vec![
1082 arc_field("b", DataType::Utf8),
1083 arc_field("a", DataType::Int64),
1084 arc_field("c", DataType::Float32),
1085 ];
1086
1087 let result = validate_struct_compatibility(&source_fields, &target_fields);
1088 assert!(result.is_ok());
1089 }
1090
1091 #[test]
1092 fn test_validate_struct_compatibility_by_name_missing_required_field() {
1093 let source_fields = vec![arc_field("field1", DataType::Int32)];
1095
1096 let target_fields = vec![
1098 arc_field("field1", DataType::Int32),
1099 Arc::new(non_null_field("field2", DataType::Int32)),
1100 ];
1101
1102 let result = validate_struct_compatibility(&source_fields, &target_fields);
1103 assert!(result.is_err());
1104 let error_msg = result.unwrap_err().to_string();
1105 assert_contains!(
1106 error_msg,
1107 "Cannot cast struct: target field 'field2' is non-nullable but missing from source. Cannot fill with NULL."
1108 );
1109 }
1110
1111 #[test]
1112 fn test_validate_struct_compatibility_partial_name_overlap_with_count_mismatch() {
1113 let source_fields = vec![arc_field("a", DataType::Int32)];
1115
1116 let target_fields = vec![
1118 arc_field("a", DataType::Int32),
1119 arc_field("b", DataType::Utf8),
1120 ];
1121
1122 let result = validate_struct_compatibility(&source_fields, &target_fields);
1125 assert!(result.is_ok());
1126 }
1127
1128 #[test]
1129 fn test_cast_nested_struct_with_extra_and_missing_fields() {
1130 let a = Arc::new(Int32Array::from(vec![Some(1), None])) as ArrayRef;
1132 let b = Arc::new(Int32Array::from(vec![Some(2), Some(3)])) as ArrayRef;
1133 let extra = Arc::new(Int32Array::from(vec![Some(9), Some(10)])) as ArrayRef;
1134
1135 let inner = StructArray::from(vec![
1136 (arc_field("a", DataType::Int32), a),
1137 (arc_field("b", DataType::Int32), b),
1138 (arc_field("extra", DataType::Int32), extra),
1139 ]);
1140
1141 let source_struct = StructArray::from(vec![(
1142 arc_struct_field(
1143 "inner",
1144 vec![
1145 field("a", DataType::Int32),
1146 field("b", DataType::Int32),
1147 field("extra", DataType::Int32),
1148 ],
1149 ),
1150 Arc::new(inner) as ArrayRef,
1151 )]);
1152 let source_col = Arc::new(source_struct) as ArrayRef;
1153
1154 let target_field = struct_field(
1156 "outer",
1157 vec![struct_field(
1158 "inner",
1159 vec![
1160 field("b", DataType::Int64),
1161 field("a", DataType::Int32),
1162 field("missing", DataType::Int32),
1163 ],
1164 )],
1165 );
1166
1167 let result =
1168 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1169 .unwrap();
1170 let outer = result.as_any().downcast_ref::<StructArray>().unwrap();
1171 let inner = get_column_as!(&outer, "inner", StructArray);
1172 assert_eq!(inner.fields().len(), 3);
1173
1174 let b = get_column_as!(inner, "b", Int64Array);
1175 assert_eq!(b.value(0), 2);
1176 assert_eq!(b.value(1), 3);
1177 assert!(!b.is_null(0));
1178 assert!(!b.is_null(1));
1179
1180 let a = get_column_as!(inner, "a", Int32Array);
1181 assert_eq!(a.value(0), 1);
1182 assert!(a.is_null(1));
1183
1184 let missing = get_column_as!(inner, "missing", Int32Array);
1185 assert!(missing.is_null(0));
1186 assert!(missing.is_null(1));
1187 }
1188
1189 #[test]
1190 fn test_cast_null_struct_field_to_nested_struct() {
1191 let null_inner = Arc::new(NullArray::new(2)) as ArrayRef;
1192 let source_struct = StructArray::from(vec![(
1193 arc_field("inner", DataType::Null),
1194 Arc::clone(&null_inner),
1195 )]);
1196 let source_col = Arc::new(source_struct) as ArrayRef;
1197
1198 let target_field = struct_field(
1199 "outer",
1200 vec![struct_field("inner", vec![field("a", DataType::Int32)])],
1201 );
1202
1203 let result =
1204 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1205 .unwrap();
1206 let outer = result.as_any().downcast_ref::<StructArray>().unwrap();
1207 let inner = get_column_as!(&outer, "inner", StructArray);
1208 assert_eq!(inner.len(), 2);
1209 assert!(inner.is_null(0));
1210 assert!(inner.is_null(1));
1211
1212 let inner_a = get_column_as!(inner, "a", Int32Array);
1213 assert!(inner_a.is_null(0));
1214 assert!(inner_a.is_null(1));
1215 }
1216
1217 #[test]
1218 fn test_cast_struct_with_array_and_map_fields() {
1219 let arr_array = Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
1221 Some(vec![Some(1), Some(2)]),
1222 None,
1223 ])) as ArrayRef;
1224
1225 let string_builder = StringBuilder::new();
1227 let int_builder = Int32Builder::new();
1228 let mut map_builder = MapBuilder::new(None, string_builder, int_builder);
1229 map_builder.keys().append_value("a");
1230 map_builder.values().append_value(1);
1231 map_builder.append(true).unwrap();
1232 map_builder.append(false).unwrap();
1233 let map_array = Arc::new(map_builder.finish()) as ArrayRef;
1234
1235 let source_struct = StructArray::from(vec![
1236 (
1237 arc_field(
1238 "arr",
1239 DataType::List(Arc::new(field("item", DataType::Int32))),
1240 ),
1241 arr_array,
1242 ),
1243 (
1244 arc_field(
1245 "map",
1246 DataType::Map(
1247 Arc::new(non_null_field(
1248 "entries",
1249 struct_type(vec![
1250 non_null_field("keys", DataType::Utf8),
1251 field("values", DataType::Int32),
1252 ]),
1253 )),
1254 false,
1255 ),
1256 ),
1257 map_array,
1258 ),
1259 ]);
1260 let source_col = Arc::new(source_struct) as ArrayRef;
1261
1262 let target_field = struct_field(
1263 "s",
1264 vec![
1265 field(
1266 "arr",
1267 DataType::List(Arc::new(field("item", DataType::Int32))),
1268 ),
1269 field(
1270 "map",
1271 DataType::Map(
1272 Arc::new(non_null_field(
1273 "entries",
1274 struct_type(vec![
1275 non_null_field("keys", DataType::Utf8),
1276 field("values", DataType::Int32),
1277 ]),
1278 )),
1279 false,
1280 ),
1281 ),
1282 ],
1283 );
1284
1285 let result =
1286 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1287 .unwrap();
1288 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1289
1290 let arr = get_column_as!(&struct_array, "arr", ListArray);
1291 assert!(!arr.is_null(0));
1292 assert!(arr.is_null(1));
1293 let arr0 = arr.value(0);
1294 let values = arr0.as_any().downcast_ref::<Int32Array>().unwrap();
1295 assert_eq!(values.value(0), 1);
1296 assert_eq!(values.value(1), 2);
1297
1298 let map = get_column_as!(&struct_array, "map", MapArray);
1299 assert!(!map.is_null(0));
1300 assert!(map.is_null(1));
1301 let map0 = map.value(0);
1302 let entries = map0.as_any().downcast_ref::<StructArray>().unwrap();
1303 let keys = get_column_as!(entries, "keys", StringArray);
1304 let vals = get_column_as!(entries, "values", Int32Array);
1305 assert_eq!(keys.value(0), "a");
1306 assert_eq!(vals.value(0), 1);
1307 }
1308
1309 #[test]
1310 fn test_cast_struct_field_order_differs() {
1311 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1312 let b = Arc::new(Int32Array::from(vec![Some(3), None])) as ArrayRef;
1313
1314 let source_struct = StructArray::from(vec![
1315 (arc_field("a", DataType::Int32), a),
1316 (arc_field("b", DataType::Int32), b),
1317 ]);
1318 let source_col = Arc::new(source_struct) as ArrayRef;
1319
1320 let target_field = struct_field(
1321 "s",
1322 vec![field("b", DataType::Int64), field("a", DataType::Int32)],
1323 );
1324
1325 let result =
1326 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1327 .unwrap();
1328 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1329
1330 let b_col = get_column_as!(&struct_array, "b", Int64Array);
1331 assert_eq!(b_col.value(0), 3);
1332 assert!(b_col.is_null(1));
1333
1334 let a_col = get_column_as!(&struct_array, "a", Int32Array);
1335 assert_eq!(a_col.value(0), 1);
1336 assert_eq!(a_col.value(1), 2);
1337 }
1338
1339 #[test]
1340 fn test_cast_struct_no_overlap_rejected() {
1341 let first = Arc::new(Int32Array::from(vec![Some(10), Some(20)])) as ArrayRef;
1342 let second =
1343 Arc::new(StringArray::from(vec![Some("alpha"), Some("beta")])) as ArrayRef;
1344
1345 let source_struct = StructArray::from(vec![
1346 (arc_field("left", DataType::Int32), first),
1347 (arc_field("right", DataType::Utf8), second),
1348 ]);
1349 let source_col = Arc::new(source_struct) as ArrayRef;
1350
1351 let target_field = struct_field(
1352 "s",
1353 vec![field("a", DataType::Int64), field("b", DataType::Utf8)],
1354 );
1355
1356 let result =
1357 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
1358 assert!(result.is_err());
1359 let error_msg = result.unwrap_err().to_string();
1360 assert_contains!(error_msg, "no field name overlap");
1361 }
1362
1363 #[test]
1364 fn test_cast_struct_missing_non_nullable_field_fails() {
1365 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1367 let source_struct = StructArray::from(vec![(arc_field("a", DataType::Int32), a)]);
1368 let source_col = Arc::new(source_struct) as ArrayRef;
1369
1370 let target_field = struct_field(
1372 "s",
1373 vec![
1374 field("a", DataType::Int32),
1375 non_null_field("b", DataType::Int32),
1376 ],
1377 );
1378
1379 let result =
1381 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
1382 assert!(result.is_err());
1383 let err = result.unwrap_err();
1384 assert!(
1385 err.to_string()
1386 .contains("target field 'b' is non-nullable but missing from source"),
1387 "Unexpected error: {err}"
1388 );
1389 }
1390
1391 #[test]
1392 fn test_cast_struct_missing_nullable_field_succeeds() {
1393 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1395 let source_struct = StructArray::from(vec![(arc_field("a", DataType::Int32), a)]);
1396 let source_col = Arc::new(source_struct) as ArrayRef;
1397
1398 let target_field = struct_field(
1400 "s",
1401 vec![field("a", DataType::Int32), field("b", DataType::Int32)],
1402 );
1403
1404 let result =
1406 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1407 .unwrap();
1408 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1409
1410 let a_col = get_column_as!(&struct_array, "a", Int32Array);
1411 assert_eq!(a_col.value(0), 1);
1412 assert_eq!(a_col.value(1), 2);
1413
1414 let b_col = get_column_as!(&struct_array, "b", Int32Array);
1415 assert!(b_col.is_null(0));
1416 assert!(b_col.is_null(1));
1417 }
1418
1419 #[test]
1420 fn test_validate_dictionary_value_evolution() {
1421 let source_inner = struct_type(vec![field("a", DataType::Int32)]);
1422 let target_inner = struct_type(vec![
1423 field("a", DataType::Int32),
1424 field("b", DataType::Utf8),
1425 ]);
1426 let source =
1427 DataType::Dictionary(Box::new(DataType::Int32), Box::new(source_inner));
1428 let target =
1429 DataType::Dictionary(Box::new(DataType::Int32), Box::new(target_inner));
1430 assert!(validate_data_type_compatibility("col", &source, &target).is_ok());
1431 }
1432
1433 #[test]
1434 fn test_cast_dictionary_struct_value() {
1435 let struct_arr = StructArray::from(vec![(
1438 arc_field("a", DataType::Int32),
1439 Arc::new(Int32Array::from(vec![10, 20])) as ArrayRef,
1440 )]);
1441 let keys = Int32Array::from(vec![Some(0), None, Some(1)]);
1443 let source_dict = DictionaryArray::<Int32Type>::new(keys, Arc::new(struct_arr));
1444 let source_col: ArrayRef = Arc::new(source_dict);
1445
1446 let target_type = DataType::Dictionary(
1447 Box::new(DataType::Int32),
1448 Box::new(struct_type(vec![
1449 field("a", DataType::Int64),
1450 field("b", DataType::Utf8),
1451 ])),
1452 );
1453
1454 let result =
1455 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1456 let result_dict = result
1457 .as_any()
1458 .downcast_ref::<DictionaryArray<Int32Type>>()
1459 .unwrap();
1460
1461 assert!(result_dict.is_valid(0));
1462 assert!(result_dict.is_null(1));
1463 assert!(result_dict.is_valid(2));
1464
1465 let struct_values = result_dict
1466 .values()
1467 .as_any()
1468 .downcast_ref::<StructArray>()
1469 .unwrap();
1470 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1471 assert_eq!(a_col.values(), &[10, 20]);
1472 let b_col = get_column_as!(&struct_values, "b", StringArray);
1473 assert!(b_col.iter().all(|v| v.is_none()));
1474 }
1475
1476 #[test]
1477 fn test_cast_list_view_struct() {
1478 let struct_arr = StructArray::from(vec![(
1481 arc_field("a", DataType::Int32),
1482 Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef,
1483 )]);
1484
1485 let source_field =
1486 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1487 let target_field = arc_field(
1488 "item",
1489 struct_type(vec![
1490 field("a", DataType::Int64),
1491 field("b", DataType::Utf8),
1492 ]),
1493 );
1494
1495 let list_view = ListViewArray::new(
1497 source_field,
1498 ScalarBuffer::from(vec![0i32, 2]),
1499 ScalarBuffer::from(vec![2i32, 1]),
1500 Arc::new(struct_arr),
1501 None,
1502 );
1503 let source_col: ArrayRef = Arc::new(list_view);
1504
1505 let target_type = DataType::ListView(target_field);
1506
1507 let result =
1508 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1509 let result_lv = result.as_any().downcast_ref::<ListViewArray>().unwrap();
1510 assert_eq!(result_lv.len(), 2);
1511
1512 let struct_values = result_lv
1513 .values()
1514 .as_any()
1515 .downcast_ref::<StructArray>()
1516 .unwrap();
1517 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1518 assert_eq!(a_col.values(), &[1, 2, 3]);
1519 let b_col = get_column_as!(&struct_values, "b", StringArray);
1520 assert!(b_col.iter().all(|v| v.is_none()));
1521 }
1522
1523 fn fixed_size_list_struct_field(fields: Vec<(&str, DataType)>) -> FieldRef {
1524 arc_field(
1525 "item",
1526 struct_type(
1527 fields
1528 .into_iter()
1529 .map(|(name, data_type)| field(name, data_type))
1530 .collect(),
1531 ),
1532 )
1533 }
1534
1535 fn create_fixed_size_list_test_fields(
1536 source_struct_fields: Vec<(&str, DataType)>,
1537 target_struct_fields: Vec<(&str, DataType)>,
1538 ) -> (FieldRef, FieldRef) {
1539 (
1540 fixed_size_list_struct_field(source_struct_fields),
1541 fixed_size_list_struct_field(target_struct_fields),
1542 )
1543 }
1544
1545 fn fixed_size_list_struct_values(
1546 array: &ArrayRef,
1547 ) -> (&FixedSizeListArray, &StructArray) {
1548 let list = array.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
1549 let values = list
1550 .values()
1551 .as_any()
1552 .downcast_ref::<StructArray>()
1553 .unwrap();
1554 (list, values)
1555 }
1556
1557 #[test]
1558 fn test_cast_fixed_size_list_struct() {
1559 let struct_arr = StructArray::from(vec![(
1560 arc_field("a", DataType::Int32),
1561 Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as ArrayRef,
1562 )]);
1563
1564 let (source_field, target_field) = create_fixed_size_list_test_fields(
1565 vec![("a", DataType::Int32)],
1566 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1567 );
1568 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1569 source_field,
1570 2,
1571 Arc::new(struct_arr),
1572 Some(NullBuffer::from(vec![true, false])),
1573 ));
1574 let target_type = DataType::FixedSizeList(target_field, 2);
1575
1576 let result =
1577 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1578 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1579 assert_eq!(result_list.len(), 2);
1580 assert!(result_list.is_valid(0));
1581 assert!(result_list.is_null(1));
1582 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1583 assert_eq!(a_col.values(), &[1, 2, 3, 4]);
1584 let b_col = get_column_as!(&struct_values, "b", StringArray);
1585 assert!(b_col.iter().all(|v| v.is_none()));
1586 }
1587
1588 #[test]
1589 fn test_validate_fixed_size_list_struct_compatibility() {
1590 let (source_field, target_field) = create_fixed_size_list_test_fields(
1591 vec![("a", DataType::Int32)],
1592 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1593 );
1594 let source = DataType::FixedSizeList(source_field, 2);
1595 let target = DataType::FixedSizeList(target_field, 2);
1596
1597 assert!(requires_nested_struct_cast(&source, &target));
1598 assert!(validate_data_type_compatibility("col", &source, &target).is_ok());
1599 }
1600
1601 #[test]
1602 fn test_validate_fixed_size_list_struct_missing_non_nullable_field_rejected() {
1603 let (source_field, _) = create_fixed_size_list_test_fields(
1604 vec![("a", DataType::Int32)],
1605 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1606 );
1607 let source = DataType::FixedSizeList(source_field, 2);
1608 let target = DataType::FixedSizeList(
1609 arc_field(
1610 "item",
1611 struct_type(vec![
1612 field("a", DataType::Int32),
1613 non_null_field("b", DataType::Utf8),
1614 ]),
1615 ),
1616 2,
1617 );
1618
1619 let error = validate_data_type_compatibility("col", &source, &target)
1620 .unwrap_err()
1621 .to_string();
1622 assert_contains!(
1623 error,
1624 "target field 'b' is non-nullable but missing from source"
1625 );
1626 }
1627
1628 #[test]
1629 fn test_fixed_size_list_struct_size_mismatch_rejected() {
1630 let source_field = fixed_size_list_struct_field(vec![("a", DataType::Int32)]);
1631 let target_field = Arc::clone(&source_field);
1632 let source_type = DataType::FixedSizeList(Arc::clone(&source_field), 2);
1633 let target_type = DataType::FixedSizeList(target_field, 3);
1634
1635 let validation_error =
1636 validate_data_type_compatibility("col", &source_type, &target_type)
1637 .unwrap_err()
1638 .to_string();
1639 assert_contains!(validation_error, "Cannot cast struct field 'col'");
1640
1641 let struct_arr = StructArray::from(vec![(
1642 arc_field("a", DataType::Int32),
1643 Arc::new(Int32Array::from(vec![1, 2])) as ArrayRef,
1644 )]);
1645 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1646 source_field,
1647 2,
1648 Arc::new(struct_arr),
1649 None,
1650 ));
1651
1652 let runtime_error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1653 .unwrap_err()
1654 .to_string();
1655 assert_contains!(
1656 runtime_error,
1657 "cannot cast fixed-size-list to fixed-size-list with different size"
1658 );
1659 }
1660
1661 #[test]
1662 fn test_cast_fixed_size_list_struct_all_null() {
1663 let (source_field, target_field) = create_fixed_size_list_test_fields(
1664 vec![("a", DataType::Int32)],
1665 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1666 );
1667 let source_col: ArrayRef =
1668 Arc::new(FixedSizeListArray::new_null(source_field, 2, 2));
1669 let target_type = DataType::FixedSizeList(target_field, 2);
1670
1671 let result =
1672 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1673 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1674 assert_eq!(result_list.null_count(), 2);
1675 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1676 let b_col = get_column_as!(&struct_values, "b", StringArray);
1677 assert!(a_col.iter().all(|v| v.is_none()));
1678 assert!(b_col.iter().all(|v| v.is_none()));
1679 }
1680
1681 #[test]
1682 fn test_fixed_size_list_struct_planner_runtime_parity_on_incompatible_type() {
1683 let source_field =
1684 arc_field("item", struct_type(vec![field("a", DataType::Binary)]));
1685 let target_field =
1686 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1687 let source_type = DataType::FixedSizeList(Arc::clone(&source_field), 2);
1688 let target_type = DataType::FixedSizeList(target_field, 2);
1689 let validation_error =
1690 validate_data_type_compatibility("col", &source_type, &target_type)
1691 .unwrap_err()
1692 .to_string();
1693 assert_contains!(validation_error, "Cannot cast struct field 'a'");
1694
1695 let struct_arr = StructArray::from(vec![(
1696 arc_field("a", DataType::Binary),
1697 Arc::new(BinaryArray::from(vec![
1698 Some(b"x".as_ref()),
1699 Some(b"y".as_ref()),
1700 ])) as ArrayRef,
1701 )]);
1702 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1703 source_field,
1704 2,
1705 Arc::new(struct_arr),
1706 None,
1707 ));
1708
1709 let runtime_error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1710 .unwrap_err()
1711 .to_string();
1712 assert_contains!(runtime_error, "Cannot cast struct field 'a'");
1713 }
1714
1715 #[test]
1716 fn test_cast_fixed_size_list_struct_missing_non_nullable_field_runtime_rejected() {
1717 let source_field =
1718 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1719 let target_field = arc_field(
1720 "item",
1721 struct_type(vec![
1722 field("a", DataType::Int32),
1723 non_null_field("b", DataType::Utf8),
1724 ]),
1725 );
1726 let source_col: ArrayRef =
1727 Arc::new(FixedSizeListArray::new_null(source_field, 2, 1));
1728 let target_type = DataType::FixedSizeList(target_field, 2);
1729
1730 let error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1731 .unwrap_err()
1732 .to_string();
1733 assert_contains!(
1734 error,
1735 "target field 'b' is non-nullable but missing from source"
1736 );
1737 }
1738
1739 #[test]
1740 fn test_cast_fixed_size_list_returns_error_for_non_nullable_child() {
1741 let source_field = Arc::new(Field::new("item", DataType::Int32, true));
1742 let target_field = Arc::new(Field::new("item", DataType::Int32, false));
1743 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1744 source_field,
1745 2,
1746 Arc::new(Int32Array::from(vec![None, Some(1)])),
1747 None,
1748 ));
1749 let target_type = DataType::FixedSizeList(target_field, 2);
1750
1751 let error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1752 .unwrap_err()
1753 .to_string();
1754 assert_contains!(error, "Found unmasked nulls for non-nullable");
1755 }
1756
1757 #[test]
1758 fn test_cast_sliced_fixed_size_list_struct_ignores_hidden_child_values() {
1759 let source_field =
1760 arc_field("item", struct_type(vec![field("a", DataType::Utf8)]));
1761 let target_field =
1762 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1763 let struct_arr = StructArray::from(vec![(
1764 arc_field("a", DataType::Utf8),
1765 Arc::new(StringArray::from(vec![
1766 "0", "0", "not_int", "also_bad", "1", "2",
1767 ])) as ArrayRef,
1768 )]);
1769 let source_col: ArrayRef = Arc::new(
1770 FixedSizeListArray::new(
1771 source_field,
1772 2,
1773 Arc::new(struct_arr),
1774 Some(NullBuffer::from(vec![true, false, true])),
1775 )
1776 .slice(1, 2),
1777 );
1778 let target_type = DataType::FixedSizeList(target_field, 2);
1779
1780 let result =
1781 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1782 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1783 assert!(result_list.is_null(0));
1784 assert!(result_list.is_valid(1));
1785 let a_col = get_column_as!(&struct_values, "a", Int32Array);
1786 assert!(a_col.is_null(0));
1787 assert!(a_col.is_null(1));
1788 assert_eq!(a_col.value(2), 1);
1789 assert_eq!(a_col.value(3), 2);
1790 }
1791
1792 #[test]
1793 fn test_requires_nested_struct_cast() {
1794 let s1 = struct_type(vec![field("a", DataType::Int32)]);
1795 let s2 = struct_type(vec![field("a", DataType::Int64)]);
1796
1797 assert!(requires_nested_struct_cast(&s1, &s2));
1798 assert!(requires_nested_struct_cast(
1799 &DataType::List(arc_field("item", s1.clone())),
1800 &DataType::List(arc_field("item", s2.clone())),
1801 ));
1802 assert!(requires_nested_struct_cast(
1803 &DataType::Dictionary(Box::new(DataType::Int32), Box::new(s1.clone())),
1804 &DataType::Dictionary(Box::new(DataType::Int32), Box::new(s2.clone())),
1805 ));
1806 assert!(requires_nested_struct_cast(
1807 &DataType::ListView(arc_field("item", s1.clone())),
1808 &DataType::ListView(arc_field("item", s2.clone())),
1809 ));
1810 assert!(requires_nested_struct_cast(
1811 &DataType::FixedSizeList(arc_field("item", s1), 2),
1812 &DataType::FixedSizeList(arc_field("item", s2), 2),
1813 ));
1814
1815 assert!(!requires_nested_struct_cast(
1817 &DataType::Int32,
1818 &DataType::Int64
1819 ));
1820 assert!(!requires_nested_struct_cast(
1821 &DataType::List(arc_field("item", DataType::Int32)),
1822 &DataType::List(arc_field("item", DataType::Int64)),
1823 ));
1824 assert!(!requires_nested_struct_cast(
1825 &DataType::FixedSizeList(arc_field("item", DataType::Int32), 2),
1826 &DataType::FixedSizeList(arc_field("item", DataType::Int64), 2),
1827 ));
1828 }
1829}
1830
1831pub fn adapt_batch_to_schema(
1839 batch: RecordBatch,
1840 target_schema: &SchemaRef,
1841) -> Result<RecordBatch> {
1842 if Arc::ptr_eq(batch.schema_ref(), target_schema)
1843 || batch.schema().as_ref() == target_schema.as_ref()
1844 {
1845 return Ok(batch);
1846 }
1847
1848 if batch.num_columns() != target_schema.fields().len() {
1849 return _plan_err!(
1850 "Batch schema does not conform to expected schema (column count mismatch). Expected: {target_schema}, got: {}",
1851 batch.schema()
1852 );
1853 }
1854
1855 let mut columns = Vec::with_capacity(batch.num_columns());
1856 let mut needs_column_adaptation = false;
1857 let cast_options = CastOptions::default();
1858
1859 for (target_field, col) in target_schema.fields().iter().zip(batch.columns()) {
1860 if target_field.data_type() != col.data_type() {
1861 if !target_field.data_type().contains(col.data_type()) {
1864 return _plan_err!(
1865 "Batch column '{}' with type {} cannot be adapted to expected type {}",
1866 target_field.name(),
1867 col.data_type(),
1868 target_field.data_type()
1869 );
1870 }
1871 needs_column_adaptation = true;
1872 let adapted_col = cast_column(col, target_field.data_type(), &cast_options)?;
1873 columns.push(adapted_col);
1874 } else {
1875 columns.push(Arc::clone(col));
1876 }
1877 }
1878
1879 if needs_column_adaptation {
1880 Ok(RecordBatch::try_new(Arc::clone(target_schema), columns)?)
1881 } else {
1882 Ok(RecordBatch::try_new(
1885 Arc::clone(target_schema),
1886 batch.columns().to_vec(),
1887 )?)
1888 }
1889}
1890
1891#[cfg(test)]
1892mod adapt_schema_tests {
1893 use super::*;
1894 use arrow::array::{Int32Array, StringArray};
1895 use arrow::datatypes::{Field, Fields, Schema};
1896
1897 #[test]
1898 fn test_adapt_batch_to_schema_identical() -> Result<()> {
1899 let schema = Arc::new(Schema::new(vec![
1900 Field::new("a", DataType::Int32, false),
1901 Field::new("b", DataType::Utf8, true),
1902 ]));
1903
1904 let a = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1905 let b = Arc::new(StringArray::from(vec![Some("x"), None, Some("z")])) as ArrayRef;
1906 let batch = RecordBatch::try_new(Arc::clone(&schema), vec![a, b])?;
1907
1908 let adapted = adapt_batch_to_schema(batch.clone(), &schema)?;
1909 assert_eq!(adapted, batch);
1910 Ok(())
1911 }
1912
1913 #[test]
1914 fn test_adapt_batch_to_schema_stricter_nested_struct() -> Result<()> {
1915 let declared_inner_fields = Fields::from(vec![
1917 Field::new("x", DataType::Int32, true),
1918 Field::new("y", DataType::Utf8, true),
1919 ]);
1920 let declared_schema = Arc::new(Schema::new(vec![Field::new(
1921 "a",
1922 Struct(declared_inner_fields),
1923 false,
1924 )]));
1925
1926 let runtime_inner_fields = Fields::from(vec![
1928 Field::new("x", DataType::Int32, false),
1929 Field::new("y", DataType::Utf8, false),
1930 ]);
1931 let runtime_schema = Arc::new(Schema::new(vec![Field::new(
1932 "a",
1933 Struct(runtime_inner_fields.clone()),
1934 false,
1935 )]));
1936
1937 let x = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1938 let y = Arc::new(StringArray::from(vec!["x", "y", "z"])) as ArrayRef;
1939 let struct_array =
1940 Arc::new(StructArray::new(runtime_inner_fields, vec![x, y], None))
1941 as ArrayRef;
1942 let batch = RecordBatch::try_new(runtime_schema, vec![struct_array])?;
1943
1944 let adapted = adapt_batch_to_schema(batch, &declared_schema)?;
1945 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
1946 assert_eq!(adapted.num_rows(), 3);
1947
1948 let Struct(fields) = adapted.column(0).data_type() else {
1950 panic!("expected struct");
1951 };
1952 assert!(fields[0].is_nullable());
1953 assert!(fields[1].is_nullable());
1954 Ok(())
1955 }
1956
1957 #[test]
1958 fn test_adapt_batch_to_schema_top_level_nullability_only() -> Result<()> {
1959 let declared_schema =
1961 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, true)]));
1962 let runtime_schema =
1963 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, false)]));
1964
1965 let a = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1966 let batch = RecordBatch::try_new(runtime_schema, vec![a])?;
1967
1968 let adapted = adapt_batch_to_schema(batch, &declared_schema)?;
1969 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
1970 assert!(adapted.schema().field(0).is_nullable());
1971 Ok(())
1972 }
1973
1974 #[test]
1975 fn test_adapt_batch_to_schema_null_into_non_nullable_rejected() {
1976 let declared_schema =
1978 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, false)]));
1979 let runtime_schema =
1980 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, true)]));
1981
1982 let a = Arc::new(Int32Array::from(vec![Some(1), None, Some(3)])) as ArrayRef;
1983 let batch = RecordBatch::try_new(runtime_schema, vec![a]).unwrap();
1984
1985 let result = adapt_batch_to_schema(batch, &declared_schema);
1987 assert!(result.is_err());
1988 }
1989
1990 #[test]
1991 fn test_adapt_batch_to_schema_incompatible_type_rejected() {
1992 let declared_schema =
1993 Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, true)]));
1994 let runtime_schema =
1995 Arc::new(Schema::new(vec![Field::new("a", DataType::Utf8, true)]));
1996
1997 let a = Arc::new(StringArray::from(vec!["1", "2"])) as ArrayRef;
1998 let batch = RecordBatch::try_new(runtime_schema, vec![a]).unwrap();
1999
2000 let result = adapt_batch_to_schema(batch, &declared_schema);
2001 assert!(result.is_err());
2002 }
2003
2004 fn test_two_field_union(nullable: bool) -> UnionFields {
2005 UnionFields::try_new(
2006 vec![0, 1],
2007 vec![
2008 Field::new("value", DataType::Int32, nullable),
2009 Field::new("str", DataType::Utf8, nullable),
2010 ],
2011 )
2012 .unwrap()
2013 }
2014
2015 #[test]
2016 fn test_adapt_batch_to_schema_stricter_sparse_union() -> Result<()> {
2017 use arrow::array::UnionArray;
2018 use arrow::buffer::ScalarBuffer;
2019 use arrow::datatypes::UnionMode;
2020
2021 let target_union_fields = test_two_field_union(true);
2022 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2023 "u",
2024 DataType::Union(target_union_fields, UnionMode::Sparse),
2025 false,
2026 )]));
2027
2028 let source_union_fields = test_two_field_union(false);
2029 let runtime_schema = Arc::new(Schema::new(vec![Field::new(
2030 "u",
2031 DataType::Union(source_union_fields.clone(), UnionMode::Sparse),
2032 false,
2033 )]));
2034
2035 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![10, 20, 30]));
2036 let str_array: ArrayRef =
2037 Arc::new(StringArray::from(vec!["hello", "world", "!"]));
2038 let type_ids = [0, 0, 1].into_iter().collect::<ScalarBuffer<i8>>();
2039 let source_union = UnionArray::try_new(
2040 source_union_fields,
2041 type_ids,
2042 None,
2043 vec![int_array, str_array],
2044 )?;
2045 let batch = RecordBatch::try_new(runtime_schema, vec![Arc::new(source_union)])?;
2046
2047 let adapted = adapt_batch_to_schema(batch, &declared_schema)?;
2048 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
2049
2050 let adapted_union = adapted
2051 .column(0)
2052 .as_any()
2053 .downcast_ref::<UnionArray>()
2054 .unwrap();
2055 let DataType::Union(fields, mode) = adapted_union.data_type() else {
2056 panic!("expected union");
2057 };
2058 assert_eq!(*mode, UnionMode::Sparse);
2059 assert!(fields.iter().all(|(_, f)| f.is_nullable()));
2060
2061 Ok(())
2062 }
2063
2064 #[test]
2065 fn test_adapt_batch_to_schema_stricter_dense_union() -> Result<()> {
2066 use arrow::array::UnionArray;
2067 use arrow::buffer::ScalarBuffer;
2068 use arrow::datatypes::UnionMode;
2069
2070 let target_union_fields = test_two_field_union(true);
2071 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2072 "u",
2073 DataType::Union(target_union_fields, UnionMode::Dense),
2074 false,
2075 )]));
2076
2077 let source_union_fields = test_two_field_union(false);
2078 let runtime_schema = Arc::new(Schema::new(vec![Field::new(
2079 "u",
2080 DataType::Union(source_union_fields.clone(), UnionMode::Dense),
2081 false,
2082 )]));
2083
2084 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![10, 30]));
2085 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["hello"]));
2086 let type_ids = [0, 1, 0].into_iter().collect::<ScalarBuffer<i8>>();
2087 let offsets = [0, 0, 1].into_iter().collect::<ScalarBuffer<i32>>();
2088 let source_union = UnionArray::try_new(
2089 source_union_fields,
2090 type_ids,
2091 Some(offsets),
2092 vec![int_array, str_array],
2093 )?;
2094 let batch = RecordBatch::try_new(runtime_schema, vec![Arc::new(source_union)])?;
2095
2096 let adapted = adapt_batch_to_schema(batch, &declared_schema)?;
2097 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
2098
2099 let adapted_union = adapted
2100 .column(0)
2101 .as_any()
2102 .downcast_ref::<UnionArray>()
2103 .unwrap();
2104 let DataType::Union(fields, mode) = adapted_union.data_type() else {
2105 panic!("expected union");
2106 };
2107 assert_eq!(*mode, UnionMode::Dense);
2108 assert!(fields.iter().all(|(_, f)| f.is_nullable()));
2109
2110 Ok(())
2111 }
2112
2113 #[test]
2114 fn test_adapt_batch_to_schema_union_reordered_and_non_contiguous_type_ids()
2115 -> Result<()> {
2116 use arrow::array::UnionArray;
2117 use arrow::buffer::ScalarBuffer;
2118 use arrow::datatypes::UnionMode;
2119
2120 let target_union_fields = UnionFields::try_new(
2121 vec![3, 1],
2122 vec![
2123 Field::new("str", DataType::Utf8, true),
2124 Field::new("int", DataType::Int32, true),
2125 ],
2126 )?;
2127 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2128 "u",
2129 DataType::Union(target_union_fields, UnionMode::Dense),
2130 false,
2131 )]));
2132
2133 let source_union_fields = UnionFields::try_new(
2134 vec![1, 3],
2135 vec![
2136 Field::new("int", DataType::Int32, false),
2137 Field::new("str", DataType::Utf8, false),
2138 ],
2139 )?;
2140 let source_schema = Arc::new(Schema::new(vec![Field::new(
2141 "u",
2142 DataType::Union(source_union_fields.clone(), UnionMode::Dense),
2143 false,
2144 )]));
2145
2146 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![10, 30]));
2147 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["b"]));
2148 let type_ids = [1, 3, 1].into_iter().collect::<ScalarBuffer<i8>>();
2149 let offsets = [0, 0, 1].into_iter().collect::<ScalarBuffer<i32>>();
2150 let source_union = UnionArray::try_new(
2151 source_union_fields,
2152 type_ids.clone(),
2153 Some(offsets.clone()),
2154 vec![int_array, str_array],
2155 )?;
2156
2157 let source_batch =
2158 RecordBatch::try_new(source_schema, vec![Arc::new(source_union)])?;
2159
2160 let adapted = adapt_batch_to_schema(source_batch, &declared_schema)?;
2161 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
2162 let adapted_union = adapted
2163 .column(0)
2164 .as_any()
2165 .downcast_ref::<UnionArray>()
2166 .unwrap();
2167 assert_eq!(
2168 adapted_union.data_type(),
2169 declared_schema.field(0).data_type()
2170 );
2171 assert_eq!(adapted_union.type_ids(), &type_ids);
2172 assert_eq!(adapted_union.offsets(), Some(&offsets));
2173
2174 let int_child = adapted_union
2176 .child(1)
2177 .as_any()
2178 .downcast_ref::<Int32Array>()
2179 .unwrap();
2180 let str_child = adapted_union
2181 .child(3)
2182 .as_any()
2183 .downcast_ref::<StringArray>()
2184 .unwrap();
2185
2186 assert_eq!(adapted_union.type_id(0), 1);
2188 assert_eq!(int_child.value(adapted_union.value_offset(0)), 10);
2189
2190 assert_eq!(adapted_union.type_id(1), 3);
2192 assert_eq!(str_child.value(adapted_union.value_offset(1)), "b");
2193
2194 assert_eq!(adapted_union.type_id(2), 1);
2196 assert_eq!(int_child.value(adapted_union.value_offset(2)), 30);
2197
2198 Ok(())
2199 }
2200
2201 #[test]
2202 fn test_adapt_batch_to_schema_union_nested_struct() -> Result<()> {
2203 use arrow::array::UnionArray;
2204 use arrow::buffer::ScalarBuffer;
2205 use arrow::datatypes::{UnionFields, UnionMode};
2206
2207 let target_struct_fields = vec![Field::new("x", DataType::Int32, true)];
2208 let target_union_fields = UnionFields::try_new(
2209 vec![0],
2210 vec![Field::new("s", Struct(target_struct_fields.into()), true)],
2211 )?;
2212 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2213 "u",
2214 DataType::Union(target_union_fields, UnionMode::Dense),
2215 false,
2216 )]));
2217
2218 let source_struct_fields = vec![Field::new("x", DataType::Int32, false)];
2219 let source_union_fields = UnionFields::try_new(
2220 vec![0],
2221 vec![Field::new("s", Struct(source_struct_fields.into()), false)],
2222 )?;
2223 let source_schema = Arc::new(Schema::new(vec![Field::new(
2224 "u",
2225 DataType::Union(source_union_fields.clone(), UnionMode::Dense),
2226 false,
2227 )]));
2228
2229 let struct_child: ArrayRef = Arc::new(StructArray::new(
2230 vec![Field::new("x", DataType::Int32, false)].into(),
2231 vec![Arc::new(Int32Array::from(vec![1, 2]))],
2232 None,
2233 ));
2234 let type_ids = [0, 0].into_iter().collect::<ScalarBuffer<i8>>();
2235 let offsets = [0, 1].into_iter().collect::<ScalarBuffer<i32>>();
2236 let source_union = UnionArray::try_new(
2237 source_union_fields,
2238 type_ids.clone(),
2239 Some(offsets.clone()),
2240 vec![struct_child],
2241 )?;
2242
2243 let source_batch =
2244 RecordBatch::try_new(source_schema, vec![Arc::new(source_union)])?;
2245
2246 let adapted = adapt_batch_to_schema(source_batch, &declared_schema)?;
2247 assert_eq!(adapted.schema().as_ref(), declared_schema.as_ref());
2248 let adapted_union = adapted
2249 .column(0)
2250 .as_any()
2251 .downcast_ref::<UnionArray>()
2252 .unwrap();
2253 let adapted_child = adapted_union.child(0);
2254 let struct_arr = adapted_child
2255 .as_any()
2256 .downcast_ref::<StructArray>()
2257 .unwrap();
2258 assert!(struct_arr.fields()[0].is_nullable());
2259 Ok(())
2260 }
2261
2262 #[test]
2263 fn test_adapt_batch_to_schema_union_incompatible_mode_rejected() {
2264 use arrow::array::UnionArray;
2265 use arrow::buffer::ScalarBuffer;
2266 use arrow::datatypes::UnionMode;
2267
2268 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2269 "u",
2270 DataType::Union(test_two_field_union(true), UnionMode::Dense),
2271 false,
2272 )]));
2273 let source_schema = Arc::new(Schema::new(vec![Field::new(
2274 "u",
2275 DataType::Union(test_two_field_union(false), UnionMode::Sparse),
2276 false,
2277 )]));
2278
2279 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![10, 20]));
2280 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b"]));
2281 let type_ids = [0, 0].into_iter().collect::<ScalarBuffer<i8>>();
2282 let source_union = UnionArray::try_new(
2283 test_two_field_union(false),
2284 type_ids,
2285 None,
2286 vec![int_array, str_array],
2287 )
2288 .unwrap();
2289
2290 let source_batch =
2291 RecordBatch::try_new(source_schema, vec![Arc::new(source_union)]).unwrap();
2292
2293 let res = adapt_batch_to_schema(source_batch, &declared_schema);
2294 assert!(res.is_err());
2295 }
2296
2297 #[test]
2298 fn test_adapt_batch_to_schema_union_field_set_mismatch_rejected() {
2299 use arrow::array::UnionArray;
2300 use arrow::buffer::ScalarBuffer;
2301 use arrow::datatypes::{UnionFields, UnionMode};
2302
2303 let target_union_fields = UnionFields::try_new(
2305 vec![0],
2306 vec![Field::new("value", DataType::Int32, true)],
2307 )
2308 .unwrap();
2309 let declared_schema = Arc::new(Schema::new(vec![Field::new(
2310 "u",
2311 DataType::Union(target_union_fields, UnionMode::Sparse),
2312 false,
2313 )]));
2314
2315 let source_union_fields = UnionFields::try_new(
2317 vec![0, 1],
2318 vec![
2319 Field::new("value", DataType::Int32, false),
2320 Field::new("extra", DataType::Utf8, false),
2321 ],
2322 )
2323 .unwrap();
2324 let source_schema = Arc::new(Schema::new(vec![Field::new(
2325 "u",
2326 DataType::Union(source_union_fields.clone(), UnionMode::Sparse),
2327 false,
2328 )]));
2329
2330 let int_array: ArrayRef = Arc::new(Int32Array::from(vec![10, 20]));
2331 let str_array: ArrayRef = Arc::new(StringArray::from(vec!["a", "b"]));
2332 let type_ids = [0, 1].into_iter().collect::<ScalarBuffer<i8>>();
2333 let source_union = UnionArray::try_new(
2334 source_union_fields,
2335 type_ids,
2336 None,
2337 vec![int_array, str_array],
2338 )
2339 .unwrap();
2340
2341 let source_batch =
2342 RecordBatch::try_new(source_schema, vec![Arc::new(source_union)]).unwrap();
2343
2344 let res = adapt_batch_to_schema(source_batch, &declared_schema);
2345 assert!(res.is_err());
2346 let err = res.unwrap_err().to_string();
2347 assert!(
2348 err.contains("different field sets")
2349 || err.contains("cannot be adapted to expected type"),
2350 "unexpected error message: {err}"
2351 );
2352 }
2353
2354 #[test]
2355 fn test_validate_data_type_compatibility_union() {
2356 use arrow::datatypes::{UnionFields, UnionMode};
2357
2358 let target_type = DataType::Union(test_two_field_union(true), UnionMode::Dense);
2359
2360 let reordered_source_fields = UnionFields::try_new(
2362 vec![1, 0],
2363 vec![
2364 Field::new("str", DataType::Utf8, false),
2365 Field::new("value", DataType::Int32, false),
2366 ],
2367 )
2368 .unwrap();
2369 let source_type = DataType::Union(reordered_source_fields, UnionMode::Dense);
2370 assert!(
2371 validate_data_type_compatibility("u", &source_type, &target_type).is_ok()
2372 );
2373
2374 let sparse_source_type =
2376 DataType::Union(test_two_field_union(false), UnionMode::Sparse);
2377 assert!(
2378 validate_data_type_compatibility("u", &sparse_source_type, &target_type)
2379 .is_err()
2380 );
2381
2382 let extra_id_source = DataType::Union(
2384 UnionFields::try_new(
2385 vec![0, 1, 2],
2386 vec![
2387 Field::new("value", DataType::Int32, false),
2388 Field::new("str", DataType::Utf8, false),
2389 Field::new("extra", DataType::Int32, false),
2390 ],
2391 )
2392 .unwrap(),
2393 UnionMode::Dense,
2394 );
2395 assert!(
2396 validate_data_type_compatibility("u", &extra_id_source, &target_type)
2397 .is_err()
2398 );
2399
2400 let missing_id_source = DataType::Union(
2402 UnionFields::try_new(
2403 vec![0],
2404 vec![Field::new("value", DataType::Int32, false)],
2405 )
2406 .unwrap(),
2407 UnionMode::Dense,
2408 );
2409 assert!(
2410 validate_data_type_compatibility("u", &missing_id_source, &target_type)
2411 .is_err()
2412 );
2413 }
2414}