1use crate::error::{_plan_err, Result};
19use arrow::{
20 array::{
21 Array, ArrayRef, AsArray, DictionaryArray, FixedSizeListArray, GenericListArray,
22 GenericListViewArray, StructArray, downcast_integer, make_array, new_null_array,
23 },
24 buffer::NullBuffer,
25 compute::{CastOptions, can_cast_types, cast_with_options},
26 datatypes::{DataType, DataType::Struct, Field, FieldRef},
27};
28use std::{collections::HashSet, sync::Arc};
29
30fn cast_struct_column(
58 source_col: &ArrayRef,
59 target_fields: &[Arc<Field>],
60 cast_options: &CastOptions,
61) -> Result<ArrayRef> {
62 if source_col.data_type() == &DataType::Null {
63 return Ok(new_null_array(
64 &Struct(target_fields.to_vec().into()),
65 source_col.len(),
66 ));
67 }
68
69 if let Some(source_struct) = source_col.as_any().downcast_ref::<StructArray>() {
70 let source_fields = source_struct.fields();
71 validate_struct_compatibility(source_fields, target_fields)?;
72
73 if !source_col.is_empty() && source_col.null_count() == source_col.len() {
74 return Ok(new_null_array(
75 &Struct(target_fields.to_vec().into()),
76 source_col.len(),
77 ));
78 }
79
80 let mut fields: Vec<Arc<Field>> = Vec::with_capacity(target_fields.len());
81 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(target_fields.len());
82 let num_rows = source_col.len();
83
84 for target_child_field in target_fields.iter() {
86 fields.push(Arc::clone(target_child_field));
87
88 let source_child_opt =
89 source_struct.column_by_name(target_child_field.name());
90
91 match source_child_opt {
92 Some(source_child_col) => {
93 let adapted_child = cast_column(
94 source_child_col,
95 target_child_field.data_type(),
96 cast_options,
97 )
98 .map_err(|e| {
99 e.context(format!(
100 "While casting struct field '{}'",
101 target_child_field.name()
102 ))
103 })?;
104 arrays.push(adapted_child);
105 }
106 None => {
107 arrays.push(new_null_array(target_child_field.data_type(), num_rows));
108 }
109 }
110 }
111
112 let struct_array =
113 StructArray::new(fields.into(), arrays, source_struct.nulls().cloned());
114 Ok(Arc::new(struct_array))
115 } else {
116 _plan_err!(
118 "Cannot cast column of type {} to struct type. Source must be a struct to cast to struct.",
119 source_col.data_type()
120 )
121 }
122}
123
124pub fn cast_column(
179 source_col: &ArrayRef,
180 target_type: &DataType,
181 cast_options: &CastOptions,
182) -> Result<ArrayRef> {
183 match (source_col.data_type(), target_type) {
184 (_, Struct(target_fields)) => {
185 cast_struct_column(source_col, target_fields, cast_options)
186 }
187 (DataType::List(_), DataType::List(target_inner)) => {
188 cast_list_column::<i32>(source_col, target_inner, cast_options)
189 }
190 (DataType::LargeList(_), DataType::LargeList(target_inner)) => {
191 cast_list_column::<i64>(source_col, target_inner, cast_options)
192 }
193 (
194 DataType::FixedSizeList(_, source_list_size),
195 DataType::FixedSizeList(target_inner, target_list_size),
196 ) if source_list_size == target_list_size => cast_fixed_size_list_column(
197 source_col,
198 target_inner,
199 *target_list_size,
200 cast_options,
201 ),
202 (DataType::ListView(_), DataType::ListView(target_inner)) => {
203 cast_list_view_column::<i32>(source_col, target_inner, cast_options)
204 }
205 (DataType::LargeListView(_), DataType::LargeListView(target_inner)) => {
206 cast_list_view_column::<i64>(source_col, target_inner, cast_options)
207 }
208 (
209 DataType::Dictionary(source_key_type, _),
210 DataType::Dictionary(target_key_type, target_value_type),
211 ) => cast_dictionary_column(
212 source_col,
213 source_key_type,
214 target_key_type,
215 target_value_type,
216 cast_options,
217 ),
218 _ => Ok(cast_with_options(source_col, target_type, cast_options)?),
219 }
220}
221
222fn cast_list_column<O: arrow::array::OffsetSizeTrait>(
223 source_col: &ArrayRef,
224 target_inner_field: &FieldRef,
225 cast_options: &CastOptions,
226) -> Result<ArrayRef> {
227 let source_list = source_col.as_list::<O>();
228
229 let cast_values = cast_column(
230 source_list.values(),
231 target_inner_field.data_type(),
232 cast_options,
233 )?;
234
235 let result = GenericListArray::<O>::new(
236 Arc::clone(target_inner_field),
237 source_list.offsets().clone(),
238 cast_values,
239 source_list.nulls().cloned(),
240 );
241 Ok(Arc::new(result))
242}
243
244fn cast_list_view_column<O: arrow::array::OffsetSizeTrait>(
245 source_col: &ArrayRef,
246 target_inner_field: &FieldRef,
247 cast_options: &CastOptions,
248) -> Result<ArrayRef> {
249 let source_list = source_col.as_list_view::<O>();
250
251 let cast_values = cast_column(
252 source_list.values(),
253 target_inner_field.data_type(),
254 cast_options,
255 )?;
256
257 let result = GenericListViewArray::<O>::try_new(
258 Arc::clone(target_inner_field),
259 source_list.offsets().clone(),
260 source_list.sizes().clone(),
261 cast_values,
262 source_list.nulls().cloned(),
263 )?;
264 Ok(Arc::new(result))
265}
266
267fn cast_fixed_size_list_column(
268 source_col: &ArrayRef,
269 target_inner_field: &FieldRef,
270 target_list_size: i32,
271 cast_options: &CastOptions,
272) -> Result<ArrayRef> {
273 let source_list = source_col.as_fixed_size_list();
274
275 let source_values = source_list.values();
276 let target_type = target_inner_field.data_type();
277
278 let cast_values = match cast_column(source_values, target_type, cast_options) {
279 Ok(cast_values) => cast_values,
280 Err(error) => match cast_fixed_size_list_values_with_parent_nulls(
281 source_values,
282 target_type,
283 cast_options,
284 source_list.nulls(),
285 target_list_size,
286 ) {
287 Some(masked_cast) => masked_cast?,
288 None => return Err(error),
289 },
290 };
291
292 Ok(Arc::new(FixedSizeListArray::try_new(
293 Arc::clone(target_inner_field),
294 target_list_size,
295 cast_values,
296 source_list.nulls().cloned(),
297 )?))
298}
299
300fn cast_fixed_size_list_values_with_parent_nulls(
301 source_values: &ArrayRef,
302 target_type: &DataType,
303 cast_options: &CastOptions,
304 parent_nulls: Option<&NullBuffer>,
305 list_size: i32,
306) -> Option<Result<ArrayRef>> {
307 let parent_nulls = parent_nulls.filter(|nulls| nulls.null_count() > 0)?;
308
309 let hidden_child_nulls = parent_nulls.expand(list_size as usize);
312 let masked_values = mask_array_values(source_values, &hidden_child_nulls);
313 Some(masked_values.and_then(|values| cast_column(&values, target_type, cast_options)))
314}
315
316fn mask_array_values(
317 values: &ArrayRef,
318 additional_nulls: &NullBuffer,
319) -> Result<ArrayRef> {
320 let nulls = NullBuffer::union(values.nulls(), Some(additional_nulls));
321
322 if let Some(struct_array) = values.as_any().downcast_ref::<StructArray>() {
323 let struct_nulls = nulls
324 .as_ref()
325 .expect("additional nulls always produce nulls");
326 let arrays = struct_array
327 .columns()
328 .iter()
329 .map(|child| mask_array_values(child, struct_nulls))
330 .collect::<Result<Vec<_>>>()?;
331 return Ok(Arc::new(StructArray::new(
332 struct_array.fields().clone(),
333 arrays,
334 nulls,
335 )));
336 }
337
338 Ok(make_array(
339 values.to_data().into_builder().nulls(nulls).build()?,
340 ))
341}
342
343fn cast_dictionary_column(
344 source_col: &ArrayRef,
345 source_key_type: &DataType,
346 target_key_type: &DataType,
347 target_value_type: &DataType,
348 cast_options: &CastOptions,
349) -> Result<ArrayRef> {
350 macro_rules! cast_dict_values {
353 ($t:ty) => {{
354 let source_dict = source_col
355 .as_any()
356 .downcast_ref::<DictionaryArray<$t>>()
357 .expect("downcast must succeed");
358 let cast_values =
359 cast_column(source_dict.values(), target_value_type, cast_options)?;
360 Ok(Arc::new(DictionaryArray::<$t>::new(
361 source_dict.keys().clone(),
362 cast_values,
363 )) as ArrayRef)
364 }};
365 }
366
367 let result: Result<ArrayRef> = downcast_integer! {
368 source_key_type => (cast_dict_values),
369 k => _plan_err!("Unsupported dictionary key type: {k}")
370 };
371 let result = result?;
372
373 if source_key_type != target_key_type {
375 let target_dict_type = DataType::Dictionary(
376 Box::new(target_key_type.clone()),
377 Box::new(target_value_type.clone()),
378 );
379 Ok(cast_with_options(&result, &target_dict_type, cast_options)?)
380 } else {
381 Ok(result)
382 }
383}
384
385pub fn validate_struct_compatibility(
422 source_fields: &[FieldRef],
423 target_fields: &[FieldRef],
424) -> Result<()> {
425 let has_overlap = has_one_of_more_common_fields(source_fields, target_fields);
426 if !has_overlap {
427 return _plan_err!(
428 "Cannot cast struct with {} fields to {} fields because there is no field name overlap",
429 source_fields.len(),
430 target_fields.len()
431 );
432 }
433
434 for target_field in target_fields {
436 if let Some(source_field) = source_fields
438 .iter()
439 .find(|f| f.name() == target_field.name())
440 {
441 validate_field_compatibility(source_field, target_field)?;
442 } else {
443 if !target_field.is_nullable() {
446 return _plan_err!(
447 "Cannot cast struct: target field '{}' is non-nullable but missing from source. \
448 Cannot fill with NULL.",
449 target_field.name()
450 );
451 }
452 }
453 }
454
455 Ok(())
457}
458
459fn validate_field_compatibility(
460 source_field: &Field,
461 target_field: &Field,
462) -> Result<()> {
463 if source_field.data_type() == &DataType::Null {
464 if !target_field.is_nullable() {
467 return _plan_err!(
468 "Cannot cast NULL struct field '{}' to non-nullable field '{}'",
469 source_field.name(),
470 target_field.name()
471 );
472 }
473 return Ok(());
474 }
475
476 if source_field.is_nullable() && !target_field.is_nullable() {
480 return _plan_err!(
481 "Cannot cast nullable struct field '{}' to non-nullable field",
482 target_field.name()
483 );
484 }
485
486 validate_data_type_compatibility(
487 target_field.name(),
488 source_field.data_type(),
489 target_field.data_type(),
490 )
491}
492
493pub fn validate_data_type_compatibility(
496 field_name: &str,
497 source_type: &DataType,
498 target_type: &DataType,
499) -> Result<()> {
500 match (source_type, target_type) {
501 (Struct(source_nested), Struct(target_nested)) => {
502 validate_struct_compatibility(source_nested, target_nested)?;
503 }
504 (
505 DataType::FixedSizeList(s, source_list_size),
506 DataType::FixedSizeList(t, target_list_size),
507 ) if source_list_size == target_list_size => {
508 validate_field_compatibility(s, t)?;
509 }
510 (DataType::List(s), DataType::List(t))
511 | (DataType::LargeList(s), DataType::LargeList(t))
512 | (DataType::ListView(s), DataType::ListView(t))
513 | (DataType::LargeListView(s), DataType::LargeListView(t)) => {
514 validate_field_compatibility(s, t)?;
515 }
516 (DataType::Dictionary(s_key, s_val), DataType::Dictionary(t_key, t_val)) => {
517 if !can_cast_types(s_key, t_key) {
518 return _plan_err!(
519 "Cannot cast dictionary key type {} to {} for field '{}'",
520 s_key,
521 t_key,
522 field_name
523 );
524 }
525 validate_data_type_compatibility(field_name, s_val, t_val)?;
526 }
527 _ => {
528 if !can_cast_types(source_type, target_type) {
529 return _plan_err!(
530 "Cannot cast struct field '{}' from type {} to type {}",
531 field_name,
532 source_type,
533 target_type
534 );
535 }
536 }
537 }
538 Ok(())
539}
540
541pub fn requires_nested_struct_cast(
552 source_type: &DataType,
553 target_type: &DataType,
554) -> bool {
555 match (source_type, target_type) {
556 (Struct(_), Struct(_)) => true,
557 (
558 DataType::FixedSizeList(s, source_list_size),
559 DataType::FixedSizeList(t, target_list_size),
560 ) if source_list_size == target_list_size => {
561 requires_nested_struct_cast(s.data_type(), t.data_type())
562 }
563 (DataType::List(s), DataType::List(t))
564 | (DataType::LargeList(s), DataType::LargeList(t))
565 | (DataType::ListView(s), DataType::ListView(t))
566 | (DataType::LargeListView(s), DataType::LargeListView(t)) => {
567 requires_nested_struct_cast(s.data_type(), t.data_type())
568 }
569 (DataType::Dictionary(_, s_val), DataType::Dictionary(_, t_val)) => {
570 requires_nested_struct_cast(s_val, t_val)
571 }
572 _ => false,
573 }
574}
575
576pub fn has_one_of_more_common_fields(
581 source_fields: &[FieldRef],
582 target_fields: &[FieldRef],
583) -> bool {
584 let source_names: HashSet<&str> = source_fields
585 .iter()
586 .map(|field| field.name().as_str())
587 .collect();
588 target_fields
589 .iter()
590 .any(|field| source_names.contains(field.name().as_str()))
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::{assert_contains, format::DEFAULT_CAST_OPTIONS};
597 use arrow::{
598 array::{
599 BinaryArray, FixedSizeListArray, Int32Array, Int32Builder, Int64Array,
600 ListArray, ListViewArray, MapArray, MapBuilder, NullArray, StringArray,
601 StringBuilder,
602 },
603 buffer::{NullBuffer, ScalarBuffer},
604 datatypes::{DataType, Field, FieldRef, Int32Type},
605 };
606 macro_rules! get_column_as {
608 ($struct_array:expr, $column_name:expr, $array_type:ty) => {
609 $struct_array
610 .column_by_name($column_name)
611 .unwrap()
612 .as_any()
613 .downcast_ref::<$array_type>()
614 .unwrap()
615 };
616 }
617
618 fn field(name: &str, data_type: DataType) -> Field {
619 Field::new(name, data_type, true)
620 }
621
622 fn non_null_field(name: &str, data_type: DataType) -> Field {
623 Field::new(name, data_type, false)
624 }
625
626 fn arc_field(name: &str, data_type: DataType) -> FieldRef {
627 Arc::new(field(name, data_type))
628 }
629
630 fn struct_type(fields: Vec<Field>) -> DataType {
631 Struct(fields.into())
632 }
633
634 fn struct_field(name: &str, fields: Vec<Field>) -> Field {
635 field(name, struct_type(fields))
636 }
637
638 fn arc_struct_field(name: &str, fields: Vec<Field>) -> FieldRef {
639 Arc::new(struct_field(name, fields))
640 }
641
642 #[test]
643 fn test_cast_simple_column() {
644 let source = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
645 let target_field = field("ints", DataType::Int64);
646 let result =
647 cast_column(&source, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
648 .unwrap();
649 let result = result.as_any().downcast_ref::<Int64Array>().unwrap();
650 assert_eq!(result.len(), 3);
651 assert_eq!(result.value(0), 1);
652 assert_eq!(result.value(1), 2);
653 assert_eq!(result.value(2), 3);
654 }
655
656 #[test]
657 fn test_cast_column_with_options() {
658 let source = Arc::new(Int64Array::from(vec![1, i64::MAX])) as ArrayRef;
659 let target_field = field("ints", DataType::Int32);
660
661 let safe_opts = CastOptions {
662 safe: false,
664 ..DEFAULT_CAST_OPTIONS
665 };
666 assert!(cast_column(&source, target_field.data_type(), &safe_opts).is_err());
667
668 let unsafe_opts = CastOptions {
669 safe: true,
671 ..DEFAULT_CAST_OPTIONS
672 };
673 let result =
674 cast_column(&source, target_field.data_type(), &unsafe_opts).unwrap();
675 let result = result.as_any().downcast_ref::<Int32Array>().unwrap();
676 assert_eq!(result.value(0), 1);
677 assert!(result.is_null(1));
678 }
679
680 #[test]
681 fn test_cast_struct_with_missing_field() {
682 let a_array = Arc::new(Int32Array::from(vec![1, 2])) as ArrayRef;
683 let source_struct = StructArray::from(vec![(
684 arc_field("a", DataType::Int32),
685 Arc::clone(&a_array),
686 )]);
687 let source_col = Arc::new(source_struct) as ArrayRef;
688
689 let target_field = struct_field(
690 "s",
691 vec![field("a", DataType::Int32), field("b", DataType::Utf8)],
692 );
693
694 let result =
695 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
696 .unwrap();
697 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
698 assert_eq!(struct_array.fields().len(), 2);
699 let a_result = get_column_as!(&struct_array, "a", Int32Array);
700 assert_eq!(a_result.value(0), 1);
701 assert_eq!(a_result.value(1), 2);
702
703 let b_result = get_column_as!(&struct_array, "b", StringArray);
704 assert_eq!(b_result.len(), 2);
705 assert!(b_result.is_null(0));
706 assert!(b_result.is_null(1));
707 }
708
709 #[test]
710 fn test_cast_struct_source_not_struct() {
711 let source = Arc::new(Int32Array::from(vec![10, 20])) as ArrayRef;
712 let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
713
714 let result =
715 cast_column(&source, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
716 assert!(result.is_err());
717 let error_msg = result.unwrap_err().to_string();
718 assert!(error_msg.contains("Cannot cast column of type"));
719 assert!(error_msg.contains("to struct type"));
720 assert!(error_msg.contains("Source must be a struct"));
721 }
722
723 #[test]
724 fn test_cast_struct_incompatible_child_type() {
725 let a_array = Arc::new(BinaryArray::from(vec![
726 Some(b"a".as_ref()),
727 Some(b"b".as_ref()),
728 ])) as ArrayRef;
729 let source_struct =
730 StructArray::from(vec![(arc_field("a", DataType::Binary), a_array)]);
731 let source_col = Arc::new(source_struct) as ArrayRef;
732
733 let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
734
735 let result =
736 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
737 assert!(result.is_err());
738 let error_msg = result.unwrap_err().to_string();
739 assert!(error_msg.contains("Cannot cast struct field 'a'"));
740 }
741
742 #[test]
743 fn test_validate_struct_compatibility_incompatible_types() {
744 let source_fields = vec![
746 arc_field("field1", DataType::Binary),
747 arc_field("field2", DataType::Utf8),
748 ];
749
750 let target_fields = vec![arc_field("field1", DataType::Int32)];
752
753 let result = validate_struct_compatibility(&source_fields, &target_fields);
754 assert!(result.is_err());
755 let error_msg = result.unwrap_err().to_string();
756 assert!(error_msg.contains("Cannot cast struct field 'field1'"));
757 assert!(error_msg.contains("Binary"));
758 assert!(error_msg.contains("Int32"));
759 }
760
761 #[test]
762 fn test_validate_struct_compatibility_compatible_types() {
763 let source_fields = vec![
765 arc_field("field1", DataType::Int32),
766 arc_field("field2", DataType::Utf8),
767 ];
768
769 let target_fields = vec![arc_field("field1", DataType::Int64)];
771
772 let result = validate_struct_compatibility(&source_fields, &target_fields);
773 assert!(result.is_ok());
774 }
775
776 #[test]
777 fn test_validate_struct_compatibility_missing_field_in_source() {
778 let source_fields = vec![arc_field("field1", DataType::Int32)];
780
781 let target_fields = vec![
783 arc_field("field1", DataType::Int32),
784 arc_field("field2", DataType::Utf8),
785 ];
786
787 let result = validate_struct_compatibility(&source_fields, &target_fields);
789 assert!(result.is_ok());
790 }
791
792 #[test]
793 fn test_validate_struct_compatibility_additional_field_in_source() {
794 let source_fields = vec![
796 arc_field("field1", DataType::Int32),
797 arc_field("field2", DataType::Utf8),
798 ];
799
800 let target_fields = vec![arc_field("field1", DataType::Int32)];
802
803 let result = validate_struct_compatibility(&source_fields, &target_fields);
805 assert!(result.is_ok());
806 }
807
808 #[test]
809 fn test_validate_struct_compatibility_no_overlap_mismatch_len() {
810 let source_fields = vec![
811 arc_field("left", DataType::Int32),
812 arc_field("right", DataType::Int32),
813 ];
814 let target_fields = vec![arc_field("alpha", DataType::Int32)];
815
816 let result = validate_struct_compatibility(&source_fields, &target_fields);
817 assert!(result.is_err());
818 let error_msg = result.unwrap_err().to_string();
819 assert_contains!(error_msg, "no field name overlap");
820 }
821
822 #[test]
823 fn test_cast_struct_parent_nulls_retained() {
824 let a_array = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
825 let fields = vec![arc_field("a", DataType::Int32)];
826 let nulls = Some(NullBuffer::from(vec![true, false]));
827 let source_struct = StructArray::new(fields.clone().into(), vec![a_array], nulls);
828 let source_col = Arc::new(source_struct) as ArrayRef;
829
830 let target_field = struct_field("s", vec![field("a", DataType::Int64)]);
831
832 let result =
833 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
834 .unwrap();
835 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
836 assert_eq!(struct_array.null_count(), 1);
837 assert!(struct_array.is_valid(0));
838 assert!(struct_array.is_null(1));
839
840 let a_result = get_column_as!(&struct_array, "a", Int64Array);
841 assert_eq!(a_result.value(0), 1);
842 assert_eq!(a_result.value(1), 2);
843 }
844
845 #[test]
846 fn test_validate_struct_compatibility_nullable_to_non_nullable() {
847 let source_fields = vec![arc_field("field1", DataType::Int32)];
849
850 let target_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
852
853 let result = validate_struct_compatibility(&source_fields, &target_fields);
854 assert!(result.is_err());
855 let error_msg = result.unwrap_err().to_string();
856 assert!(error_msg.contains("field1"));
857 assert!(error_msg.contains("non-nullable"));
858 }
859
860 #[test]
861 fn test_validate_struct_compatibility_non_nullable_to_nullable() {
862 let source_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
864
865 let target_fields = vec![arc_field("field1", DataType::Int32)];
867
868 let result = validate_struct_compatibility(&source_fields, &target_fields);
869 assert!(result.is_ok());
870 }
871
872 #[test]
873 fn test_validate_struct_compatibility_nested_nullable_to_non_nullable() {
874 let source_fields = vec![Arc::new(non_null_field(
876 "field1",
877 struct_type(vec![field("nested", DataType::Int32)]),
878 ))];
879
880 let target_fields = vec![Arc::new(non_null_field(
882 "field1",
883 struct_type(vec![non_null_field("nested", DataType::Int32)]),
884 ))];
885
886 let result = validate_struct_compatibility(&source_fields, &target_fields);
887 assert!(result.is_err());
888 let error_msg = result.unwrap_err().to_string();
889 assert!(error_msg.contains("nested"));
890 assert!(error_msg.contains("non-nullable"));
891 }
892
893 #[test]
894 fn test_validate_struct_compatibility_by_name() {
895 let source_fields = vec![
897 arc_field("field1", DataType::Int32),
898 arc_field("field2", DataType::Utf8),
899 ];
900
901 let target_fields = vec![
903 arc_field("field2", DataType::Utf8),
904 arc_field("field1", DataType::Int64),
905 ];
906
907 let result = validate_struct_compatibility(&source_fields, &target_fields);
908 assert!(result.is_ok());
909 }
910
911 #[test]
912 fn test_validate_struct_compatibility_by_name_with_type_mismatch() {
913 let source_fields = vec![arc_field("field1", DataType::Binary)];
915
916 let target_fields = vec![arc_field("field1", DataType::Int32)];
918
919 let result = validate_struct_compatibility(&source_fields, &target_fields);
920 assert!(result.is_err());
921 let error_msg = result.unwrap_err().to_string();
922 assert_contains!(
923 error_msg,
924 "Cannot cast struct field 'field1' from type Binary to type Int32"
925 );
926 }
927
928 #[test]
929 fn test_validate_struct_compatibility_no_overlap_equal_len() {
930 let source_fields = vec![
931 arc_field("left", DataType::Int32),
932 arc_field("right", DataType::Utf8),
933 ];
934
935 let target_fields = vec![
936 arc_field("alpha", DataType::Int32),
937 arc_field("beta", DataType::Utf8),
938 ];
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_validate_struct_compatibility_mixed_name_overlap() {
948 let source_fields = vec![
950 arc_field("a", DataType::Int32),
951 arc_field("b", DataType::Utf8),
952 arc_field("extra", DataType::Boolean),
953 ];
954
955 let target_fields = vec![
958 arc_field("b", DataType::Utf8),
959 arc_field("a", DataType::Int64),
960 arc_field("c", DataType::Float32),
961 ];
962
963 let result = validate_struct_compatibility(&source_fields, &target_fields);
964 assert!(result.is_ok());
965 }
966
967 #[test]
968 fn test_validate_struct_compatibility_by_name_missing_required_field() {
969 let source_fields = vec![arc_field("field1", DataType::Int32)];
971
972 let target_fields = vec![
974 arc_field("field1", DataType::Int32),
975 Arc::new(non_null_field("field2", DataType::Int32)),
976 ];
977
978 let result = validate_struct_compatibility(&source_fields, &target_fields);
979 assert!(result.is_err());
980 let error_msg = result.unwrap_err().to_string();
981 assert_contains!(
982 error_msg,
983 "Cannot cast struct: target field 'field2' is non-nullable but missing from source. Cannot fill with NULL."
984 );
985 }
986
987 #[test]
988 fn test_validate_struct_compatibility_partial_name_overlap_with_count_mismatch() {
989 let source_fields = vec![arc_field("a", DataType::Int32)];
991
992 let target_fields = vec![
994 arc_field("a", DataType::Int32),
995 arc_field("b", DataType::Utf8),
996 ];
997
998 let result = validate_struct_compatibility(&source_fields, &target_fields);
1001 assert!(result.is_ok());
1002 }
1003
1004 #[test]
1005 fn test_cast_nested_struct_with_extra_and_missing_fields() {
1006 let a = Arc::new(Int32Array::from(vec![Some(1), None])) as ArrayRef;
1008 let b = Arc::new(Int32Array::from(vec![Some(2), Some(3)])) as ArrayRef;
1009 let extra = Arc::new(Int32Array::from(vec![Some(9), Some(10)])) as ArrayRef;
1010
1011 let inner = StructArray::from(vec![
1012 (arc_field("a", DataType::Int32), a),
1013 (arc_field("b", DataType::Int32), b),
1014 (arc_field("extra", DataType::Int32), extra),
1015 ]);
1016
1017 let source_struct = StructArray::from(vec![(
1018 arc_struct_field(
1019 "inner",
1020 vec![
1021 field("a", DataType::Int32),
1022 field("b", DataType::Int32),
1023 field("extra", DataType::Int32),
1024 ],
1025 ),
1026 Arc::new(inner) as ArrayRef,
1027 )]);
1028 let source_col = Arc::new(source_struct) as ArrayRef;
1029
1030 let target_field = struct_field(
1032 "outer",
1033 vec![struct_field(
1034 "inner",
1035 vec![
1036 field("b", DataType::Int64),
1037 field("a", DataType::Int32),
1038 field("missing", DataType::Int32),
1039 ],
1040 )],
1041 );
1042
1043 let result =
1044 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1045 .unwrap();
1046 let outer = result.as_any().downcast_ref::<StructArray>().unwrap();
1047 let inner = get_column_as!(&outer, "inner", StructArray);
1048 assert_eq!(inner.fields().len(), 3);
1049
1050 let b = get_column_as!(inner, "b", Int64Array);
1051 assert_eq!(b.value(0), 2);
1052 assert_eq!(b.value(1), 3);
1053 assert!(!b.is_null(0));
1054 assert!(!b.is_null(1));
1055
1056 let a = get_column_as!(inner, "a", Int32Array);
1057 assert_eq!(a.value(0), 1);
1058 assert!(a.is_null(1));
1059
1060 let missing = get_column_as!(inner, "missing", Int32Array);
1061 assert!(missing.is_null(0));
1062 assert!(missing.is_null(1));
1063 }
1064
1065 #[test]
1066 fn test_cast_null_struct_field_to_nested_struct() {
1067 let null_inner = Arc::new(NullArray::new(2)) as ArrayRef;
1068 let source_struct = StructArray::from(vec![(
1069 arc_field("inner", DataType::Null),
1070 Arc::clone(&null_inner),
1071 )]);
1072 let source_col = Arc::new(source_struct) as ArrayRef;
1073
1074 let target_field = struct_field(
1075 "outer",
1076 vec![struct_field("inner", vec![field("a", DataType::Int32)])],
1077 );
1078
1079 let result =
1080 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1081 .unwrap();
1082 let outer = result.as_any().downcast_ref::<StructArray>().unwrap();
1083 let inner = get_column_as!(&outer, "inner", StructArray);
1084 assert_eq!(inner.len(), 2);
1085 assert!(inner.is_null(0));
1086 assert!(inner.is_null(1));
1087
1088 let inner_a = get_column_as!(inner, "a", Int32Array);
1089 assert!(inner_a.is_null(0));
1090 assert!(inner_a.is_null(1));
1091 }
1092
1093 #[test]
1094 fn test_cast_struct_with_array_and_map_fields() {
1095 let arr_array = Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
1097 Some(vec![Some(1), Some(2)]),
1098 None,
1099 ])) as ArrayRef;
1100
1101 let string_builder = StringBuilder::new();
1103 let int_builder = Int32Builder::new();
1104 let mut map_builder = MapBuilder::new(None, string_builder, int_builder);
1105 map_builder.keys().append_value("a");
1106 map_builder.values().append_value(1);
1107 map_builder.append(true).unwrap();
1108 map_builder.append(false).unwrap();
1109 let map_array = Arc::new(map_builder.finish()) as ArrayRef;
1110
1111 let source_struct = StructArray::from(vec![
1112 (
1113 arc_field(
1114 "arr",
1115 DataType::List(Arc::new(field("item", DataType::Int32))),
1116 ),
1117 arr_array,
1118 ),
1119 (
1120 arc_field(
1121 "map",
1122 DataType::Map(
1123 Arc::new(non_null_field(
1124 "entries",
1125 struct_type(vec![
1126 non_null_field("keys", DataType::Utf8),
1127 field("values", DataType::Int32),
1128 ]),
1129 )),
1130 false,
1131 ),
1132 ),
1133 map_array,
1134 ),
1135 ]);
1136 let source_col = Arc::new(source_struct) as ArrayRef;
1137
1138 let target_field = struct_field(
1139 "s",
1140 vec![
1141 field(
1142 "arr",
1143 DataType::List(Arc::new(field("item", DataType::Int32))),
1144 ),
1145 field(
1146 "map",
1147 DataType::Map(
1148 Arc::new(non_null_field(
1149 "entries",
1150 struct_type(vec![
1151 non_null_field("keys", DataType::Utf8),
1152 field("values", DataType::Int32),
1153 ]),
1154 )),
1155 false,
1156 ),
1157 ),
1158 ],
1159 );
1160
1161 let result =
1162 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1163 .unwrap();
1164 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1165
1166 let arr = get_column_as!(&struct_array, "arr", ListArray);
1167 assert!(!arr.is_null(0));
1168 assert!(arr.is_null(1));
1169 let arr0 = arr.value(0);
1170 let values = arr0.as_any().downcast_ref::<Int32Array>().unwrap();
1171 assert_eq!(values.value(0), 1);
1172 assert_eq!(values.value(1), 2);
1173
1174 let map = get_column_as!(&struct_array, "map", MapArray);
1175 assert!(!map.is_null(0));
1176 assert!(map.is_null(1));
1177 let map0 = map.value(0);
1178 let entries = map0.as_any().downcast_ref::<StructArray>().unwrap();
1179 let keys = get_column_as!(entries, "keys", StringArray);
1180 let vals = get_column_as!(entries, "values", Int32Array);
1181 assert_eq!(keys.value(0), "a");
1182 assert_eq!(vals.value(0), 1);
1183 }
1184
1185 #[test]
1186 fn test_cast_struct_field_order_differs() {
1187 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1188 let b = Arc::new(Int32Array::from(vec![Some(3), None])) as ArrayRef;
1189
1190 let source_struct = StructArray::from(vec![
1191 (arc_field("a", DataType::Int32), a),
1192 (arc_field("b", DataType::Int32), b),
1193 ]);
1194 let source_col = Arc::new(source_struct) as ArrayRef;
1195
1196 let target_field = struct_field(
1197 "s",
1198 vec![field("b", DataType::Int64), field("a", DataType::Int32)],
1199 );
1200
1201 let result =
1202 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1203 .unwrap();
1204 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1205
1206 let b_col = get_column_as!(&struct_array, "b", Int64Array);
1207 assert_eq!(b_col.value(0), 3);
1208 assert!(b_col.is_null(1));
1209
1210 let a_col = get_column_as!(&struct_array, "a", Int32Array);
1211 assert_eq!(a_col.value(0), 1);
1212 assert_eq!(a_col.value(1), 2);
1213 }
1214
1215 #[test]
1216 fn test_cast_struct_no_overlap_rejected() {
1217 let first = Arc::new(Int32Array::from(vec![Some(10), Some(20)])) as ArrayRef;
1218 let second =
1219 Arc::new(StringArray::from(vec![Some("alpha"), Some("beta")])) as ArrayRef;
1220
1221 let source_struct = StructArray::from(vec![
1222 (arc_field("left", DataType::Int32), first),
1223 (arc_field("right", DataType::Utf8), second),
1224 ]);
1225 let source_col = Arc::new(source_struct) as ArrayRef;
1226
1227 let target_field = struct_field(
1228 "s",
1229 vec![field("a", DataType::Int64), field("b", DataType::Utf8)],
1230 );
1231
1232 let result =
1233 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
1234 assert!(result.is_err());
1235 let error_msg = result.unwrap_err().to_string();
1236 assert_contains!(error_msg, "no field name overlap");
1237 }
1238
1239 #[test]
1240 fn test_cast_struct_missing_non_nullable_field_fails() {
1241 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1243 let source_struct = StructArray::from(vec![(arc_field("a", DataType::Int32), a)]);
1244 let source_col = Arc::new(source_struct) as ArrayRef;
1245
1246 let target_field = struct_field(
1248 "s",
1249 vec![
1250 field("a", DataType::Int32),
1251 non_null_field("b", DataType::Int32),
1252 ],
1253 );
1254
1255 let result =
1257 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS);
1258 assert!(result.is_err());
1259 let err = result.unwrap_err();
1260 assert!(
1261 err.to_string()
1262 .contains("target field 'b' is non-nullable but missing from source"),
1263 "Unexpected error: {err}"
1264 );
1265 }
1266
1267 #[test]
1268 fn test_cast_struct_missing_nullable_field_succeeds() {
1269 let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
1271 let source_struct = StructArray::from(vec![(arc_field("a", DataType::Int32), a)]);
1272 let source_col = Arc::new(source_struct) as ArrayRef;
1273
1274 let target_field = struct_field(
1276 "s",
1277 vec![field("a", DataType::Int32), field("b", DataType::Int32)],
1278 );
1279
1280 let result =
1282 cast_column(&source_col, target_field.data_type(), &DEFAULT_CAST_OPTIONS)
1283 .unwrap();
1284 let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
1285
1286 let a_col = get_column_as!(&struct_array, "a", Int32Array);
1287 assert_eq!(a_col.value(0), 1);
1288 assert_eq!(a_col.value(1), 2);
1289
1290 let b_col = get_column_as!(&struct_array, "b", Int32Array);
1291 assert!(b_col.is_null(0));
1292 assert!(b_col.is_null(1));
1293 }
1294
1295 #[test]
1296 fn test_validate_dictionary_value_evolution() {
1297 let source_inner = struct_type(vec![field("a", DataType::Int32)]);
1298 let target_inner = struct_type(vec![
1299 field("a", DataType::Int32),
1300 field("b", DataType::Utf8),
1301 ]);
1302 let source =
1303 DataType::Dictionary(Box::new(DataType::Int32), Box::new(source_inner));
1304 let target =
1305 DataType::Dictionary(Box::new(DataType::Int32), Box::new(target_inner));
1306 assert!(validate_data_type_compatibility("col", &source, &target).is_ok());
1307 }
1308
1309 #[test]
1310 fn test_cast_dictionary_struct_value() {
1311 let struct_arr = StructArray::from(vec![(
1314 arc_field("a", DataType::Int32),
1315 Arc::new(Int32Array::from(vec![10, 20])) as ArrayRef,
1316 )]);
1317 let keys = Int32Array::from(vec![Some(0), None, Some(1)]);
1319 let source_dict = DictionaryArray::<Int32Type>::new(keys, Arc::new(struct_arr));
1320 let source_col: ArrayRef = Arc::new(source_dict);
1321
1322 let target_type = DataType::Dictionary(
1323 Box::new(DataType::Int32),
1324 Box::new(struct_type(vec![
1325 field("a", DataType::Int64),
1326 field("b", DataType::Utf8),
1327 ])),
1328 );
1329
1330 let result =
1331 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1332 let result_dict = result
1333 .as_any()
1334 .downcast_ref::<DictionaryArray<Int32Type>>()
1335 .unwrap();
1336
1337 assert!(result_dict.is_valid(0));
1338 assert!(result_dict.is_null(1));
1339 assert!(result_dict.is_valid(2));
1340
1341 let struct_values = result_dict
1342 .values()
1343 .as_any()
1344 .downcast_ref::<StructArray>()
1345 .unwrap();
1346 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1347 assert_eq!(a_col.values(), &[10, 20]);
1348 let b_col = get_column_as!(&struct_values, "b", StringArray);
1349 assert!(b_col.iter().all(|v| v.is_none()));
1350 }
1351
1352 #[test]
1353 fn test_cast_list_view_struct() {
1354 let struct_arr = StructArray::from(vec![(
1357 arc_field("a", DataType::Int32),
1358 Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef,
1359 )]);
1360
1361 let source_field =
1362 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1363 let target_field = arc_field(
1364 "item",
1365 struct_type(vec![
1366 field("a", DataType::Int64),
1367 field("b", DataType::Utf8),
1368 ]),
1369 );
1370
1371 let list_view = ListViewArray::new(
1373 source_field,
1374 ScalarBuffer::from(vec![0i32, 2]),
1375 ScalarBuffer::from(vec![2i32, 1]),
1376 Arc::new(struct_arr),
1377 None,
1378 );
1379 let source_col: ArrayRef = Arc::new(list_view);
1380
1381 let target_type = DataType::ListView(target_field);
1382
1383 let result =
1384 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1385 let result_lv = result.as_any().downcast_ref::<ListViewArray>().unwrap();
1386 assert_eq!(result_lv.len(), 2);
1387
1388 let struct_values = result_lv
1389 .values()
1390 .as_any()
1391 .downcast_ref::<StructArray>()
1392 .unwrap();
1393 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1394 assert_eq!(a_col.values(), &[1, 2, 3]);
1395 let b_col = get_column_as!(&struct_values, "b", StringArray);
1396 assert!(b_col.iter().all(|v| v.is_none()));
1397 }
1398
1399 fn fixed_size_list_struct_field(fields: Vec<(&str, DataType)>) -> FieldRef {
1400 arc_field(
1401 "item",
1402 struct_type(
1403 fields
1404 .into_iter()
1405 .map(|(name, data_type)| field(name, data_type))
1406 .collect(),
1407 ),
1408 )
1409 }
1410
1411 fn create_fixed_size_list_test_fields(
1412 source_struct_fields: Vec<(&str, DataType)>,
1413 target_struct_fields: Vec<(&str, DataType)>,
1414 ) -> (FieldRef, FieldRef) {
1415 (
1416 fixed_size_list_struct_field(source_struct_fields),
1417 fixed_size_list_struct_field(target_struct_fields),
1418 )
1419 }
1420
1421 fn fixed_size_list_struct_values(
1422 array: &ArrayRef,
1423 ) -> (&FixedSizeListArray, &StructArray) {
1424 let list = array.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
1425 let values = list
1426 .values()
1427 .as_any()
1428 .downcast_ref::<StructArray>()
1429 .unwrap();
1430 (list, values)
1431 }
1432
1433 #[test]
1434 fn test_cast_fixed_size_list_struct() {
1435 let struct_arr = StructArray::from(vec![(
1436 arc_field("a", DataType::Int32),
1437 Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as ArrayRef,
1438 )]);
1439
1440 let (source_field, target_field) = create_fixed_size_list_test_fields(
1441 vec![("a", DataType::Int32)],
1442 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1443 );
1444 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1445 source_field,
1446 2,
1447 Arc::new(struct_arr),
1448 Some(NullBuffer::from(vec![true, false])),
1449 ));
1450 let target_type = DataType::FixedSizeList(target_field, 2);
1451
1452 let result =
1453 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1454 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1455 assert_eq!(result_list.len(), 2);
1456 assert!(result_list.is_valid(0));
1457 assert!(result_list.is_null(1));
1458 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1459 assert_eq!(a_col.values(), &[1, 2, 3, 4]);
1460 let b_col = get_column_as!(&struct_values, "b", StringArray);
1461 assert!(b_col.iter().all(|v| v.is_none()));
1462 }
1463
1464 #[test]
1465 fn test_validate_fixed_size_list_struct_compatibility() {
1466 let (source_field, target_field) = create_fixed_size_list_test_fields(
1467 vec![("a", DataType::Int32)],
1468 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1469 );
1470 let source = DataType::FixedSizeList(source_field, 2);
1471 let target = DataType::FixedSizeList(target_field, 2);
1472
1473 assert!(requires_nested_struct_cast(&source, &target));
1474 assert!(validate_data_type_compatibility("col", &source, &target).is_ok());
1475 }
1476
1477 #[test]
1478 fn test_validate_fixed_size_list_struct_missing_non_nullable_field_rejected() {
1479 let (source_field, _) = create_fixed_size_list_test_fields(
1480 vec![("a", DataType::Int32)],
1481 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1482 );
1483 let source = DataType::FixedSizeList(source_field, 2);
1484 let target = DataType::FixedSizeList(
1485 arc_field(
1486 "item",
1487 struct_type(vec![
1488 field("a", DataType::Int32),
1489 non_null_field("b", DataType::Utf8),
1490 ]),
1491 ),
1492 2,
1493 );
1494
1495 let error = validate_data_type_compatibility("col", &source, &target)
1496 .unwrap_err()
1497 .to_string();
1498 assert_contains!(
1499 error,
1500 "target field 'b' is non-nullable but missing from source"
1501 );
1502 }
1503
1504 #[test]
1505 fn test_fixed_size_list_struct_size_mismatch_rejected() {
1506 let source_field = fixed_size_list_struct_field(vec![("a", DataType::Int32)]);
1507 let target_field = Arc::clone(&source_field);
1508 let source_type = DataType::FixedSizeList(Arc::clone(&source_field), 2);
1509 let target_type = DataType::FixedSizeList(target_field, 3);
1510
1511 let validation_error =
1512 validate_data_type_compatibility("col", &source_type, &target_type)
1513 .unwrap_err()
1514 .to_string();
1515 assert_contains!(validation_error, "Cannot cast struct field 'col'");
1516
1517 let struct_arr = StructArray::from(vec![(
1518 arc_field("a", DataType::Int32),
1519 Arc::new(Int32Array::from(vec![1, 2])) as ArrayRef,
1520 )]);
1521 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1522 source_field,
1523 2,
1524 Arc::new(struct_arr),
1525 None,
1526 ));
1527
1528 let runtime_error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1529 .unwrap_err()
1530 .to_string();
1531 assert_contains!(
1532 runtime_error,
1533 "cannot cast fixed-size-list to fixed-size-list with different size"
1534 );
1535 }
1536
1537 #[test]
1538 fn test_cast_fixed_size_list_struct_all_null() {
1539 let (source_field, target_field) = create_fixed_size_list_test_fields(
1540 vec![("a", DataType::Int32)],
1541 vec![("a", DataType::Int64), ("b", DataType::Utf8)],
1542 );
1543 let source_col: ArrayRef =
1544 Arc::new(FixedSizeListArray::new_null(source_field, 2, 2));
1545 let target_type = DataType::FixedSizeList(target_field, 2);
1546
1547 let result =
1548 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1549 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1550 assert_eq!(result_list.null_count(), 2);
1551 let a_col = get_column_as!(&struct_values, "a", Int64Array);
1552 let b_col = get_column_as!(&struct_values, "b", StringArray);
1553 assert!(a_col.iter().all(|v| v.is_none()));
1554 assert!(b_col.iter().all(|v| v.is_none()));
1555 }
1556
1557 #[test]
1558 fn test_fixed_size_list_struct_planner_runtime_parity_on_incompatible_type() {
1559 let source_field =
1560 arc_field("item", struct_type(vec![field("a", DataType::Binary)]));
1561 let target_field =
1562 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1563 let source_type = DataType::FixedSizeList(Arc::clone(&source_field), 2);
1564 let target_type = DataType::FixedSizeList(target_field, 2);
1565 let validation_error =
1566 validate_data_type_compatibility("col", &source_type, &target_type)
1567 .unwrap_err()
1568 .to_string();
1569 assert_contains!(validation_error, "Cannot cast struct field 'a'");
1570
1571 let struct_arr = StructArray::from(vec![(
1572 arc_field("a", DataType::Binary),
1573 Arc::new(BinaryArray::from(vec![
1574 Some(b"x".as_ref()),
1575 Some(b"y".as_ref()),
1576 ])) as ArrayRef,
1577 )]);
1578 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1579 source_field,
1580 2,
1581 Arc::new(struct_arr),
1582 None,
1583 ));
1584
1585 let runtime_error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1586 .unwrap_err()
1587 .to_string();
1588 assert_contains!(runtime_error, "Cannot cast struct field 'a'");
1589 }
1590
1591 #[test]
1592 fn test_cast_fixed_size_list_struct_missing_non_nullable_field_runtime_rejected() {
1593 let source_field =
1594 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1595 let target_field = arc_field(
1596 "item",
1597 struct_type(vec![
1598 field("a", DataType::Int32),
1599 non_null_field("b", DataType::Utf8),
1600 ]),
1601 );
1602 let source_col: ArrayRef =
1603 Arc::new(FixedSizeListArray::new_null(source_field, 2, 1));
1604 let target_type = DataType::FixedSizeList(target_field, 2);
1605
1606 let error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1607 .unwrap_err()
1608 .to_string();
1609 assert_contains!(
1610 error,
1611 "target field 'b' is non-nullable but missing from source"
1612 );
1613 }
1614
1615 #[test]
1616 fn test_cast_fixed_size_list_returns_error_for_non_nullable_child() {
1617 let source_field = Arc::new(Field::new("item", DataType::Int32, true));
1618 let target_field = Arc::new(Field::new("item", DataType::Int32, false));
1619 let source_col: ArrayRef = Arc::new(FixedSizeListArray::new(
1620 source_field,
1621 2,
1622 Arc::new(Int32Array::from(vec![None, Some(1)])),
1623 None,
1624 ));
1625 let target_type = DataType::FixedSizeList(target_field, 2);
1626
1627 let error = cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS)
1628 .unwrap_err()
1629 .to_string();
1630 assert_contains!(error, "Found unmasked nulls for non-nullable");
1631 }
1632
1633 #[test]
1634 fn test_cast_sliced_fixed_size_list_struct_ignores_hidden_child_values() {
1635 let source_field =
1636 arc_field("item", struct_type(vec![field("a", DataType::Utf8)]));
1637 let target_field =
1638 arc_field("item", struct_type(vec![field("a", DataType::Int32)]));
1639 let struct_arr = StructArray::from(vec![(
1640 arc_field("a", DataType::Utf8),
1641 Arc::new(StringArray::from(vec![
1642 "0", "0", "not_int", "also_bad", "1", "2",
1643 ])) as ArrayRef,
1644 )]);
1645 let source_col: ArrayRef = Arc::new(
1646 FixedSizeListArray::new(
1647 source_field,
1648 2,
1649 Arc::new(struct_arr),
1650 Some(NullBuffer::from(vec![true, false, true])),
1651 )
1652 .slice(1, 2),
1653 );
1654 let target_type = DataType::FixedSizeList(target_field, 2);
1655
1656 let result =
1657 cast_column(&source_col, &target_type, &DEFAULT_CAST_OPTIONS).unwrap();
1658 let (result_list, struct_values) = fixed_size_list_struct_values(&result);
1659 assert!(result_list.is_null(0));
1660 assert!(result_list.is_valid(1));
1661 let a_col = get_column_as!(&struct_values, "a", Int32Array);
1662 assert!(a_col.is_null(0));
1663 assert!(a_col.is_null(1));
1664 assert_eq!(a_col.value(2), 1);
1665 assert_eq!(a_col.value(3), 2);
1666 }
1667
1668 #[test]
1669 fn test_requires_nested_struct_cast() {
1670 let s1 = struct_type(vec![field("a", DataType::Int32)]);
1671 let s2 = struct_type(vec![field("a", DataType::Int64)]);
1672
1673 assert!(requires_nested_struct_cast(&s1, &s2));
1674 assert!(requires_nested_struct_cast(
1675 &DataType::List(arc_field("item", s1.clone())),
1676 &DataType::List(arc_field("item", s2.clone())),
1677 ));
1678 assert!(requires_nested_struct_cast(
1679 &DataType::Dictionary(Box::new(DataType::Int32), Box::new(s1.clone())),
1680 &DataType::Dictionary(Box::new(DataType::Int32), Box::new(s2.clone())),
1681 ));
1682 assert!(requires_nested_struct_cast(
1683 &DataType::ListView(arc_field("item", s1.clone())),
1684 &DataType::ListView(arc_field("item", s2.clone())),
1685 ));
1686 assert!(requires_nested_struct_cast(
1687 &DataType::FixedSizeList(arc_field("item", s1), 2),
1688 &DataType::FixedSizeList(arc_field("item", s2), 2),
1689 ));
1690
1691 assert!(!requires_nested_struct_cast(
1693 &DataType::Int32,
1694 &DataType::Int64
1695 ));
1696 assert!(!requires_nested_struct_cast(
1697 &DataType::List(arc_field("item", DataType::Int32)),
1698 &DataType::List(arc_field("item", DataType::Int64)),
1699 ));
1700 assert!(!requires_nested_struct_cast(
1701 &DataType::FixedSizeList(arc_field("item", DataType::Int32), 2),
1702 &DataType::FixedSizeList(arc_field("item", DataType::Int64), 2),
1703 ));
1704 }
1705}