## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/common/src/nested_struct.rs

    Line
    Count
    Source
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    use crate::error::{Result, _plan_err};
    19
    use arrow::{
    20
        array::{new_null_array, Array, ArrayRef, StructArray},
    21
        compute::{cast_with_options, CastOptions},
    22
        datatypes::{DataType::Struct, Field, FieldRef},
    23
    };
    24
    use std::sync::Arc;
    25
    26
    /// Cast a struct column to match target struct fields, handling nested structs recursively.
    27
    ///
    28
    /// This function implements struct-to-struct casting with the assumption that **structs should
    29
    /// always be allowed to cast to other structs**. However, the source column must already be
    30
    /// a struct type - non-struct sources will result in an error.
    31
    ///
    32
    /// ## Field Matching Strategy
    33
    /// - **By Name**: Source struct fields are matched to target fields by name (case-sensitive)
    34
    /// - **Type Adaptation**: When a matching field is found, it is recursively cast to the target field's type
    35
    /// - **Missing Fields**: Target fields not present in the source are filled with null values
    36
    /// - **Extra Fields**: Source fields not present in the target are ignored
    37
    ///
    38
    /// ## Nested Struct Handling
    39
    /// - Nested structs are handled recursively using the same casting rules
    40
    /// - Each level of nesting follows the same field matching and null-filling strategy
    41
    /// - This allows for complex struct transformations while maintaining data integrity
    42
    ///
    43
    /// # Arguments
    44
    /// * `source_col` - The source array to cast (must be a struct array)
    45
    /// * `target_fields` - The target struct field definitions to cast to
    46
    ///
    47
    /// # Returns
    48
    /// A `Result<ArrayRef>` containing the cast struct array
    49
    ///
    50
    /// # Errors
    51
    /// Returns a `DataFusionError::Plan` if the source column is not a struct type
    52
    0
    fn cast_struct_column(
    53
    0
        source_col: &ArrayRef,
    54
    0
        target_fields: &[Arc<Field>],
    55
    0
        cast_options: &CastOptions,
    56
    0
    ) -> Result<ArrayRef> {
    57
    0
        if let Some(source_struct) = source_col.as_any().downcast_ref::<StructArray>() {
    58
    0
            validate_struct_compatibility(source_struct.fields(), target_fields)?;
    59
    60
    0
            let mut fields: Vec<Arc<Field>> = Vec::with_capacity(target_fields.len());
    61
    0
            let mut arrays: Vec<ArrayRef> = Vec::with_capacity(target_fields.len());
    62
    0
            let num_rows = source_col.len();
    63
    64
    0
            for target_child_field in target_fields {
    65
    0
                fields.push(Arc::clone(target_child_field));
    66
    0
                match source_struct.column_by_name(target_child_field.name()) {
    67
    0
                    Some(source_child_col) => {
    68
    0
                        let adapted_child =
    69
    0
                            cast_column(source_child_col, target_child_field, cast_options)
    70
    0
                                .map_err(|e| {
    71
    0
                                e.context(format!(
    72
    0
                                    "While casting struct field '{}'",
    73
    0
                                    target_child_field.name()
    74
                                ))
    75
    0
                            })?;
    76
    0
                        arrays.push(adapted_child);
    77
                    }
    78
    0
                    None => {
    79
    0
                        arrays.push(new_null_array(target_child_field.data_type(), num_rows));
    80
    0
                    }
    81
                }
    82
            }
    83
    84
    0
            let struct_array =
    85
    0
                StructArray::new(fields.into(), arrays, source_struct.nulls().cloned());
    86
    0
            Ok(Arc::new(struct_array))
    87
        } else {
    88
            // Return error if source is not a struct type
    89
    0
            _plan_err!(
    90
    0
                "Cannot cast column of type {} to struct type. Source must be a struct to cast to struct.",
    91
    0
                source_col.data_type()
    92
            )
    93
        }
    94
    0
    }
    95
    96
    /// Cast a column to match the target field type, with special handling for nested structs.
    97
    ///
    98
    /// This function serves as the main entry point for column casting operations. For struct
    99
    /// types, it enforces that **only struct columns can be cast to struct types**.
    100
    ///
    101
    /// ## Casting Behavior
    102
    /// - **Struct Types**: Delegates to `cast_struct_column` for struct-to-struct casting only
    103
    /// - **Non-Struct Types**: Uses Arrow's standard `cast` function for primitive type conversions
    104
    ///
    105
    /// ## Cast Options
    106
    /// The `cast_options` argument controls how Arrow handles values that cannot be represented
    107
    /// in the target type. When `safe` is `false` (DataFusion's default) the cast will return an
    108
    /// error if such a value is encountered. Setting `safe` to `true` instead produces `NULL`
    109
    /// for out-of-range or otherwise invalid values. The options also allow customizing how
    110
    /// temporal values are formatted when cast to strings.
    111
    ///
    112
    /// ```
    113
    /// use arrow::array::{ArrayRef, Int64Array};
    114
    /// use arrow::compute::CastOptions;
    115
    /// use arrow::datatypes::{DataType, Field};
    116
    /// use datafusion_common::nested_struct::cast_column;
    117
    /// use std::sync::Arc;
    118
    ///
    119
    /// let source: ArrayRef = Arc::new(Int64Array::from(vec![1, i64::MAX]));
    120
    /// let target = Field::new("ints", DataType::Int32, true);
    121
    /// // Permit lossy conversions by producing NULL on overflow instead of erroring
    122
    /// let options = CastOptions {
    123
    ///     safe: true,
    124
    ///     ..Default::default()
    125
    /// };
    126
    /// let result = cast_column(&source, &target, &options).unwrap();
    127
    /// assert!(result.is_null(1));
    128
    /// ```
    129
    ///
    130
    /// ## Struct Casting Requirements
    131
    /// The struct casting logic requires that the source column must already be a struct type.
    132
    /// This makes the function useful for:
    133
    /// - Schema evolution scenarios where struct layouts change over time
    134
    /// - Data migration between different struct schemas
    135
    /// - Type-safe data processing pipelines that maintain struct type integrity
    136
    ///
    137
    /// # Arguments
    138
    /// * `source_col` - The source array to cast
    139
    /// * `target_field` - The target field definition (including type and metadata)
    140
    /// * `cast_options` - Options that govern strictness and formatting of the cast
    141
    ///
    142
    /// # Returns
    143
    /// A `Result<ArrayRef>` containing the cast array
    144
    ///
    145
    /// # Errors
    146
    /// Returns an error if:
    147
    /// - Attempting to cast a non-struct column to a struct type
    148
    /// - Arrow's cast function fails for non-struct types
    149
    /// - Memory allocation fails during struct construction
    150
    /// - Invalid data type combinations are encountered
    151
    0
    pub fn cast_column(
    152
    0
        source_col: &ArrayRef,
    153
    0
        target_field: &Field,
    154
    0
        cast_options: &CastOptions,
    155
    0
    ) -> Result<ArrayRef> {
    156
    0
        match target_field.data_type() {
    157
    0
            Struct(target_fields) => {
    158
    0
                cast_struct_column(source_col, target_fields, cast_options)
    159
            }
    160
    0
            _ => Ok(cast_with_options(
    161
    0
                source_col,
    162
    0
                target_field.data_type(),
    163
    0
                cast_options,
    164
    0
            )?),
    165
        }
    166
    0
    }
    167
    168
    /// Validates compatibility between source and target struct fields for casting operations.
    169
    ///
    170
    /// This function implements comprehensive struct compatibility checking by examining:
    171
    /// - Field name matching between source and target structs
    172
    /// - Type castability for each matching field (including recursive struct validation)
    173
    /// - Proper handling of missing fields (target fields not in source are allowed - filled with nulls)
    174
    /// - Proper handling of extra fields (source fields not in target are allowed - ignored)
    175
    ///
    176
    /// # Compatibility Rules
    177
    /// - **Field Matching**: Fields are matched by name (case-sensitive)
    178
    /// - **Missing Target Fields**: Allowed - will be filled with null values during casting
    179
    /// - **Extra Source Fields**: Allowed - will be ignored during casting
    180
    /// - **Type Compatibility**: Each matching field must be castable using Arrow's type system
    181
    /// - **Nested Structs**: Recursively validates nested struct compatibility
    182
    ///
    183
    /// # Arguments
    184
    /// * `source_fields` - Fields from the source struct type
    185
    /// * `target_fields` - Fields from the target struct type
    186
    ///
    187
    /// # Returns
    188
    /// * `Ok(())` if the structs are compatible for casting
    189
    /// * `Err(DataFusionError)` with detailed error message if incompatible
    190
    ///
    191
    /// # Examples
    192
    /// ```text
    193
    /// // Compatible: source has extra field, target has missing field
    194
    /// // Source: {a: i32, b: string, c: f64}
    195
    /// // Target: {a: i64, d: bool}
    196
    /// // Result: Ok(()) - 'a' can cast i32->i64, 'b','c' ignored, 'd' filled with nulls
    197
    ///
    198
    /// // Incompatible: matching field has incompatible types
    199
    /// // Source: {a: string}
    200
    /// // Target: {a: binary}
    201
    /// // Result: Err(...) - string cannot cast to binary
    202
    /// ```
    203
    0
    pub fn validate_struct_compatibility(
    204
    0
        source_fields: &[FieldRef],
    205
    0
        target_fields: &[FieldRef],
    206
    0
    ) -> Result<()> {
    207
        // Check compatibility for each target field
    208
    0
        for target_field in target_fields {
    209
            // Look for matching field in source by name
    210
    0
            if let Some(source_field) = source_fields
    211
    0
                .iter()
    212
    0
                .find(|f| f.name() == target_field.name())
    213
            {
    214
                // Ensure nullability is compatible. It is invalid to cast a nullable
    215
                // source field to a non-nullable target field as this may discard
    216
                // null values.
    217
    0
                if source_field.is_nullable() && !target_field.is_nullable() {
    218
    0
                    return _plan_err!(
    219
    0
                        "Cannot cast nullable struct field '{}' to non-nullable field",
    220
    0
                        target_field.name()
    221
                    );
    222
    0
                }
    223
                // Check if the matching field types are compatible
    224
    0
                match (source_field.data_type(), target_field.data_type()) {
    225
                    // Recursively validate nested structs
    226
    0
                    (Struct(source_nested), Struct(target_nested)) => {
    227
    0
                        validate_struct_compatibility(source_nested, target_nested)?;
    228
                    }
    229
                    // For non-struct types, use the existing castability check
    230
                    _ => {
    231
    0
                        if !arrow::compute::can_cast_types(
    232
    0
                            source_field.data_type(),
    233
    0
                            target_field.data_type(),
    234
    0
                        ) {
    235
    0
                            return _plan_err!(
    236
    0
                                "Cannot cast struct field '{}' from type {} to type {}",
    237
    0
                                target_field.name(),
    238
    0
                                source_field.data_type(),
    239
    0
                                target_field.data_type()
    240
                            );
    241
    0
                        }
    242
                    }
    243
                }
    244
    0
            }
    245
            // Missing fields in source are OK - they'll be filled with nulls
    246
        }
    247
    248
        // Extra fields in source are OK - they'll be ignored
    249
    0
        Ok(())
    250
    0
    }
    251
    252
    #[cfg(test)]
    253
    mod tests {
    254
    255
        use super::*;
    256
        use crate::format::DEFAULT_CAST_OPTIONS;
    257
        use arrow::{
    258
            array::{
    259
                BinaryArray, Int32Array, Int32Builder, Int64Array, ListArray, MapArray,
    260
                MapBuilder, StringArray, StringBuilder,
    261
            },
    262
            buffer::NullBuffer,
    263
            datatypes::{DataType, Field, FieldRef, Int32Type},
    264
        };
    265
        /// Macro to extract and downcast a column from a StructArray
    266
        macro_rules! get_column_as {
    267
            ($struct_array:expr, $column_name:expr, $array_type:ty) => {
    268
                $struct_array
    269
                    .column_by_name($column_name)
    270
                    .unwrap()
    271
                    .as_any()
    272
                    .downcast_ref::<$array_type>()
    273
                    .unwrap()
    274
            };
    275
        }
    276
    277
        fn field(name: &str, data_type: DataType) -> Field {
    278
            Field::new(name, data_type, true)
    279
        }
    280
    281
        fn non_null_field(name: &str, data_type: DataType) -> Field {
    282
            Field::new(name, data_type, false)
    283
        }
    284
    285
        fn arc_field(name: &str, data_type: DataType) -> FieldRef {
    286
            Arc::new(field(name, data_type))
    287
        }
    288
    289
        fn struct_type(fields: Vec<Field>) -> DataType {
    290
            Struct(fields.into())
    291
        }
    292
    293
        fn struct_field(name: &str, fields: Vec<Field>) -> Field {
    294
            field(name, struct_type(fields))
    295
        }
    296
    297
        fn arc_struct_field(name: &str, fields: Vec<Field>) -> FieldRef {
    298
            Arc::new(struct_field(name, fields))
    299
        }
    300
    301
        #[test]
    302
        fn test_cast_simple_column() {
    303
            let source = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
    304
            let target_field = field("ints", DataType::Int64);
    305
            let result = cast_column(&source, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    306
            let result = result.as_any().downcast_ref::<Int64Array>().unwrap();
    307
            assert_eq!(result.len(), 3);
    308
            assert_eq!(result.value(0), 1);
    309
            assert_eq!(result.value(1), 2);
    310
            assert_eq!(result.value(2), 3);
    311
        }
    312
    313
        #[test]
    314
        fn test_cast_column_with_options() {
    315
            let source = Arc::new(Int64Array::from(vec![1, i64::MAX])) as ArrayRef;
    316
            let target_field = field("ints", DataType::Int32);
    317
    318
            let safe_opts = CastOptions {
    319
                // safe: false - return Err for failure
    320
                safe: false,
    321
                ..DEFAULT_CAST_OPTIONS
    322
            };
    323
            assert!(cast_column(&source, &target_field, &safe_opts).is_err());
    324
    325
            let unsafe_opts = CastOptions {
    326
                // safe: true - return Null for failure
    327
                safe: true,
    328
                ..DEFAULT_CAST_OPTIONS
    329
            };
    330
            let result = cast_column(&source, &target_field, &unsafe_opts).unwrap();
    331
            let result = result.as_any().downcast_ref::<Int32Array>().unwrap();
    332
            assert_eq!(result.value(0), 1);
    333
            assert!(result.is_null(1));
    334
        }
    335
    336
        #[test]
    337
        fn test_cast_struct_with_missing_field() {
    338
            let a_array = Arc::new(Int32Array::from(vec![1, 2])) as ArrayRef;
    339
            let source_struct = StructArray::from(vec![(
    340
                arc_field("a", DataType::Int32),
    341
                Arc::clone(&a_array),
    342
            )]);
    343
            let source_col = Arc::new(source_struct) as ArrayRef;
    344
    345
            let target_field = struct_field(
    346
                "s",
    347
                vec![field("a", DataType::Int32), field("b", DataType::Utf8)],
    348
            );
    349
    350
            let result =
    351
                cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    352
            let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
    353
            assert_eq!(struct_array.fields().len(), 2);
    354
            let a_result = get_column_as!(&struct_array, "a", Int32Array);
    355
            assert_eq!(a_result.value(0), 1);
    356
            assert_eq!(a_result.value(1), 2);
    357
    358
            let b_result = get_column_as!(&struct_array, "b", StringArray);
    359
            assert_eq!(b_result.len(), 2);
    360
            assert!(b_result.is_null(0));
    361
            assert!(b_result.is_null(1));
    362
        }
    363
    364
        #[test]
    365
        fn test_cast_struct_source_not_struct() {
    366
            let source = Arc::new(Int32Array::from(vec![10, 20])) as ArrayRef;
    367
            let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
    368
    369
            let result = cast_column(&source, &target_field, &DEFAULT_CAST_OPTIONS);
    370
            assert!(result.is_err());
    371
            let error_msg = result.unwrap_err().to_string();
    372
            assert!(error_msg.contains("Cannot cast column of type"));
    373
            assert!(error_msg.contains("to struct type"));
    374
            assert!(error_msg.contains("Source must be a struct"));
    375
        }
    376
    377
        #[test]
    378
        fn test_cast_struct_incompatible_child_type() {
    379
            let a_array = Arc::new(BinaryArray::from(vec![
    380
                Some(b"a".as_ref()),
    381
                Some(b"b".as_ref()),
    382
            ])) as ArrayRef;
    383
            let source_struct =
    384
                StructArray::from(vec![(arc_field("a", DataType::Binary), a_array)]);
    385
            let source_col = Arc::new(source_struct) as ArrayRef;
    386
    387
            let target_field = struct_field("s", vec![field("a", DataType::Int32)]);
    388
    389
            let result = cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS);
    390
            assert!(result.is_err());
    391
            let error_msg = result.unwrap_err().to_string();
    392
            assert!(error_msg.contains("Cannot cast struct field 'a'"));
    393
        }
    394
    395
        #[test]
    396
        fn test_validate_struct_compatibility_incompatible_types() {
    397
            // Source struct: {field1: Binary, field2: String}
    398
            let source_fields = vec![
    399
                arc_field("field1", DataType::Binary),
    400
                arc_field("field2", DataType::Utf8),
    401
            ];
    402
    403
            // Target struct: {field1: Int32}
    404
            let target_fields = vec![arc_field("field1", DataType::Int32)];
    405
    406
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    407
            assert!(result.is_err());
    408
            let error_msg = result.unwrap_err().to_string();
    409
            assert!(error_msg.contains("Cannot cast struct field 'field1'"));
    410
            assert!(error_msg.contains("Binary"));
    411
            assert!(error_msg.contains("Int32"));
    412
        }
    413
    414
        #[test]
    415
        fn test_validate_struct_compatibility_compatible_types() {
    416
            // Source struct: {field1: Int32, field2: String}
    417
            let source_fields = vec![
    418
                arc_field("field1", DataType::Int32),
    419
                arc_field("field2", DataType::Utf8),
    420
            ];
    421
    422
            // Target struct: {field1: Int64} (Int32 can cast to Int64)
    423
            let target_fields = vec![arc_field("field1", DataType::Int64)];
    424
    425
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    426
            assert!(result.is_ok());
    427
        }
    428
    429
        #[test]
    430
        fn test_validate_struct_compatibility_missing_field_in_source() {
    431
            // Source struct: {field2: String} (missing field1)
    432
            let source_fields = vec![arc_field("field2", DataType::Utf8)];
    433
    434
            // Target struct: {field1: Int32}
    435
            let target_fields = vec![arc_field("field1", DataType::Int32)];
    436
    437
            // Should be OK - missing fields will be filled with nulls
    438
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    439
            assert!(result.is_ok());
    440
        }
    441
    442
        #[test]
    443
        fn test_validate_struct_compatibility_additional_field_in_source() {
    444
            // Source struct: {field1: Int32, field2: String} (extra field2)
    445
            let source_fields = vec![
    446
                arc_field("field1", DataType::Int32),
    447
                arc_field("field2", DataType::Utf8),
    448
            ];
    449
    450
            // Target struct: {field1: Int32}
    451
            let target_fields = vec![arc_field("field1", DataType::Int32)];
    452
    453
            // Should be OK - extra fields in source are ignored
    454
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    455
            assert!(result.is_ok());
    456
        }
    457
    458
        #[test]
    459
        fn test_cast_struct_parent_nulls_retained() {
    460
            let a_array = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
    461
            let fields = vec![arc_field("a", DataType::Int32)];
    462
            let nulls = Some(NullBuffer::from(vec![true, false]));
    463
            let source_struct = StructArray::new(fields.clone().into(), vec![a_array], nulls);
    464
            let source_col = Arc::new(source_struct) as ArrayRef;
    465
    466
            let target_field = struct_field("s", vec![field("a", DataType::Int64)]);
    467
    468
            let result =
    469
                cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    470
            let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
    471
            assert_eq!(struct_array.null_count(), 1);
    472
            assert!(struct_array.is_valid(0));
    473
            assert!(struct_array.is_null(1));
    474
    475
            let a_result = get_column_as!(&struct_array, "a", Int64Array);
    476
            assert_eq!(a_result.value(0), 1);
    477
            assert_eq!(a_result.value(1), 2);
    478
        }
    479
    480
        #[test]
    481
        fn test_validate_struct_compatibility_nullable_to_non_nullable() {
    482
            // Source struct: {field1: Int32 nullable}
    483
            let source_fields = vec![arc_field("field1", DataType::Int32)];
    484
    485
            // Target struct: {field1: Int32 non-nullable}
    486
            let target_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
    487
    488
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    489
            assert!(result.is_err());
    490
            let error_msg = result.unwrap_err().to_string();
    491
            assert!(error_msg.contains("field1"));
    492
            assert!(error_msg.contains("non-nullable"));
    493
        }
    494
    495
        #[test]
    496
        fn test_validate_struct_compatibility_non_nullable_to_nullable() {
    497
            // Source struct: {field1: Int32 non-nullable}
    498
            let source_fields = vec![Arc::new(non_null_field("field1", DataType::Int32))];
    499
    500
            // Target struct: {field1: Int32 nullable}
    501
            let target_fields = vec![arc_field("field1", DataType::Int32)];
    502
    503
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    504
            assert!(result.is_ok());
    505
        }
    506
    507
        #[test]
    508
        fn test_validate_struct_compatibility_nested_nullable_to_non_nullable() {
    509
            // Source struct: {field1: {nested: Int32 nullable}}
    510
            let source_fields = vec![Arc::new(non_null_field(
    511
                "field1",
    512
                struct_type(vec![field("nested", DataType::Int32)]),
    513
            ))];
    514
    515
            // Target struct: {field1: {nested: Int32 non-nullable}}
    516
            let target_fields = vec![Arc::new(non_null_field(
    517
                "field1",
    518
                struct_type(vec![non_null_field("nested", DataType::Int32)]),
    519
            ))];
    520
    521
            let result = validate_struct_compatibility(&source_fields, &target_fields);
    522
            assert!(result.is_err());
    523
            let error_msg = result.unwrap_err().to_string();
    524
            assert!(error_msg.contains("nested"));
    525
            assert!(error_msg.contains("non-nullable"));
    526
        }
    527
    528
        #[test]
    529
        fn test_cast_nested_struct_with_extra_and_missing_fields() {
    530
            // Source inner struct has fields a, b, extra
    531
            let a = Arc::new(Int32Array::from(vec![Some(1), None])) as ArrayRef;
    532
            let b = Arc::new(Int32Array::from(vec![Some(2), Some(3)])) as ArrayRef;
    533
            let extra = Arc::new(Int32Array::from(vec![Some(9), Some(10)])) as ArrayRef;
    534
    535
            let inner = StructArray::from(vec![
    536
                (arc_field("a", DataType::Int32), a),
    537
                (arc_field("b", DataType::Int32), b),
    538
                (arc_field("extra", DataType::Int32), extra),
    539
            ]);
    540
    541
            let source_struct = StructArray::from(vec![(
    542
                arc_struct_field(
    543
                    "inner",
    544
                    vec![
    545
                        field("a", DataType::Int32),
    546
                        field("b", DataType::Int32),
    547
                        field("extra", DataType::Int32),
    548
                    ],
    549
                ),
    550
                Arc::new(inner) as ArrayRef,
    551
            )]);
    552
            let source_col = Arc::new(source_struct) as ArrayRef;
    553
    554
            // Target inner struct reorders fields, adds "missing", and drops "extra"
    555
            let target_field = struct_field(
    556
                "outer",
    557
                vec![struct_field(
    558
                    "inner",
    559
                    vec![
    560
                        field("b", DataType::Int64),
    561
                        field("a", DataType::Int32),
    562
                        field("missing", DataType::Int32),
    563
                    ],
    564
                )],
    565
            );
    566
    567
            let result =
    568
                cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    569
            let outer = result.as_any().downcast_ref::<StructArray>().unwrap();
    570
            let inner = get_column_as!(&outer, "inner", StructArray);
    571
            assert_eq!(inner.fields().len(), 3);
    572
    573
            let b = get_column_as!(inner, "b", Int64Array);
    574
            assert_eq!(b.value(0), 2);
    575
            assert_eq!(b.value(1), 3);
    576
            assert!(!b.is_null(0));
    577
            assert!(!b.is_null(1));
    578
    579
            let a = get_column_as!(inner, "a", Int32Array);
    580
            assert_eq!(a.value(0), 1);
    581
            assert!(a.is_null(1));
    582
    583
            let missing = get_column_as!(inner, "missing", Int32Array);
    584
            assert!(missing.is_null(0));
    585
            assert!(missing.is_null(1));
    586
        }
    587
    588
        #[test]
    589
        fn test_cast_struct_with_array_and_map_fields() {
    590
            // Array field with second row null
    591
            let arr_array = Arc::new(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
    592
                Some(vec![Some(1), Some(2)]),
    593
                None,
    594
            ])) as ArrayRef;
    595
    596
            // Map field with second row null
    597
            let string_builder = StringBuilder::new();
    598
            let int_builder = Int32Builder::new();
    599
            let mut map_builder = MapBuilder::new(None, string_builder, int_builder);
    600
            map_builder.keys().append_value("a");
    601
            map_builder.values().append_value(1);
    602
            map_builder.append(true).unwrap();
    603
            map_builder.append(false).unwrap();
    604
            let map_array = Arc::new(map_builder.finish()) as ArrayRef;
    605
    606
            let source_struct = StructArray::from(vec![
    607
                (
    608
                    arc_field(
    609
                        "arr",
    610
                        DataType::List(Arc::new(field("item", DataType::Int32))),
    611
                    ),
    612
                    arr_array,
    613
                ),
    614
                (
    615
                    arc_field(
    616
                        "map",
    617
                        DataType::Map(
    618
                            Arc::new(non_null_field(
    619
                                "entries",
    620
                                struct_type(vec![
    621
                                    non_null_field("keys", DataType::Utf8),
    622
                                    field("values", DataType::Int32),
    623
                                ]),
    624
                            )),
    625
                            false,
    626
                        ),
    627
                    ),
    628
                    map_array,
    629
                ),
    630
            ]);
    631
            let source_col = Arc::new(source_struct) as ArrayRef;
    632
    633
            let target_field = struct_field(
    634
                "s",
    635
                vec![
    636
                    field(
    637
                        "arr",
    638
                        DataType::List(Arc::new(field("item", DataType::Int32))),
    639
                    ),
    640
                    field(
    641
                        "map",
    642
                        DataType::Map(
    643
                            Arc::new(non_null_field(
    644
                                "entries",
    645
                                struct_type(vec![
    646
                                    non_null_field("keys", DataType::Utf8),
    647
                                    field("values", DataType::Int32),
    648
                                ]),
    649
                            )),
    650
                            false,
    651
                        ),
    652
                    ),
    653
                ],
    654
            );
    655
    656
            let result =
    657
                cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    658
            let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
    659
    660
            let arr = get_column_as!(&struct_array, "arr", ListArray);
    661
            assert!(!arr.is_null(0));
    662
            assert!(arr.is_null(1));
    663
            let arr0 = arr.value(0);
    664
            let values = arr0.as_any().downcast_ref::<Int32Array>().unwrap();
    665
            assert_eq!(values.value(0), 1);
    666
            assert_eq!(values.value(1), 2);
    667
    668
            let map = get_column_as!(&struct_array, "map", MapArray);
    669
            assert!(!map.is_null(0));
    670
            assert!(map.is_null(1));
    671
            let map0 = map.value(0);
    672
            let entries = map0.as_any().downcast_ref::<StructArray>().unwrap();
    673
            let keys = get_column_as!(entries, "keys", StringArray);
    674
            let vals = get_column_as!(entries, "values", Int32Array);
    675
            assert_eq!(keys.value(0), "a");
    676
            assert_eq!(vals.value(0), 1);
    677
        }
    678
    679
        #[test]
    680
        fn test_cast_struct_field_order_differs() {
    681
            let a = Arc::new(Int32Array::from(vec![Some(1), Some(2)])) as ArrayRef;
    682
            let b = Arc::new(Int32Array::from(vec![Some(3), None])) as ArrayRef;
    683
    684
            let source_struct = StructArray::from(vec![
    685
                (arc_field("a", DataType::Int32), a),
    686
                (arc_field("b", DataType::Int32), b),
    687
            ]);
    688
            let source_col = Arc::new(source_struct) as ArrayRef;
    689
    690
            let target_field = struct_field(
    691
                "s",
    692
                vec![field("b", DataType::Int64), field("a", DataType::Int32)],
    693
            );
    694
    695
            let result =
    696
                cast_column(&source_col, &target_field, &DEFAULT_CAST_OPTIONS).unwrap();
    697
            let struct_array = result.as_any().downcast_ref::<StructArray>().unwrap();
    698
    699
            let b_col = get_column_as!(&struct_array, "b", Int64Array);
    700
            assert_eq!(b_col.value(0), 3);
    701
            assert!(b_col.is_null(1));
    702
    703
            let a_col = get_column_as!(&struct_array, "a", Int32Array);
    704
            assert_eq!(a_col.value(0), 1);
    705
            assert_eq!(a_col.value(1), 2);
    706
        }
    707
    }