parquet_variant_compute/
variant_get.rs

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.
17use arrow::{
18    array::{self, Array, ArrayRef, BinaryViewArray, StructArray},
19    compute::CastOptions,
20    datatypes::Field,
21    error::Result,
22};
23use arrow_schema::{ArrowError, DataType, FieldRef};
24use parquet_variant::{VariantPath, VariantPathElement};
25
26use crate::VariantArray;
27use crate::variant_array::BorrowedShreddingState;
28use crate::variant_to_arrow::make_variant_to_arrow_row_builder;
29
30use arrow::array::AsArray;
31use std::sync::Arc;
32
33pub(crate) enum ShreddedPathStep<'a> {
34    /// Path step succeeded, return the new shredding state
35    Success(BorrowedShreddingState<'a>),
36    /// The path element is not present in the `typed_value` column and there is no `value` column,
37    /// so we know it does not exist. It, and all paths under it, are all-NULL.
38    Missing,
39    /// The path element is not present in the `typed_value` column and must be retrieved from the `value`
40    /// column instead. The caller should be prepared to handle any value, including the requested
41    /// type, an arbitrary "wrong" type, or `Variant::Null`.
42    NotShredded,
43}
44
45/// Given a shredded variant field -- a `(value?, typed_value?)` pair -- try to take one path step
46/// deeper. For a `VariantPathElement::Field`, the step fails if there is no `typed_value` at this
47/// level, or if `typed_value` is not a struct, or if the requested field name does not exist.
48///
49/// TODO: Support `VariantPathElement::Index`? It wouldn't be easy, and maybe not even possible.
50pub(crate) fn follow_shredded_path_element<'a>(
51    shredding_state: &BorrowedShreddingState<'a>,
52    path_element: &VariantPathElement<'_>,
53    cast_options: &CastOptions,
54) -> Result<ShreddedPathStep<'a>> {
55    // If the requested path element is not present in `typed_value`, and `value` is missing, then
56    // we know it does not exist; it, and all paths under it, are all-NULL.
57    let missing_path_step = || match shredding_state.value_field() {
58        Some(_) => ShreddedPathStep::NotShredded,
59        None => ShreddedPathStep::Missing,
60    };
61
62    let Some(typed_value) = shredding_state.typed_value_field() else {
63        return Ok(missing_path_step());
64    };
65
66    match path_element {
67        VariantPathElement::Field { name } => {
68            // Try to step into the requested field name of a struct.
69            // First, try to downcast to StructArray
70            let Some(struct_array) = typed_value.as_any().downcast_ref::<StructArray>() else {
71                // Downcast failure - if strict cast options are enabled, this should be an error
72                if !cast_options.safe {
73                    return Err(ArrowError::CastError(format!(
74                        "Cannot access field '{}' on non-struct type: {}",
75                        name,
76                        typed_value.data_type()
77                    )));
78                }
79                // With safe cast options, return NULL (missing_path_step)
80                return Ok(missing_path_step());
81            };
82
83            // Now try to find the column - missing column in a present struct is just missing data
84            let Some(field) = struct_array.column_by_name(name) else {
85                // Missing column in a present struct is just missing, not wrong - return Ok
86                return Ok(missing_path_step());
87            };
88
89            let struct_array = field.as_struct_opt().ok_or_else(|| {
90                // TODO: Should we blow up? Or just end the traversal and let the normal
91                // variant pathing code sort out the mess that it must anyway be
92                // prepared to handle?
93                ArrowError::InvalidArgumentError(format!(
94                    "Expected Struct array while following path, got {}",
95                    field.data_type(),
96                ))
97            })?;
98
99            let state = BorrowedShreddingState::try_from(struct_array)?;
100            Ok(ShreddedPathStep::Success(state))
101        }
102        VariantPathElement::Index { .. } => {
103            // TODO: Support array indexing. Among other things, it will require slicing not
104            // only the array we have here, but also the corresponding metadata and null masks.
105            Err(ArrowError::NotYetImplemented(
106                "Pathing into shredded variant array index".into(),
107            ))
108        }
109    }
110}
111
112/// Follows the given path as far as possible through shredded variant fields. If the path ends on a
113/// shredded field, return it directly. Otherwise, use a row shredder to follow the rest of the path
114/// and extract the requested value on a per-row basis.
115fn shredded_get_path(
116    input: &VariantArray,
117    path: &[VariantPathElement<'_>],
118    as_field: Option<&Field>,
119    cast_options: &CastOptions,
120) -> Result<ArrayRef> {
121    // Helper that creates a new VariantArray from the given nested value and typed_value columns,
122    // properly accounting for accumulated nulls from path traversal
123    let make_target_variant =
124        |value: Option<BinaryViewArray>,
125         typed_value: Option<ArrayRef>,
126         accumulated_nulls: Option<arrow::buffer::NullBuffer>| {
127            let metadata = input.metadata_field().clone();
128            VariantArray::from_parts(metadata, value, typed_value, accumulated_nulls)
129        };
130
131    // Helper that shreds a VariantArray to a specific type.
132    let shred_basic_variant =
133        |target: VariantArray, path: VariantPath<'_>, as_field: Option<&Field>| {
134            let as_type = as_field.map(|f| f.data_type());
135            let mut builder = make_variant_to_arrow_row_builder(
136                target.metadata_field(),
137                path,
138                as_type,
139                cast_options,
140                target.len(),
141            )?;
142            for i in 0..target.len() {
143                if target.is_null(i) {
144                    builder.append_null()?;
145                } else if !cast_options.safe {
146                    let value = target.try_value(i)?;
147                    builder.append_value(value)?;
148                } else {
149                    let _ = match target.try_value(i) {
150                        Ok(v) => builder.append_value(v)?,
151                        Err(_) => {
152                            builder.append_null()?;
153                            false // add this to make match arms have the same return type
154                        }
155                    };
156                }
157            }
158            builder.finish()
159        };
160
161    // Peel away the prefix of path elements that traverses the shredded parts of this variant
162    // column. Shredding will traverse the rest of the path on a per-row basis.
163    let mut shredding_state = input.shredding_state().borrow();
164    let mut accumulated_nulls = input.inner().nulls().cloned();
165    let mut path_index = 0;
166    for path_element in path {
167        match follow_shredded_path_element(&shredding_state, path_element, cast_options)? {
168            ShreddedPathStep::Success(state) => {
169                // Union nulls from the typed_value we just accessed
170                if let Some(typed_value) = shredding_state.typed_value_field() {
171                    accumulated_nulls = arrow::buffer::NullBuffer::union(
172                        accumulated_nulls.as_ref(),
173                        typed_value.nulls(),
174                    );
175                }
176                shredding_state = state;
177                path_index += 1;
178                continue;
179            }
180            ShreddedPathStep::Missing => {
181                let num_rows = input.len();
182                let arr = match as_field.map(|f| f.data_type()) {
183                    Some(data_type) => Arc::new(array::new_null_array(data_type, num_rows)) as _,
184                    None => Arc::new(array::NullArray::new(num_rows)) as _,
185                };
186                return Ok(arr);
187            }
188            ShreddedPathStep::NotShredded => {
189                let target = make_target_variant(
190                    shredding_state.value_field().cloned(),
191                    None,
192                    accumulated_nulls,
193                );
194                return shred_basic_variant(target, path[path_index..].into(), as_field);
195            }
196        };
197    }
198
199    // Path exhausted! Create a new `VariantArray` for the location we landed on.
200    let target = make_target_variant(
201        shredding_state.value_field().cloned(),
202        shredding_state.typed_value_field().cloned(),
203        accumulated_nulls,
204    );
205
206    // If our caller did not request any specific type, we can just return whatever we landed on.
207    let Some(as_field) = as_field else {
208        return Ok(ArrayRef::from(target));
209    };
210
211    // Try to return the typed value directly when we have a perfect shredding match.
212    if let Some(shredded) = try_perfect_shredding(&target, as_field) {
213        return Ok(shredded);
214    }
215
216    // Structs are special. Recurse into each field separately, hoping to follow the shredding even
217    // further, and build up the final struct from those individually shredded results.
218    if let DataType::Struct(fields) = as_field.data_type() {
219        let children = fields
220            .iter()
221            .map(|field| {
222                shredded_get_path(
223                    &target,
224                    &[VariantPathElement::from(field.name().as_str())],
225                    Some(field),
226                    cast_options,
227                )
228            })
229            .collect::<Result<Vec<_>>>()?;
230
231        let struct_nulls = target.nulls().cloned();
232
233        return Ok(Arc::new(StructArray::try_new(
234            fields.clone(),
235            children,
236            struct_nulls,
237        )?));
238    }
239
240    // Not a struct, so directly shred the variant as the requested type
241    shred_basic_variant(target, VariantPath::default(), Some(as_field))
242}
243
244fn try_perfect_shredding(variant_array: &VariantArray, as_field: &Field) -> Option<ArrayRef> {
245    // Try to return the typed value directly when we have a perfect shredding match.
246    if matches!(as_field.data_type(), DataType::Struct(_)) {
247        return None;
248    }
249    let typed_value = variant_array.typed_value_field()?;
250    if typed_value.data_type() == as_field.data_type()
251        && variant_array
252            .value_field()
253            .is_none_or(|v| v.null_count() == v.len())
254    {
255        // Here we need to gate against the case where the `typed_value` is null but data is in the `value` column.
256        // 1. If the `value` column is null, or
257        // 2. If every row in the `value` column is null
258
259        // This is a perfect shredding, where the value is entirely shredded out,
260        // so we can just return the typed value.
261        return Some(typed_value.clone());
262    }
263    None
264}
265
266/// Returns an array with the specified path extracted from the variant values.
267///
268/// The return array type depends on the `as_type` field of the options parameter
269/// 1. `as_type: None`: a VariantArray is returned. The values in this new VariantArray will point
270///    to the specified path.
271/// 2. `as_type: Some(<specific field>)`: an array of the specified type is returned.
272///
273/// TODO: How would a caller request a struct or list type where the fields/elements can be any
274/// variant? Caller can pass None as the requested type to fetch a specific path, but it would
275/// quickly become annoying (and inefficient) to call `variant_get` for each leaf value in a struct or
276/// list and then try to assemble the results.
277pub fn variant_get(input: &ArrayRef, options: GetOptions) -> Result<ArrayRef> {
278    let variant_array = VariantArray::try_new(input)?;
279
280    let GetOptions {
281        as_type,
282        path,
283        cast_options,
284    } = options;
285
286    shredded_get_path(&variant_array, &path, as_type.as_deref(), &cast_options)
287}
288
289/// Controls the action of the variant_get kernel.
290#[derive(Debug, Clone, Default)]
291pub struct GetOptions<'a> {
292    /// What path to extract
293    pub path: VariantPath<'a>,
294    /// if `as_type` is None, the returned array will itself be a VariantArray.
295    ///
296    /// if `as_type` is `Some(type)` the field is returned as the specified type.
297    pub as_type: Option<FieldRef>,
298    /// Controls the casting behavior (e.g. error vs substituting null on cast error).
299    pub cast_options: CastOptions<'a>,
300}
301
302impl<'a> GetOptions<'a> {
303    /// Construct default options to get the specified path as a variant.
304    pub fn new() -> Self {
305        Default::default()
306    }
307
308    /// Construct options to get the specified path as a variant.
309    pub fn new_with_path(path: VariantPath<'a>) -> Self {
310        Self {
311            path,
312            as_type: None,
313            cast_options: Default::default(),
314        }
315    }
316
317    /// Specify the type to return.
318    pub fn with_as_type(mut self, as_type: Option<FieldRef>) -> Self {
319        self.as_type = as_type;
320        self
321    }
322
323    /// Specify the cast options to use when casting to the specified type.
324    pub fn with_cast_options(mut self, cast_options: CastOptions<'a>) -> Self {
325        self.cast_options = cast_options;
326        self
327    }
328}
329
330#[cfg(test)]
331mod test {
332    use std::str::FromStr;
333    use std::sync::Arc;
334
335    use super::{GetOptions, variant_get};
336    use crate::variant_array::{ShreddedVariantFieldArray, StructArrayBuilder};
337    use crate::{VariantArray, VariantArrayBuilder, json_to_variant};
338    use arrow::array::{
339        Array, ArrayRef, AsArray, BinaryArray, BinaryViewArray, BooleanArray, Date32Array,
340        Date64Array, Decimal32Array, Decimal64Array, Decimal128Array, Decimal256Array,
341        Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Int64Array,
342        LargeBinaryArray, LargeStringArray, NullBuilder, StringArray, StringViewArray, StructArray,
343        Time32MillisecondArray, Time32SecondArray, Time64MicrosecondArray, Time64NanosecondArray,
344    };
345    use arrow::buffer::NullBuffer;
346    use arrow::compute::CastOptions;
347    use arrow::datatypes::DataType::{Int16, Int32, Int64};
348    use arrow::datatypes::i256;
349    use arrow::util::display::FormatOptions;
350    use arrow_schema::DataType::{Boolean, Float32, Float64, Int8};
351    use arrow_schema::{DataType, Field, FieldRef, Fields, IntervalUnit, TimeUnit};
352    use chrono::DateTime;
353    use parquet_variant::{
354        EMPTY_VARIANT_METADATA_BYTES, Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16,
355        VariantDecimalType, VariantPath,
356    };
357
358    fn single_variant_get_test(input_json: &str, path: VariantPath, expected_json: &str) {
359        // Create input array from JSON string
360        let input_array_ref: ArrayRef = Arc::new(StringArray::from(vec![Some(input_json)]));
361        let input_variant_array_ref = ArrayRef::from(json_to_variant(&input_array_ref).unwrap());
362
363        let result =
364            variant_get(&input_variant_array_ref, GetOptions::new_with_path(path)).unwrap();
365
366        // Create expected array from JSON string
367        let expected_array_ref: ArrayRef = Arc::new(StringArray::from(vec![Some(expected_json)]));
368        let expected_variant_array = json_to_variant(&expected_array_ref).unwrap();
369
370        let result_array = VariantArray::try_new(&result).unwrap();
371        assert_eq!(
372            result_array.len(),
373            1,
374            "Expected result array to have length 1"
375        );
376        assert!(
377            result_array.nulls().is_none(),
378            "Expected no nulls in result array"
379        );
380        let result_variant = result_array.value(0);
381        let expected_variant = expected_variant_array.value(0);
382        assert_eq!(
383            result_variant, expected_variant,
384            "Result variant does not match expected variant"
385        );
386    }
387
388    #[test]
389    fn get_primitive_variant_field() {
390        single_variant_get_test(
391            r#"{"some_field": 1234}"#,
392            VariantPath::from("some_field"),
393            "1234",
394        );
395    }
396
397    #[test]
398    fn get_primitive_variant_list_index() {
399        single_variant_get_test("[1234, 5678]", VariantPath::from(0), "1234");
400    }
401
402    #[test]
403    fn get_primitive_variant_inside_object_of_object() {
404        single_variant_get_test(
405            r#"{"top_level_field": {"inner_field": 1234}}"#,
406            VariantPath::from("top_level_field").join("inner_field"),
407            "1234",
408        );
409    }
410
411    #[test]
412    fn get_primitive_variant_inside_list_of_object() {
413        single_variant_get_test(
414            r#"[{"some_field": 1234}]"#,
415            VariantPath::from(0).join("some_field"),
416            "1234",
417        );
418    }
419
420    #[test]
421    fn get_primitive_variant_inside_object_of_list() {
422        single_variant_get_test(
423            r#"{"some_field": [1234]}"#,
424            VariantPath::from("some_field").join(0),
425            "1234",
426        );
427    }
428
429    #[test]
430    fn get_complex_variant() {
431        single_variant_get_test(
432            r#"{"top_level_field": {"inner_field": 1234}}"#,
433            VariantPath::from("top_level_field"),
434            r#"{"inner_field": 1234}"#,
435        );
436    }
437
438    /// Partial Shredding: extract a value as a VariantArray
439    macro_rules! numeric_partially_shredded_test {
440        ($primitive_type:ty, $data_fn:ident) => {
441            let array = $data_fn();
442            let options = GetOptions::new();
443            let result = variant_get(&array, options).unwrap();
444
445            // expect the result is a VariantArray
446            let result = VariantArray::try_new(&result).unwrap();
447            assert_eq!(result.len(), 4);
448
449            // Expect the values are the same as the original values
450            assert_eq!(
451                result.value(0),
452                Variant::from(<$primitive_type>::try_from(34u8).unwrap())
453            );
454            assert!(!result.is_valid(1));
455            assert_eq!(result.value(2), Variant::from("n/a"));
456            assert_eq!(
457                result.value(3),
458                Variant::from(<$primitive_type>::try_from(100u8).unwrap())
459            );
460        };
461    }
462
463    macro_rules! partially_shredded_variant_array_gen {
464        ($func_name:ident,  $typed_value_array_gen: expr) => {
465            fn $func_name() -> ArrayRef {
466                let (metadata, string_value) = {
467                    let mut builder = parquet_variant::VariantBuilder::new();
468                    builder.append_value("n/a");
469                    builder.finish()
470                };
471
472                let nulls = NullBuffer::from(vec![
473                    true,  // row 0 non null
474                    false, // row 1 is null
475                    true,  // row 2 non null
476                    true,  // row 3 non null
477                ]);
478
479                // metadata is the same for all rows
480                let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 4));
481
482                // See https://docs.google.com/document/d/1pw0AWoMQY3SjD7R4LgbPvMjG_xSCtXp3rZHkVp9jpZ4/edit?disco=AAABml8WQrY
483                // about why row1 is an empty but non null, value.
484                let values = BinaryViewArray::from(vec![
485                    None,                // row 0 is shredded, so no value
486                    Some(b"" as &[u8]),  // row 1 is null, so empty value (why?)
487                    Some(&string_value), // copy the string value "N/A"
488                    None,                // row 3 is shredded, so no value
489                ]);
490
491                let typed_value = $typed_value_array_gen();
492
493                let struct_array = StructArrayBuilder::new()
494                    .with_field("metadata", Arc::new(metadata), false)
495                    .with_field("typed_value", Arc::new(typed_value), true)
496                    .with_field("value", Arc::new(values), true)
497                    .with_nulls(nulls)
498                    .build();
499                ArrayRef::from(
500                    VariantArray::try_new(&struct_array).expect("should create variant array"),
501                )
502            }
503        };
504    }
505
506    #[test]
507    fn get_variant_partially_shredded_int8_as_variant() {
508        numeric_partially_shredded_test!(i8, partially_shredded_int8_variant_array);
509    }
510
511    #[test]
512    fn get_variant_partially_shredded_int16_as_variant() {
513        numeric_partially_shredded_test!(i16, partially_shredded_int16_variant_array);
514    }
515
516    #[test]
517    fn get_variant_partially_shredded_int32_as_variant() {
518        numeric_partially_shredded_test!(i32, partially_shredded_int32_variant_array);
519    }
520
521    #[test]
522    fn get_variant_partially_shredded_int64_as_variant() {
523        numeric_partially_shredded_test!(i64, partially_shredded_int64_variant_array);
524    }
525
526    #[test]
527    fn get_variant_partially_shredded_float32_as_variant() {
528        numeric_partially_shredded_test!(f32, partially_shredded_float32_variant_array);
529    }
530
531    #[test]
532    fn get_variant_partially_shredded_float64_as_variant() {
533        numeric_partially_shredded_test!(f64, partially_shredded_float64_variant_array);
534    }
535
536    #[test]
537    fn get_variant_partially_shredded_bool_as_variant() {
538        let array = partially_shredded_bool_variant_array();
539        let options = GetOptions::new();
540        let result = variant_get(&array, options).unwrap();
541
542        // expect the result is a VariantArray
543        let result = VariantArray::try_new(&result).unwrap();
544        assert_eq!(result.len(), 4);
545
546        // Expect the values are the same as the original values
547        assert_eq!(result.value(0), Variant::from(true));
548        assert!(!result.is_valid(1));
549        assert_eq!(result.value(2), Variant::from("n/a"));
550        assert_eq!(result.value(3), Variant::from(false));
551    }
552
553    #[test]
554    fn get_variant_partially_shredded_utf8_as_variant() {
555        let array = partially_shredded_utf8_variant_array();
556        let options = GetOptions::new();
557        let result = variant_get(&array, options).unwrap();
558
559        // expect the result is a VariantArray
560        let result = VariantArray::try_new(&result).unwrap();
561        assert_eq!(result.len(), 4);
562
563        // Expect the values are the same as the original values
564        assert_eq!(result.value(0), Variant::from("hello"));
565        assert!(!result.is_valid(1));
566        assert_eq!(result.value(2), Variant::from("n/a"));
567        assert_eq!(result.value(3), Variant::from("world"));
568    }
569
570    partially_shredded_variant_array_gen!(partially_shredded_binary_view_variant_array, || {
571        BinaryViewArray::from(vec![
572            Some(&[1u8, 2u8, 3u8][..]), // row 0 is shredded
573            None,                       // row 1 is null
574            None,                       // row 2 is a string
575            Some(&[4u8, 5u8, 6u8][..]), // row 3 is shredded
576        ])
577    });
578
579    #[test]
580    fn get_variant_partially_shredded_date32_as_variant() {
581        let array = partially_shredded_date32_variant_array();
582        let options = GetOptions::new();
583        let result = variant_get(&array, options).unwrap();
584
585        // expect the result is a VariantArray
586        let result = VariantArray::try_new(&result).unwrap();
587        assert_eq!(result.len(), 4);
588
589        // Expect the values are the same as the original values
590        use chrono::NaiveDate;
591        let date1 = NaiveDate::from_ymd_opt(2025, 9, 17).unwrap();
592        let date2 = NaiveDate::from_ymd_opt(2025, 9, 9).unwrap();
593        assert_eq!(result.value(0), Variant::from(date1));
594        assert!(!result.is_valid(1));
595        assert_eq!(result.value(2), Variant::from("n/a"));
596        assert_eq!(result.value(3), Variant::from(date2));
597    }
598
599    #[test]
600    fn get_variant_partially_shredded_binary_view_as_variant() {
601        let array = partially_shredded_binary_view_variant_array();
602        let options = GetOptions::new();
603        let result = variant_get(&array, options).unwrap();
604
605        // expect the result is a VariantArray
606        let result = VariantArray::try_new(&result).unwrap();
607        assert_eq!(result.len(), 4);
608
609        // Expect the values are the same as the original values
610        assert_eq!(result.value(0), Variant::from(&[1u8, 2u8, 3u8][..]));
611        assert!(!result.is_valid(1));
612        assert_eq!(result.value(2), Variant::from("n/a"));
613        assert_eq!(result.value(3), Variant::from(&[4u8, 5u8, 6u8][..]));
614    }
615
616    /// Shredding: extract a value as an Int32Array
617    #[test]
618    fn get_variant_shredded_int32_as_int32_safe_cast() {
619        // Extract the typed value as Int32Array
620        let array = partially_shredded_int32_variant_array();
621        // specify we want the typed value as Int32
622        let field = Field::new("typed_value", DataType::Int32, true);
623        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
624        let result = variant_get(&array, options).unwrap();
625        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
626            Some(34),
627            None,
628            None, // "n/a" is not an Int32 so converted to null
629            Some(100),
630        ]));
631        assert_eq!(&result, &expected)
632    }
633
634    /// Shredding: extract a value as an Int32Array, unsafe cast (should error on "n/a")
635    #[test]
636    fn get_variant_shredded_int32_as_int32_unsafe_cast() {
637        // Extract the typed value as Int32Array
638        let array = partially_shredded_int32_variant_array();
639        let field = Field::new("typed_value", DataType::Int32, true);
640        let cast_options = CastOptions {
641            safe: false, // unsafe cast
642            ..Default::default()
643        };
644        let options = GetOptions::new()
645            .with_as_type(Some(FieldRef::from(field)))
646            .with_cast_options(cast_options);
647
648        let err = variant_get(&array, options).unwrap_err();
649        // TODO make this error message nicer (not Debug format)
650        assert_eq!(
651            err.to_string(),
652            "Cast error: Failed to extract primitive of type Int32 from variant ShortString(ShortString(\"n/a\")) at path VariantPath([])"
653        );
654    }
655
656    /// Perfect Shredding: extract the typed value as a VariantArray
657    macro_rules! numeric_perfectly_shredded_test {
658        ($primitive_type:ty, $data_fn:ident) => {
659            let array = $data_fn();
660            let options = GetOptions::new();
661            let result = variant_get(&array, options).unwrap();
662
663            // expect the result is a VariantArray
664            let result = VariantArray::try_new(&result).unwrap();
665            assert_eq!(result.len(), 3);
666
667            // Expect the values are the same as the original values
668            assert_eq!(
669                result.value(0),
670                Variant::from(<$primitive_type>::try_from(1u8).unwrap())
671            );
672            assert_eq!(
673                result.value(1),
674                Variant::from(<$primitive_type>::try_from(2u8).unwrap())
675            );
676            assert_eq!(
677                result.value(2),
678                Variant::from(<$primitive_type>::try_from(3u8).unwrap())
679            );
680        };
681    }
682
683    #[test]
684    fn get_variant_perfectly_shredded_int8_as_variant() {
685        numeric_perfectly_shredded_test!(i8, perfectly_shredded_int8_variant_array);
686    }
687
688    #[test]
689    fn get_variant_perfectly_shredded_int16_as_variant() {
690        numeric_perfectly_shredded_test!(i16, perfectly_shredded_int16_variant_array);
691    }
692
693    #[test]
694    fn get_variant_perfectly_shredded_int32_as_variant() {
695        numeric_perfectly_shredded_test!(i32, perfectly_shredded_int32_variant_array);
696    }
697
698    #[test]
699    fn get_variant_perfectly_shredded_int64_as_variant() {
700        numeric_perfectly_shredded_test!(i64, perfectly_shredded_int64_variant_array);
701    }
702
703    #[test]
704    fn get_variant_perfectly_shredded_float32_as_variant() {
705        numeric_perfectly_shredded_test!(f32, perfectly_shredded_float32_variant_array);
706    }
707
708    #[test]
709    fn get_variant_perfectly_shredded_float64_as_variant() {
710        numeric_perfectly_shredded_test!(f64, perfectly_shredded_float64_variant_array);
711    }
712
713    /// AllNull: extract a value as a VariantArray
714    #[test]
715    fn get_variant_all_null_as_variant() {
716        let array = all_null_variant_array();
717        let options = GetOptions::new();
718        let result = variant_get(&array, options).unwrap();
719
720        // expect the result is a VariantArray
721        let result = VariantArray::try_new(&result).unwrap();
722        assert_eq!(result.len(), 3);
723
724        // All values should be null
725        assert!(!result.is_valid(0));
726        assert!(!result.is_valid(1));
727        assert!(!result.is_valid(2));
728    }
729
730    /// AllNull: extract a value as an Int32Array
731    #[test]
732    fn get_variant_all_null_as_int32() {
733        let array = all_null_variant_array();
734        // specify we want the typed value as Int32
735        let field = Field::new("typed_value", DataType::Int32, true);
736        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
737        let result = variant_get(&array, options).unwrap();
738
739        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
740            Option::<i32>::None,
741            Option::<i32>::None,
742            Option::<i32>::None,
743        ]));
744        assert_eq!(&result, &expected)
745    }
746
747    macro_rules! perfectly_shredded_to_arrow_primitive_test {
748        ($name:ident, $primitive_type:expr, $perfectly_shredded_array_gen_fun:ident, $expected_array:expr) => {
749            #[test]
750            fn $name() {
751                let array = $perfectly_shredded_array_gen_fun();
752                let field = Field::new("typed_value", $primitive_type, true);
753                let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
754                let result = variant_get(&array, options).unwrap();
755                let expected_array: ArrayRef = Arc::new($expected_array);
756                assert_eq!(&result, &expected_array);
757            }
758        };
759    }
760
761    perfectly_shredded_to_arrow_primitive_test!(
762        get_variant_perfectly_shredded_int18_as_int8,
763        Int8,
764        perfectly_shredded_int8_variant_array,
765        Int8Array::from(vec![Some(1), Some(2), Some(3)])
766    );
767
768    perfectly_shredded_to_arrow_primitive_test!(
769        get_variant_perfectly_shredded_int16_as_int16,
770        Int16,
771        perfectly_shredded_int16_variant_array,
772        Int16Array::from(vec![Some(1), Some(2), Some(3)])
773    );
774
775    perfectly_shredded_to_arrow_primitive_test!(
776        get_variant_perfectly_shredded_int32_as_int32,
777        Int32,
778        perfectly_shredded_int32_variant_array,
779        Int32Array::from(vec![Some(1), Some(2), Some(3)])
780    );
781
782    perfectly_shredded_to_arrow_primitive_test!(
783        get_variant_perfectly_shredded_int64_as_int64,
784        Int64,
785        perfectly_shredded_int64_variant_array,
786        Int64Array::from(vec![Some(1), Some(2), Some(3)])
787    );
788
789    perfectly_shredded_to_arrow_primitive_test!(
790        get_variant_perfectly_shredded_float32_as_float32,
791        Float32,
792        perfectly_shredded_float32_variant_array,
793        Float32Array::from(vec![Some(1.0), Some(2.0), Some(3.0)])
794    );
795
796    perfectly_shredded_to_arrow_primitive_test!(
797        get_variant_perfectly_shredded_float64_as_float64,
798        Float64,
799        perfectly_shredded_float64_variant_array,
800        Float64Array::from(vec![Some(1.0), Some(2.0), Some(3.0)])
801    );
802
803    perfectly_shredded_to_arrow_primitive_test!(
804        get_variant_perfectly_shredded_boolean_as_boolean,
805        Boolean,
806        perfectly_shredded_bool_variant_array,
807        BooleanArray::from(vec![Some(true), Some(false), Some(true)])
808    );
809
810    perfectly_shredded_to_arrow_primitive_test!(
811        get_variant_perfectly_shredded_utf8_as_utf8,
812        DataType::Utf8,
813        perfectly_shredded_utf8_variant_array,
814        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
815    );
816
817    perfectly_shredded_to_arrow_primitive_test!(
818        get_variant_perfectly_shredded_large_utf8_as_utf8,
819        DataType::Utf8,
820        perfectly_shredded_large_utf8_variant_array,
821        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
822    );
823
824    perfectly_shredded_to_arrow_primitive_test!(
825        get_variant_perfectly_shredded_utf8_view_as_utf8,
826        DataType::Utf8,
827        perfectly_shredded_utf8_view_variant_array,
828        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
829    );
830
831    macro_rules! perfectly_shredded_variant_array_fn {
832        ($func:ident, $typed_value_gen:expr) => {
833            fn $func() -> ArrayRef {
834                // At the time of writing, the `VariantArrayBuilder` does not support shredding.
835                // so we must construct the array manually.  see https://github.com/apache/arrow-rs/issues/7895
836                let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(
837                    EMPTY_VARIANT_METADATA_BYTES,
838                    3,
839                ));
840                let typed_value = $typed_value_gen();
841
842                let struct_array = StructArrayBuilder::new()
843                    .with_field("metadata", Arc::new(metadata), false)
844                    .with_field("typed_value", Arc::new(typed_value), true)
845                    .build();
846
847                VariantArray::try_new(&struct_array)
848                    .expect("should create variant array")
849                    .into()
850            }
851        };
852    }
853
854    perfectly_shredded_variant_array_fn!(perfectly_shredded_utf8_variant_array, || {
855        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
856    });
857
858    perfectly_shredded_variant_array_fn!(perfectly_shredded_large_utf8_variant_array, || {
859        LargeStringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
860    });
861
862    perfectly_shredded_variant_array_fn!(perfectly_shredded_utf8_view_variant_array, || {
863        StringViewArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
864    });
865
866    perfectly_shredded_variant_array_fn!(perfectly_shredded_bool_variant_array, || {
867        BooleanArray::from(vec![Some(true), Some(false), Some(true)])
868    });
869
870    /// Return a VariantArray that represents a perfectly "shredded" variant
871    /// for the given typed value.
872    ///
873    /// The schema of the corresponding `StructArray` would look like this:
874    ///
875    /// ```text
876    /// StructArray {
877    ///   metadata: BinaryViewArray,
878    ///   typed_value: Int32Array,
879    /// }
880    /// ```
881    macro_rules! numeric_perfectly_shredded_variant_array_fn {
882        ($func:ident, $array_type:ident, $primitive_type:ty) => {
883            perfectly_shredded_variant_array_fn!($func, || {
884                $array_type::from(vec![
885                    Some(<$primitive_type>::try_from(1u8).unwrap()),
886                    Some(<$primitive_type>::try_from(2u8).unwrap()),
887                    Some(<$primitive_type>::try_from(3u8).unwrap()),
888                ])
889            });
890        };
891    }
892
893    numeric_perfectly_shredded_variant_array_fn!(
894        perfectly_shredded_int8_variant_array,
895        Int8Array,
896        i8
897    );
898    numeric_perfectly_shredded_variant_array_fn!(
899        perfectly_shredded_int16_variant_array,
900        Int16Array,
901        i16
902    );
903    numeric_perfectly_shredded_variant_array_fn!(
904        perfectly_shredded_int32_variant_array,
905        Int32Array,
906        i32
907    );
908    numeric_perfectly_shredded_variant_array_fn!(
909        perfectly_shredded_int64_variant_array,
910        Int64Array,
911        i64
912    );
913    numeric_perfectly_shredded_variant_array_fn!(
914        perfectly_shredded_float32_variant_array,
915        Float32Array,
916        f32
917    );
918    numeric_perfectly_shredded_variant_array_fn!(
919        perfectly_shredded_float64_variant_array,
920        Float64Array,
921        f64
922    );
923
924    perfectly_shredded_variant_array_fn!(
925        perfectly_shredded_timestamp_micro_ntz_variant_array,
926        || {
927            arrow::array::TimestampMicrosecondArray::from(vec![
928                Some(-456000),
929                Some(1758602096000001),
930                Some(1758602096000002),
931            ])
932        }
933    );
934
935    perfectly_shredded_to_arrow_primitive_test!(
936        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_micro_ntz,
937        DataType::Timestamp(TimeUnit::Microsecond, None),
938        perfectly_shredded_timestamp_micro_ntz_variant_array,
939        arrow::array::TimestampMicrosecondArray::from(vec![
940            Some(-456000),
941            Some(1758602096000001),
942            Some(1758602096000002),
943        ])
944    );
945
946    // test converting micro to nano
947    perfectly_shredded_to_arrow_primitive_test!(
948        get_variant_perfectly_shredded_timestamp_micro_ntz_as_nano_ntz,
949        DataType::Timestamp(TimeUnit::Nanosecond, None),
950        perfectly_shredded_timestamp_micro_ntz_variant_array,
951        arrow::array::TimestampNanosecondArray::from(vec![
952            Some(-456000000),
953            Some(1758602096000001000),
954            Some(1758602096000002000)
955        ])
956    );
957
958    perfectly_shredded_variant_array_fn!(perfectly_shredded_timestamp_micro_variant_array, || {
959        arrow::array::TimestampMicrosecondArray::from(vec![
960            Some(-456000),
961            Some(1758602096000001),
962            Some(1758602096000002),
963        ])
964        .with_timezone("+00:00")
965    });
966
967    perfectly_shredded_to_arrow_primitive_test!(
968        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_micro,
969        DataType::Timestamp(TimeUnit::Microsecond, Some(Arc::from("+00:00"))),
970        perfectly_shredded_timestamp_micro_variant_array,
971        arrow::array::TimestampMicrosecondArray::from(vec![
972            Some(-456000),
973            Some(1758602096000001),
974            Some(1758602096000002),
975        ])
976        .with_timezone("+00:00")
977    );
978
979    // test converting micro to nano
980    perfectly_shredded_to_arrow_primitive_test!(
981        get_variant_perfectly_shredded_timestamp_micro_as_nano,
982        DataType::Timestamp(TimeUnit::Nanosecond, Some(Arc::from("+00:00"))),
983        perfectly_shredded_timestamp_micro_variant_array,
984        arrow::array::TimestampNanosecondArray::from(vec![
985            Some(-456000000),
986            Some(1758602096000001000),
987            Some(1758602096000002000)
988        ])
989        .with_timezone("+00:00")
990    );
991
992    perfectly_shredded_variant_array_fn!(
993        perfectly_shredded_timestamp_nano_ntz_variant_array,
994        || {
995            arrow::array::TimestampNanosecondArray::from(vec![
996                Some(-4999999561),
997                Some(1758602096000000001),
998                Some(1758602096000000002),
999            ])
1000        }
1001    );
1002
1003    perfectly_shredded_variant_array_fn!(
1004        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1005        || {
1006            arrow::array::TimestampMicrosecondArray::from(vec![
1007                Some(1234),       // can't be cast to second & millisecond
1008                Some(1234000),    // can be cast to millisecond, but not second
1009                Some(1234000000), // can be cast to second & millisecond
1010            ])
1011            .with_timezone("+00:00")
1012        }
1013    );
1014
1015    // The following two tests wants to cover the micro with timezone -> milli/second cases
1016    // there are three test items, which contains some items can be cast safely, and some can't
1017    perfectly_shredded_to_arrow_primitive_test!(
1018        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_second,
1019        DataType::Timestamp(TimeUnit::Second, Some(Arc::from("+00:00"))),
1020        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1021        arrow::array::TimestampSecondArray::from(vec![
1022            None,
1023            None, // Return None if can't be cast to second safely
1024            Some(1234)
1025        ])
1026        .with_timezone("+00:00")
1027    );
1028
1029    perfectly_shredded_to_arrow_primitive_test!(
1030        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_milli,
1031        DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from("+00:00"))),
1032        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1033        arrow::array::TimestampMillisecondArray::from(vec![
1034            None, // Return None if can't be cast to millisecond safely
1035            Some(1234),
1036            Some(1234000)
1037        ])
1038        .with_timezone("+00:00")
1039    );
1040
1041    perfectly_shredded_variant_array_fn!(
1042        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1043        || {
1044            arrow::array::TimestampMicrosecondArray::from(vec![
1045                Some(1234),       // can't be cast to second & millisecond
1046                Some(1234000),    // can be cast to millisecond, but not second
1047                Some(1234000000), // can be cast to second & millisecond
1048            ])
1049        }
1050    );
1051
1052    // The following two tests wants to cover the micro_ntz -> milli/second cases
1053    // there are three test items, which contains some items can be cast safely, and some can't
1054    perfectly_shredded_to_arrow_primitive_test!(
1055        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_second,
1056        DataType::Timestamp(TimeUnit::Second, None),
1057        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1058        arrow::array::TimestampSecondArray::from(vec![
1059            None,
1060            None, // Return None if can't be cast to second safely
1061            Some(1234)
1062        ])
1063    );
1064
1065    perfectly_shredded_to_arrow_primitive_test!(
1066        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_milli,
1067        DataType::Timestamp(TimeUnit::Millisecond, None),
1068        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1069        arrow::array::TimestampMillisecondArray::from(vec![
1070            None, // Return None if can't be cast to millisecond safely
1071            Some(1234),
1072            Some(1234000)
1073        ])
1074    );
1075
1076    perfectly_shredded_variant_array_fn!(
1077        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1078        || {
1079            arrow::array::TimestampNanosecondArray::from(vec![
1080                Some(1234000),       // can't be cast to second & millisecond
1081                Some(1234000000),    // can be cast to millisecond, but not second
1082                Some(1234000000000), // can be cast to second & millisecond
1083            ])
1084            .with_timezone("+00:00")
1085        }
1086    );
1087
1088    // The following two tests wants to cover the nano with timezone -> milli/second cases
1089    // there are three test items, which contains some items can be cast safely, and some can't
1090    perfectly_shredded_to_arrow_primitive_test!(
1091        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_second,
1092        DataType::Timestamp(TimeUnit::Second, Some(Arc::from("+00:00"))),
1093        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1094        arrow::array::TimestampSecondArray::from(vec![
1095            None,
1096            None, // Return None if can't be cast to second safely
1097            Some(1234)
1098        ])
1099        .with_timezone("+00:00")
1100    );
1101
1102    perfectly_shredded_to_arrow_primitive_test!(
1103        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_milli,
1104        DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from("+00:00"))),
1105        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1106        arrow::array::TimestampMillisecondArray::from(vec![
1107            None, // Return None if can't be cast to millisecond safely
1108            Some(1234),
1109            Some(1234000)
1110        ])
1111        .with_timezone("+00:00")
1112    );
1113
1114    perfectly_shredded_variant_array_fn!(
1115        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1116        || {
1117            arrow::array::TimestampNanosecondArray::from(vec![
1118                Some(1234000),       // can't be cast to second & millisecond
1119                Some(1234000000),    // can be cast to millisecond, but not second
1120                Some(1234000000000), // can be cast to second & millisecond
1121            ])
1122        }
1123    );
1124
1125    // The following two tests wants to cover the nano_ntz -> milli/second cases
1126    // there are three test items, which contains some items can be cast safely, and some can't
1127    perfectly_shredded_to_arrow_primitive_test!(
1128        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_second,
1129        DataType::Timestamp(TimeUnit::Second, None),
1130        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1131        arrow::array::TimestampSecondArray::from(vec![
1132            None,
1133            None, // Return None if can't be cast to second safely
1134            Some(1234)
1135        ])
1136    );
1137
1138    perfectly_shredded_to_arrow_primitive_test!(
1139        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_milli,
1140        DataType::Timestamp(TimeUnit::Millisecond, None),
1141        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1142        arrow::array::TimestampMillisecondArray::from(vec![
1143            None, // Return None if can't be cast to millisecond safely
1144            Some(1234),
1145            Some(1234000)
1146        ])
1147    );
1148
1149    perfectly_shredded_to_arrow_primitive_test!(
1150        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_nano_ntz,
1151        DataType::Timestamp(TimeUnit::Nanosecond, None),
1152        perfectly_shredded_timestamp_nano_ntz_variant_array,
1153        arrow::array::TimestampNanosecondArray::from(vec![
1154            Some(-4999999561),
1155            Some(1758602096000000001),
1156            Some(1758602096000000002),
1157        ])
1158    );
1159
1160    perfectly_shredded_variant_array_fn!(perfectly_shredded_timestamp_nano_variant_array, || {
1161        arrow::array::TimestampNanosecondArray::from(vec![
1162            Some(-4999999561),
1163            Some(1758602096000000001),
1164            Some(1758602096000000002),
1165        ])
1166        .with_timezone("+00:00")
1167    });
1168
1169    perfectly_shredded_to_arrow_primitive_test!(
1170        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_nano,
1171        DataType::Timestamp(TimeUnit::Nanosecond, Some(Arc::from("+00:00"))),
1172        perfectly_shredded_timestamp_nano_variant_array,
1173        arrow::array::TimestampNanosecondArray::from(vec![
1174            Some(-4999999561),
1175            Some(1758602096000000001),
1176            Some(1758602096000000002),
1177        ])
1178        .with_timezone("+00:00")
1179    );
1180
1181    perfectly_shredded_variant_array_fn!(perfectly_shredded_date_variant_array, || {
1182        Date32Array::from(vec![Some(-12345), Some(17586), Some(20000)])
1183    });
1184
1185    perfectly_shredded_to_arrow_primitive_test!(
1186        get_variant_perfectly_shredded_date_as_date,
1187        DataType::Date32,
1188        perfectly_shredded_date_variant_array,
1189        Date32Array::from(vec![Some(-12345), Some(17586), Some(20000)])
1190    );
1191
1192    perfectly_shredded_to_arrow_primitive_test!(
1193        get_variant_perfectly_shredded_date_as_date64,
1194        DataType::Date64,
1195        perfectly_shredded_date_variant_array,
1196        Date64Array::from(vec![
1197            Some(-1066608000000),
1198            Some(1519430400000),
1199            Some(1728000000000)
1200        ])
1201    );
1202
1203    perfectly_shredded_variant_array_fn!(perfectly_shredded_time_variant_array, || {
1204        Time64MicrosecondArray::from(vec![Some(12345000), Some(87654000), Some(135792000)])
1205    });
1206
1207    perfectly_shredded_to_arrow_primitive_test!(
1208        get_variant_perfectly_shredded_time_as_time,
1209        DataType::Time64(TimeUnit::Microsecond),
1210        perfectly_shredded_time_variant_array,
1211        Time64MicrosecondArray::from(vec![Some(12345000), Some(87654000), Some(135792000)])
1212    );
1213
1214    perfectly_shredded_to_arrow_primitive_test!(
1215        get_variant_perfectly_shredded_time_as_time64_nano,
1216        DataType::Time64(TimeUnit::Nanosecond),
1217        perfectly_shredded_time_variant_array,
1218        Time64NanosecondArray::from(vec![
1219            Some(12345000000),
1220            Some(87654000000),
1221            Some(135792000000)
1222        ])
1223    );
1224
1225    perfectly_shredded_variant_array_fn!(perfectly_shredded_time_variant_array_for_time32, || {
1226        Time64MicrosecondArray::from(vec![
1227            Some(1234),        // This can't be cast to Time32 losslessly
1228            Some(7654000),     // This can be cast to Time32(Millisecond), but not Time32(Second)
1229            Some(35792000000), // This can be cast to Time32(Second) & Time32(Millisecond)
1230        ])
1231    });
1232
1233    perfectly_shredded_to_arrow_primitive_test!(
1234        get_variant_perfectly_shredded_time_as_time32_second,
1235        DataType::Time32(TimeUnit::Second),
1236        perfectly_shredded_time_variant_array_for_time32,
1237        Time32SecondArray::from(vec![
1238            None,
1239            None, // Return None if can't be cast to Time32(Second) safely
1240            Some(35792)
1241        ])
1242    );
1243
1244    perfectly_shredded_to_arrow_primitive_test!(
1245        get_variant_perfectly_shredded_time_as_time32_milli,
1246        DataType::Time32(TimeUnit::Millisecond),
1247        perfectly_shredded_time_variant_array_for_time32,
1248        Time32MillisecondArray::from(vec![
1249            None, // Return None if can't be cast to Time32(Second) safely
1250            Some(7654),
1251            Some(35792000)
1252        ])
1253    );
1254
1255    perfectly_shredded_variant_array_fn!(perfectly_shredded_null_variant_array, || {
1256        let mut builder = NullBuilder::new();
1257        builder.append_nulls(3);
1258        builder.finish()
1259    });
1260
1261    perfectly_shredded_to_arrow_primitive_test!(
1262        get_variant_perfectly_shredded_null_as_null,
1263        DataType::Null,
1264        perfectly_shredded_null_variant_array,
1265        arrow::array::NullArray::new(3)
1266    );
1267
1268    perfectly_shredded_variant_array_fn!(perfectly_shredded_null_variant_array_with_int, || {
1269        Int32Array::from(vec![Some(32), Some(64), Some(48)])
1270    });
1271
1272    // We append null values if type miss match happens in safe mode
1273    perfectly_shredded_to_arrow_primitive_test!(
1274        get_variant_perfectly_shredded_null_with_type_missmatch_in_safe_mode,
1275        DataType::Null,
1276        perfectly_shredded_null_variant_array_with_int,
1277        arrow::array::NullArray::new(3)
1278    );
1279
1280    // We'll return an error if type miss match happens in strict mode
1281    #[test]
1282    fn get_variant_perfectly_shredded_null_as_null_with_type_missmatch_in_strict_mode() {
1283        let array = perfectly_shredded_null_variant_array_with_int();
1284        let field = Field::new("typed_value", DataType::Null, true);
1285        let options = GetOptions::new()
1286            .with_as_type(Some(FieldRef::from(field)))
1287            .with_cast_options(CastOptions {
1288                safe: false,
1289                format_options: FormatOptions::default(),
1290            });
1291
1292        let result = variant_get(&array, options);
1293
1294        assert!(result.is_err());
1295        let error_msg = format!("{}", result.unwrap_err());
1296        assert!(
1297            error_msg
1298                .contains("Cast error: Failed to extract primitive of type Null from variant Int32(32) at path VariantPath([])"),
1299            "Expected=[Cast error: Failed to extract primitive of type Null from variant Int32(32) at path VariantPath([])],\
1300                Got error message=[{}]",
1301            error_msg
1302        );
1303    }
1304
1305    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal4_variant_array, || {
1306        Decimal32Array::from(vec![Some(12345), Some(23400), Some(-12342)])
1307            .with_precision_and_scale(5, 2)
1308            .unwrap()
1309    });
1310
1311    perfectly_shredded_to_arrow_primitive_test!(
1312        get_variant_perfectly_shredded_decimal4_as_decimal4,
1313        DataType::Decimal32(5, 2),
1314        perfectly_shredded_decimal4_variant_array,
1315        Decimal32Array::from(vec![Some(12345), Some(23400), Some(-12342)])
1316            .with_precision_and_scale(5, 2)
1317            .unwrap()
1318    );
1319
1320    perfectly_shredded_variant_array_fn!(
1321        perfectly_shredded_decimal8_variant_array_cast2decimal32,
1322        || {
1323            Decimal64Array::from(vec![Some(123456), Some(145678), Some(-123456)])
1324                .with_precision_and_scale(6, 1)
1325                .unwrap()
1326        }
1327    );
1328
1329    // The input will be cast to Decimal32 when transformed to Variant
1330    // This tests will covert the logic DataType::Decimal64(the original array)
1331    // -> Variant::Decimal4(VariantArray) -> DataType::Decimal64(the result array)
1332    perfectly_shredded_to_arrow_primitive_test!(
1333        get_variant_perfectly_shredded_decimal8_through_decimal32_as_decimal8,
1334        DataType::Decimal64(6, 1),
1335        perfectly_shredded_decimal8_variant_array_cast2decimal32,
1336        Decimal64Array::from(vec![Some(123456), Some(145678), Some(-123456)])
1337            .with_precision_and_scale(6, 1)
1338            .unwrap()
1339    );
1340
1341    // This tests will covert the logic DataType::Decimal64(the original array)
1342    //  -> Variant::Decimal8(VariantArray) -> DataType::Decimal64(the result array)
1343    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal8_variant_array, || {
1344        Decimal64Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1345            .with_precision_and_scale(10, 1)
1346            .unwrap()
1347    });
1348
1349    perfectly_shredded_to_arrow_primitive_test!(
1350        get_variant_perfectly_shredded_decimal8_as_decimal8,
1351        DataType::Decimal64(10, 1),
1352        perfectly_shredded_decimal8_variant_array,
1353        Decimal64Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1354            .with_precision_and_scale(10, 1)
1355            .unwrap()
1356    );
1357
1358    // This tests will covert the logic DataType::Decimal128(the original array)
1359    //  -> Variant::Decimal4(VariantArray) -> DataType::Decimal128(the result array)
1360    perfectly_shredded_variant_array_fn!(
1361        perfectly_shredded_decimal16_within_decimal4_variant_array,
1362        || {
1363            Decimal128Array::from(vec![
1364                Some(i128::from(1234589)),
1365                Some(i128::from(2344444)),
1366                Some(i128::from(-1234789)),
1367            ])
1368            .with_precision_and_scale(7, 3)
1369            .unwrap()
1370        }
1371    );
1372
1373    // This tests will covert the logic DataType::Decimal128(the original array)
1374    // -> Variant::Decimal4(VariantArray) -> DataType::Decimal128(the result array)
1375    perfectly_shredded_to_arrow_primitive_test!(
1376        get_variant_perfectly_shredded_decimal16_within_decimal4_as_decimal16,
1377        DataType::Decimal128(7, 3),
1378        perfectly_shredded_decimal16_within_decimal4_variant_array,
1379        Decimal128Array::from(vec![
1380            Some(i128::from(1234589)),
1381            Some(i128::from(2344444)),
1382            Some(i128::from(-1234789)),
1383        ])
1384        .with_precision_and_scale(7, 3)
1385        .unwrap()
1386    );
1387
1388    perfectly_shredded_variant_array_fn!(
1389        perfectly_shredded_decimal16_within_decimal8_variant_array,
1390        || {
1391            Decimal128Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1392                .with_precision_and_scale(10, 1)
1393                .unwrap()
1394        }
1395    );
1396
1397    // This tests will covert the logic DataType::Decimal128(the original array)
1398    // -> Variant::Decimal8(VariantArray) -> DataType::Decimal128(the result array)
1399    perfectly_shredded_to_arrow_primitive_test!(
1400        get_variant_perfectly_shredded_decimal16_within8_as_decimal16,
1401        DataType::Decimal128(10, 1),
1402        perfectly_shredded_decimal16_within_decimal8_variant_array,
1403        Decimal128Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1404            .with_precision_and_scale(10, 1)
1405            .unwrap()
1406    );
1407
1408    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal16_variant_array, || {
1409        Decimal128Array::from(vec![
1410            Some(i128::from_str("12345678901234567899").unwrap()),
1411            Some(i128::from_str("23445677483748324300").unwrap()),
1412            Some(i128::from_str("-12345678901234567899").unwrap()),
1413        ])
1414        .with_precision_and_scale(20, 3)
1415        .unwrap()
1416    });
1417
1418    // This tests will covert the logic DataType::Decimal128(the original array)
1419    // -> Variant::Decimal16(VariantArray) -> DataType::Decimal128(the result array)
1420    perfectly_shredded_to_arrow_primitive_test!(
1421        get_variant_perfectly_shredded_decimal16_as_decimal16,
1422        DataType::Decimal128(20, 3),
1423        perfectly_shredded_decimal16_variant_array,
1424        Decimal128Array::from(vec![
1425            Some(i128::from_str("12345678901234567899").unwrap()),
1426            Some(i128::from_str("23445677483748324300").unwrap()),
1427            Some(i128::from_str("-12345678901234567899").unwrap())
1428        ])
1429        .with_precision_and_scale(20, 3)
1430        .unwrap()
1431    );
1432
1433    macro_rules! assert_variant_get_as_variant_array_with_default_option {
1434        ($variant_array: expr, $array_expected: expr) => {{
1435            let options = GetOptions::new();
1436            let array = $variant_array;
1437            let result = variant_get(&array, options).unwrap();
1438
1439            // expect the result is a VariantArray
1440            let result = VariantArray::try_new(&result).unwrap();
1441
1442            assert_eq!(result.len(), $array_expected.len());
1443
1444            for (idx, item) in $array_expected.into_iter().enumerate() {
1445                match item {
1446                    Some(item) => assert_eq!(result.value(idx), item),
1447                    None => assert!(result.is_null(idx)),
1448                }
1449            }
1450        }};
1451    }
1452
1453    partially_shredded_variant_array_gen!(
1454        partially_shredded_timestamp_micro_ntz_variant_array,
1455        || {
1456            arrow::array::TimestampMicrosecondArray::from(vec![
1457                Some(-456000),
1458                None,
1459                None,
1460                Some(1758602096000000),
1461            ])
1462        }
1463    );
1464
1465    #[test]
1466    fn get_variant_partial_shredded_timestamp_micro_ntz_as_variant() {
1467        let array = partially_shredded_timestamp_micro_ntz_variant_array();
1468        assert_variant_get_as_variant_array_with_default_option!(
1469            array,
1470            vec![
1471                Some(Variant::from(
1472                    DateTime::from_timestamp_micros(-456000i64)
1473                        .unwrap()
1474                        .naive_utc(),
1475                )),
1476                None,
1477                Some(Variant::from("n/a")),
1478                Some(Variant::from(
1479                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
1480                        .unwrap()
1481                        .naive_utc(),
1482                )),
1483            ]
1484        )
1485    }
1486
1487    partially_shredded_variant_array_gen!(partially_shredded_timestamp_micro_variant_array, || {
1488        arrow::array::TimestampMicrosecondArray::from(vec![
1489            Some(-456000),
1490            None,
1491            None,
1492            Some(1758602096000000),
1493        ])
1494        .with_timezone("+00:00")
1495    });
1496
1497    #[test]
1498    fn get_variant_partial_shredded_timestamp_micro_as_variant() {
1499        let array = partially_shredded_timestamp_micro_variant_array();
1500        assert_variant_get_as_variant_array_with_default_option!(
1501            array,
1502            vec![
1503                Some(Variant::from(
1504                    DateTime::from_timestamp_micros(-456000i64)
1505                        .unwrap()
1506                        .to_utc(),
1507                )),
1508                None,
1509                Some(Variant::from("n/a")),
1510                Some(Variant::from(
1511                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
1512                        .unwrap()
1513                        .to_utc(),
1514                )),
1515            ]
1516        )
1517    }
1518
1519    partially_shredded_variant_array_gen!(
1520        partially_shredded_timestamp_nano_ntz_variant_array,
1521        || {
1522            arrow::array::TimestampNanosecondArray::from(vec![
1523                Some(-4999999561),
1524                None,
1525                None,
1526                Some(1758602096000000000),
1527            ])
1528        }
1529    );
1530
1531    #[test]
1532    fn get_variant_partial_shredded_timestamp_nano_ntz_as_variant() {
1533        let array = partially_shredded_timestamp_nano_ntz_variant_array();
1534
1535        assert_variant_get_as_variant_array_with_default_option!(
1536            array,
1537            vec![
1538                Some(Variant::from(
1539                    DateTime::from_timestamp(-5, 439).unwrap().naive_utc()
1540                )),
1541                None,
1542                Some(Variant::from("n/a")),
1543                Some(Variant::from(
1544                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
1545                        .unwrap()
1546                        .naive_utc()
1547                )),
1548            ]
1549        )
1550    }
1551
1552    partially_shredded_variant_array_gen!(partially_shredded_timestamp_nano_variant_array, || {
1553        arrow::array::TimestampNanosecondArray::from(vec![
1554            Some(-4999999561),
1555            None,
1556            None,
1557            Some(1758602096000000000),
1558        ])
1559        .with_timezone("+00:00")
1560    });
1561
1562    #[test]
1563    fn get_variant_partial_shredded_timestamp_nano_as_variant() {
1564        let array = partially_shredded_timestamp_nano_variant_array();
1565
1566        assert_variant_get_as_variant_array_with_default_option!(
1567            array,
1568            vec![
1569                Some(Variant::from(
1570                    DateTime::from_timestamp(-5, 439).unwrap().to_utc()
1571                )),
1572                None,
1573                Some(Variant::from("n/a")),
1574                Some(Variant::from(
1575                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
1576                        .unwrap()
1577                        .to_utc()
1578                )),
1579            ]
1580        )
1581    }
1582
1583    perfectly_shredded_variant_array_fn!(perfectly_shredded_binary_variant_array, || {
1584        BinaryArray::from(vec![
1585            Some(b"Apache" as &[u8]),
1586            Some(b"Arrow-rs" as &[u8]),
1587            Some(b"Parquet-variant" as &[u8]),
1588        ])
1589    });
1590
1591    perfectly_shredded_to_arrow_primitive_test!(
1592        get_variant_perfectly_shredded_binary_as_binary,
1593        DataType::Binary,
1594        perfectly_shredded_binary_variant_array,
1595        BinaryArray::from(vec![
1596            Some(b"Apache" as &[u8]),
1597            Some(b"Arrow-rs" as &[u8]),
1598            Some(b"Parquet-variant" as &[u8]),
1599        ])
1600    );
1601
1602    perfectly_shredded_variant_array_fn!(perfectly_shredded_large_binary_variant_array, || {
1603        LargeBinaryArray::from(vec![
1604            Some(b"Apache" as &[u8]),
1605            Some(b"Arrow-rs" as &[u8]),
1606            Some(b"Parquet-variant" as &[u8]),
1607        ])
1608    });
1609
1610    perfectly_shredded_to_arrow_primitive_test!(
1611        get_variant_perfectly_shredded_large_binary_as_large_binary,
1612        DataType::LargeBinary,
1613        perfectly_shredded_large_binary_variant_array,
1614        LargeBinaryArray::from(vec![
1615            Some(b"Apache" as &[u8]),
1616            Some(b"Arrow-rs" as &[u8]),
1617            Some(b"Parquet-variant" as &[u8]),
1618        ])
1619    );
1620
1621    perfectly_shredded_variant_array_fn!(perfectly_shredded_binary_view_variant_array, || {
1622        BinaryViewArray::from(vec![
1623            Some(b"Apache" as &[u8]),
1624            Some(b"Arrow-rs" as &[u8]),
1625            Some(b"Parquet-variant" as &[u8]),
1626        ])
1627    });
1628
1629    perfectly_shredded_to_arrow_primitive_test!(
1630        get_variant_perfectly_shredded_binary_view_as_binary_view,
1631        DataType::BinaryView,
1632        perfectly_shredded_binary_view_variant_array,
1633        BinaryViewArray::from(vec![
1634            Some(b"Apache" as &[u8]),
1635            Some(b"Arrow-rs" as &[u8]),
1636            Some(b"Parquet-variant" as &[u8]),
1637        ])
1638    );
1639
1640    /// Return a VariantArray that represents a normal "shredded" variant
1641    /// for the following example
1642    ///
1643    /// Based on the example from [the doc]
1644    ///
1645    /// [the doc]: https://docs.google.com/document/d/1pw0AWoMQY3SjD7R4LgbPvMjG_xSCtXp3rZHkVp9jpZ4/edit?tab=t.0
1646    ///
1647    /// ```text
1648    /// 34
1649    /// null (an Arrow NULL, not a Variant::Null)
1650    /// "n/a" (a string)
1651    /// 100
1652    /// ```
1653    ///
1654    /// The schema of the corresponding `StructArray` would look like this:
1655    ///
1656    /// ```text
1657    /// StructArray {
1658    ///   metadata: BinaryViewArray,
1659    ///   value: BinaryViewArray,
1660    ///   typed_value: Int32Array,
1661    /// }
1662    /// ```
1663    macro_rules! numeric_partially_shredded_variant_array_fn {
1664        ($func:ident, $array_type:ident, $primitive_type:ty) => {
1665            partially_shredded_variant_array_gen!($func, || $array_type::from(vec![
1666                Some(<$primitive_type>::try_from(34u8).unwrap()), // row 0 is shredded, so it has a value
1667                None,                                             // row 1 is null, so no value
1668                None, // row 2 is a string, so no typed value
1669                Some(<$primitive_type>::try_from(100u8).unwrap()), // row 3 is shredded, so it has a value
1670            ]));
1671        };
1672    }
1673
1674    macro_rules! partially_shredded_variant_array_gen {
1675        ($func:ident, $typed_array_gen: expr) => {
1676            fn $func() -> ArrayRef {
1677                // At the time of writing, the `VariantArrayBuilder` does not support shredding.
1678                // so we must construct the array manually.  see https://github.com/apache/arrow-rs/issues/7895
1679                let (metadata, string_value) = {
1680                    let mut builder = parquet_variant::VariantBuilder::new();
1681                    builder.append_value("n/a");
1682                    builder.finish()
1683                };
1684
1685                let nulls = NullBuffer::from(vec![
1686                    true,  // row 0 non null
1687                    false, // row 1 is null
1688                    true,  // row 2 non null
1689                    true,  // row 3 non null
1690                ]);
1691
1692                // metadata is the same for all rows
1693                let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 4));
1694
1695                // See https://docs.google.com/document/d/1pw0AWoMQY3SjD7R4LgbPvMjG_xSCtXp3rZHkVp9jpZ4/edit?disco=AAABml8WQrY
1696                // about why row1 is an empty but non null, value.
1697                let values = BinaryViewArray::from(vec![
1698                    None,                // row 0 is shredded, so no value
1699                    Some(b"" as &[u8]),  // row 1 is null, so empty value (why?)
1700                    Some(&string_value), // copy the string value "N/A"
1701                    None,                // row 3 is shredded, so no value
1702                ]);
1703
1704                let typed_value = $typed_array_gen();
1705
1706                let struct_array = StructArrayBuilder::new()
1707                    .with_field("metadata", Arc::new(metadata), false)
1708                    .with_field("typed_value", Arc::new(typed_value), true)
1709                    .with_field("value", Arc::new(values), true)
1710                    .with_nulls(nulls)
1711                    .build();
1712
1713                ArrayRef::from(
1714                    VariantArray::try_new(&struct_array).expect("should create variant array"),
1715                )
1716            }
1717        };
1718    }
1719
1720    numeric_partially_shredded_variant_array_fn!(
1721        partially_shredded_int8_variant_array,
1722        Int8Array,
1723        i8
1724    );
1725    numeric_partially_shredded_variant_array_fn!(
1726        partially_shredded_int16_variant_array,
1727        Int16Array,
1728        i16
1729    );
1730    numeric_partially_shredded_variant_array_fn!(
1731        partially_shredded_int32_variant_array,
1732        Int32Array,
1733        i32
1734    );
1735    numeric_partially_shredded_variant_array_fn!(
1736        partially_shredded_int64_variant_array,
1737        Int64Array,
1738        i64
1739    );
1740    numeric_partially_shredded_variant_array_fn!(
1741        partially_shredded_float32_variant_array,
1742        Float32Array,
1743        f32
1744    );
1745    numeric_partially_shredded_variant_array_fn!(
1746        partially_shredded_float64_variant_array,
1747        Float64Array,
1748        f64
1749    );
1750
1751    partially_shredded_variant_array_gen!(partially_shredded_bool_variant_array, || {
1752        arrow::array::BooleanArray::from(vec![
1753            Some(true),  // row 0 is shredded, so it has a value
1754            None,        // row 1 is null, so no value
1755            None,        // row 2 is a string, so no typed value
1756            Some(false), // row 3 is shredded, so it has a value
1757        ])
1758    });
1759
1760    partially_shredded_variant_array_gen!(partially_shredded_utf8_variant_array, || {
1761        StringArray::from(vec![
1762            Some("hello"), // row 0 is shredded
1763            None,          // row 1 is null
1764            None,          // row 2 is a string
1765            Some("world"), // row 3 is shredded
1766        ])
1767    });
1768
1769    partially_shredded_variant_array_gen!(partially_shredded_date32_variant_array, || {
1770        Date32Array::from(vec![
1771            Some(20348), // row 0 is shredded, 2025-09-17
1772            None,        // row 1 is null
1773            None,        // row 2 is a string, not a date
1774            Some(20340), // row 3 is shredded, 2025-09-09
1775        ])
1776    });
1777
1778    /// Return a VariantArray that represents an "all null" variant
1779    /// for the following example (3 null values):
1780    ///
1781    /// ```text
1782    /// null
1783    /// null
1784    /// null
1785    /// ```
1786    ///
1787    /// The schema of the corresponding `StructArray` would look like this:
1788    ///
1789    /// ```text
1790    /// StructArray {
1791    ///   metadata: BinaryViewArray,
1792    /// }
1793    /// ```
1794    fn all_null_variant_array() -> ArrayRef {
1795        let nulls = NullBuffer::from(vec![
1796            false, // row 0 is null
1797            false, // row 1 is null
1798            false, // row 2 is null
1799        ]);
1800
1801        // metadata is the same for all rows (though they're all null)
1802        let metadata =
1803            BinaryViewArray::from_iter_values(std::iter::repeat_n(EMPTY_VARIANT_METADATA_BYTES, 3));
1804
1805        let struct_array = StructArrayBuilder::new()
1806            .with_field("metadata", Arc::new(metadata), false)
1807            .with_nulls(nulls)
1808            .build();
1809
1810        Arc::new(struct_array)
1811    }
1812    /// This test manually constructs a shredded variant array representing objects
1813    /// like {"x": 1, "y": "foo"} and {"x": 42} and tests extracting the "x" field
1814    /// as VariantArray using variant_get.
1815    #[test]
1816    fn test_shredded_object_field_access() {
1817        let array = shredded_object_with_x_field_variant_array();
1818
1819        // Test: Extract the "x" field as VariantArray first
1820        let options = GetOptions::new_with_path(VariantPath::from("x"));
1821        let result = variant_get(&array, options).unwrap();
1822
1823        let result_variant = VariantArray::try_new(&result).unwrap();
1824        assert_eq!(result_variant.len(), 2);
1825
1826        // Row 0: expect x=1
1827        assert_eq!(result_variant.value(0), Variant::Int32(1));
1828        // Row 1: expect x=42
1829        assert_eq!(result_variant.value(1), Variant::Int32(42));
1830    }
1831
1832    /// Test extracting shredded object field with type conversion
1833    #[test]
1834    fn test_shredded_object_field_as_int32() {
1835        let array = shredded_object_with_x_field_variant_array();
1836
1837        // Test: Extract the "x" field as Int32Array (type conversion)
1838        let field = Field::new("x", DataType::Int32, false);
1839        let options = GetOptions::new_with_path(VariantPath::from("x"))
1840            .with_as_type(Some(FieldRef::from(field)));
1841        let result = variant_get(&array, options).unwrap();
1842
1843        // Should get Int32Array
1844        let expected: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(42)]));
1845        assert_eq!(&result, &expected);
1846    }
1847
1848    /// Helper function to create a shredded variant array representing objects
1849    ///
1850    /// This creates an array that represents:
1851    /// Row 0: {"x": 1, "y": "foo"}  (x is shredded, y is in value field)
1852    /// Row 1: {"x": 42}             (x is shredded, perfect shredding)
1853    ///
1854    /// The physical layout follows the shredding spec where:
1855    /// - metadata: contains object metadata
1856    /// - typed_value: StructArray with field "x" (ShreddedVariantFieldArray)
1857    /// - value: contains fallback for unshredded fields like {"y": "foo"}
1858    /// - The "x" field has typed_value=Int32Array and value=NULL (perfect shredding)
1859    fn shredded_object_with_x_field_variant_array() -> ArrayRef {
1860        // Create the base metadata for objects
1861        let (metadata, y_field_value) = {
1862            let mut builder = parquet_variant::VariantBuilder::new();
1863            let mut obj = builder.new_object();
1864            obj.insert("x", Variant::Int32(42));
1865            obj.insert("y", Variant::from("foo"));
1866            obj.finish();
1867            builder.finish()
1868        };
1869
1870        // Create metadata array (same for both rows)
1871        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
1872
1873        // Create the main value field per the 3-step shredding spec:
1874        // Step 2: If field not in shredding schema, check value field
1875        // Row 0: {"y": "foo"} (y is not shredded, stays in value for step 2)
1876        // Row 1: {} (empty object - no unshredded fields)
1877        let empty_object_value = {
1878            let mut builder = parquet_variant::VariantBuilder::new();
1879            let obj = builder.new_object();
1880            obj.finish();
1881            let (_, value) = builder.finish();
1882            value
1883        };
1884
1885        let value_array = BinaryViewArray::from(vec![
1886            Some(y_field_value.as_slice()),      // Row 0 has {"y": "foo"}
1887            Some(empty_object_value.as_slice()), // Row 1 has {}
1888        ]);
1889
1890        // Create the "x" field as a ShreddedVariantFieldArray
1891        // This represents the shredded Int32 values for the "x" field
1892        let x_field_typed_value = Int32Array::from(vec![Some(1), Some(42)]);
1893
1894        // For perfect shredding of the x field, no "value" column, only typed_value
1895        let x_field_struct = StructArrayBuilder::new()
1896            .with_field("typed_value", Arc::new(x_field_typed_value), true)
1897            .build();
1898
1899        // Wrap the x field struct in a ShreddedVariantFieldArray
1900        let x_field_shredded = ShreddedVariantFieldArray::try_new(&x_field_struct)
1901            .expect("should create ShreddedVariantFieldArray");
1902
1903        // Create the main typed_value as a struct containing the "x" field
1904        let typed_value_fields = Fields::from(vec![Field::new(
1905            "x",
1906            x_field_shredded.data_type().clone(),
1907            true,
1908        )]);
1909        let typed_value_struct = StructArray::try_new(
1910            typed_value_fields,
1911            vec![ArrayRef::from(x_field_shredded)],
1912            None, // No nulls - both rows have the object structure
1913        )
1914        .unwrap();
1915
1916        // Create the main VariantArray
1917        let main_struct = StructArrayBuilder::new()
1918            .with_field("metadata", Arc::new(metadata_array), false)
1919            .with_field("value", Arc::new(value_array), true)
1920            .with_field("typed_value", Arc::new(typed_value_struct), true)
1921            .build();
1922
1923        Arc::new(main_struct)
1924    }
1925
1926    /// Simple test to check if nested paths are supported by current implementation
1927    #[test]
1928    fn test_simple_nested_path_support() {
1929        // Check: How does VariantPath parse different strings?
1930        println!("Testing path parsing:");
1931
1932        let path_x = VariantPath::from("x");
1933        let elements_x: Vec<_> = path_x.iter().collect();
1934        println!("  'x' -> {} elements: {:?}", elements_x.len(), elements_x);
1935
1936        let path_ax = VariantPath::from("a.x");
1937        let elements_ax: Vec<_> = path_ax.iter().collect();
1938        println!(
1939            "  'a.x' -> {} elements: {:?}",
1940            elements_ax.len(),
1941            elements_ax
1942        );
1943
1944        let path_ax_alt = VariantPath::from("$.a.x");
1945        let elements_ax_alt: Vec<_> = path_ax_alt.iter().collect();
1946        println!(
1947            "  '$.a.x' -> {} elements: {:?}",
1948            elements_ax_alt.len(),
1949            elements_ax_alt
1950        );
1951
1952        let path_nested = VariantPath::from("a").join("x");
1953        let elements_nested: Vec<_> = path_nested.iter().collect();
1954        println!(
1955            "  VariantPath::from('a').join('x') -> {} elements: {:?}",
1956            elements_nested.len(),
1957            elements_nested
1958        );
1959
1960        // Use your existing simple test data but try "a.x" instead of "x"
1961        let array = shredded_object_with_x_field_variant_array();
1962
1963        // Test if variant_get with REAL nested path throws not implemented error
1964        let real_nested_path = VariantPath::from("a").join("x");
1965        let options = GetOptions::new_with_path(real_nested_path);
1966        let result = variant_get(&array, options);
1967
1968        match result {
1969            Ok(_) => {
1970                println!("Nested path 'a.x' works unexpectedly!");
1971            }
1972            Err(e) => {
1973                println!("Nested path 'a.x' error: {}", e);
1974                if e.to_string().contains("Not yet implemented")
1975                    || e.to_string().contains("NotYetImplemented")
1976                {
1977                    println!("This is expected - nested paths are not implemented");
1978                    return;
1979                }
1980                // Any other error is also expected for now
1981                println!("This shows nested paths need implementation");
1982            }
1983        }
1984    }
1985
1986    /// Test comprehensive variant_get scenarios with Int32 conversion
1987    /// Test depth 0: Direct field access "x" with Int32 conversion
1988    /// Covers shredded vs non-shredded VariantArrays for simple field access
1989    #[test]
1990    fn test_depth_0_int32_conversion() {
1991        println!("=== Testing Depth 0: Direct field access ===");
1992
1993        // Non-shredded test data: [{"x": 42}, {"x": "foo"}, {"y": 10}]
1994        let unshredded_array = create_depth_0_test_data();
1995
1996        let field = Field::new("result", DataType::Int32, true);
1997        let path = VariantPath::from("x");
1998        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
1999        let result = variant_get(&unshredded_array, options).unwrap();
2000
2001        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2002            Some(42), // {"x": 42} -> 42
2003            None,     // {"x": "foo"} -> NULL (type mismatch)
2004            None,     // {"y": 10} -> NULL (field missing)
2005        ]));
2006        assert_eq!(&result, &expected);
2007        println!("Depth 0 (unshredded) passed");
2008
2009        // Shredded test data: using simplified approach based on working pattern
2010        let shredded_array = create_depth_0_shredded_test_data_simple();
2011
2012        let field = Field::new("result", DataType::Int32, true);
2013        let path = VariantPath::from("x");
2014        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2015        let result = variant_get(&shredded_array, options).unwrap();
2016
2017        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2018            Some(42), // {"x": 42} -> 42 (from typed_value)
2019            None,     // {"x": "foo"} -> NULL (type mismatch, from value field)
2020        ]));
2021        assert_eq!(&result, &expected);
2022        println!("Depth 0 (shredded) passed");
2023    }
2024
2025    /// Test depth 1: Single nested field access "a.x" with Int32 conversion
2026    /// Covers shredded vs non-shredded VariantArrays for nested field access
2027    #[test]
2028    fn test_depth_1_int32_conversion() {
2029        println!("=== Testing Depth 1: Single nested field access ===");
2030
2031        // Non-shredded test data from the GitHub issue
2032        let unshredded_array = create_nested_path_test_data();
2033
2034        let field = Field::new("result", DataType::Int32, true);
2035        let path = VariantPath::from("a.x"); // Dot notation!
2036        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2037        let result = variant_get(&unshredded_array, options).unwrap();
2038
2039        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2040            Some(55), // {"a": {"x": 55}} -> 55
2041            None,     // {"a": {"x": "foo"}} -> NULL (type mismatch)
2042        ]));
2043        assert_eq!(&result, &expected);
2044        println!("Depth 1 (unshredded) passed");
2045
2046        // Shredded test data: depth 1 nested shredding
2047        let shredded_array = create_depth_1_shredded_test_data_working();
2048
2049        let field = Field::new("result", DataType::Int32, true);
2050        let path = VariantPath::from("a.x"); // Dot notation!
2051        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2052        let result = variant_get(&shredded_array, options).unwrap();
2053
2054        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2055            Some(55), // {"a": {"x": 55}} -> 55 (from nested shredded x)
2056            None,     // {"a": {"x": "foo"}} -> NULL (type mismatch in nested value)
2057        ]));
2058        assert_eq!(&result, &expected);
2059        println!("Depth 1 (shredded) passed");
2060    }
2061
2062    /// Test depth 2: Double nested field access "a.b.x" with Int32 conversion  
2063    /// Covers shredded vs non-shredded VariantArrays for deeply nested field access
2064    #[test]
2065    fn test_depth_2_int32_conversion() {
2066        println!("=== Testing Depth 2: Double nested field access ===");
2067
2068        // Non-shredded test data: [{"a": {"b": {"x": 100}}}, {"a": {"b": {"x": "bar"}}}, {"a": {"b": {"y": 200}}}]
2069        let unshredded_array = create_depth_2_test_data();
2070
2071        let field = Field::new("result", DataType::Int32, true);
2072        let path = VariantPath::from("a.b.x"); // Double nested dot notation!
2073        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2074        let result = variant_get(&unshredded_array, options).unwrap();
2075
2076        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2077            Some(100), // {"a": {"b": {"x": 100}}} -> 100
2078            None,      // {"a": {"b": {"x": "bar"}}} -> NULL (type mismatch)
2079            None,      // {"a": {"b": {"y": 200}}} -> NULL (field missing)
2080        ]));
2081        assert_eq!(&result, &expected);
2082        println!("Depth 2 (unshredded) passed");
2083
2084        // Shredded test data: depth 2 nested shredding
2085        let shredded_array = create_depth_2_shredded_test_data_working();
2086
2087        let field = Field::new("result", DataType::Int32, true);
2088        let path = VariantPath::from("a.b.x"); // Double nested dot notation!
2089        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2090        let result = variant_get(&shredded_array, options).unwrap();
2091
2092        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2093            Some(100), // {"a": {"b": {"x": 100}}} -> 100 (from deeply nested shredded x)
2094            None,      // {"a": {"b": {"x": "bar"}}} -> NULL (type mismatch in deep value)
2095            None,      // {"a": {"b": {"y": 200}}} -> NULL (field missing in deep structure)
2096        ]));
2097        assert_eq!(&result, &expected);
2098        println!("Depth 2 (shredded) passed");
2099    }
2100
2101    /// Test that demonstrates what CURRENTLY WORKS
2102    ///
2103    /// This shows that nested path functionality does work, but only when the
2104    /// test data matches what the current implementation expects
2105    #[test]
2106    fn test_current_nested_path_functionality() {
2107        let array = shredded_object_with_x_field_variant_array();
2108
2109        // Test: Extract the "x" field (single level) - this works
2110        let single_path = VariantPath::from("x");
2111        let field = Field::new("result", DataType::Int32, true);
2112        let options =
2113            GetOptions::new_with_path(single_path).with_as_type(Some(FieldRef::from(field)));
2114        let result = variant_get(&array, options).unwrap();
2115
2116        println!("Single path 'x' works - result: {:?}", result);
2117
2118        // Test: Try nested path "a.x" - this is what we need to implement
2119        let nested_path = VariantPath::from("a").join("x");
2120        let field = Field::new("result", DataType::Int32, true);
2121        let options =
2122            GetOptions::new_with_path(nested_path).with_as_type(Some(FieldRef::from(field)));
2123        let result = variant_get(&array, options).unwrap();
2124
2125        println!("Nested path 'a.x' result: {:?}", result);
2126    }
2127
2128    /// Create test data for depth 0 (direct field access)
2129    /// [{"x": 42}, {"x": "foo"}, {"y": 10}]
2130    fn create_depth_0_test_data() -> ArrayRef {
2131        let mut builder = crate::VariantArrayBuilder::new(3);
2132
2133        // Row 1: {"x": 42}
2134        {
2135            let json_str = r#"{"x": 42}"#;
2136            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2137            if let Ok(variant_array) = json_to_variant(&string_array) {
2138                builder.append_variant(variant_array.value(0));
2139            } else {
2140                builder.append_null();
2141            }
2142        }
2143
2144        // Row 2: {"x": "foo"}
2145        {
2146            let json_str = r#"{"x": "foo"}"#;
2147            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2148            if let Ok(variant_array) = json_to_variant(&string_array) {
2149                builder.append_variant(variant_array.value(0));
2150            } else {
2151                builder.append_null();
2152            }
2153        }
2154
2155        // Row 3: {"y": 10} (missing "x" field)
2156        {
2157            let json_str = r#"{"y": 10}"#;
2158            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2159            if let Ok(variant_array) = json_to_variant(&string_array) {
2160                builder.append_variant(variant_array.value(0));
2161            } else {
2162                builder.append_null();
2163            }
2164        }
2165
2166        ArrayRef::from(builder.build())
2167    }
2168
2169    /// Create test data for depth 1 (single nested field)
2170    /// This represents the exact scenarios from the GitHub issue: "a.x"
2171    fn create_nested_path_test_data() -> ArrayRef {
2172        let mut builder = crate::VariantArrayBuilder::new(2);
2173
2174        // Row 1: {"a": {"x": 55}, "b": 42}
2175        {
2176            let json_str = r#"{"a": {"x": 55}, "b": 42}"#;
2177            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2178            if let Ok(variant_array) = json_to_variant(&string_array) {
2179                builder.append_variant(variant_array.value(0));
2180            } else {
2181                builder.append_null();
2182            }
2183        }
2184
2185        // Row 2: {"a": {"x": "foo"}, "b": 42}
2186        {
2187            let json_str = r#"{"a": {"x": "foo"}, "b": 42}"#;
2188            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2189            if let Ok(variant_array) = json_to_variant(&string_array) {
2190                builder.append_variant(variant_array.value(0));
2191            } else {
2192                builder.append_null();
2193            }
2194        }
2195
2196        ArrayRef::from(builder.build())
2197    }
2198
2199    /// Create test data for depth 2 (double nested field)
2200    /// [{"a": {"b": {"x": 100}}}, {"a": {"b": {"x": "bar"}}}, {"a": {"b": {"y": 200}}}]
2201    fn create_depth_2_test_data() -> ArrayRef {
2202        let mut builder = crate::VariantArrayBuilder::new(3);
2203
2204        // Row 1: {"a": {"b": {"x": 100}}}
2205        {
2206            let json_str = r#"{"a": {"b": {"x": 100}}}"#;
2207            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2208            if let Ok(variant_array) = json_to_variant(&string_array) {
2209                builder.append_variant(variant_array.value(0));
2210            } else {
2211                builder.append_null();
2212            }
2213        }
2214
2215        // Row 2: {"a": {"b": {"x": "bar"}}}
2216        {
2217            let json_str = r#"{"a": {"b": {"x": "bar"}}}"#;
2218            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2219            if let Ok(variant_array) = json_to_variant(&string_array) {
2220                builder.append_variant(variant_array.value(0));
2221            } else {
2222                builder.append_null();
2223            }
2224        }
2225
2226        // Row 3: {"a": {"b": {"y": 200}}} (missing "x" field)
2227        {
2228            let json_str = r#"{"a": {"b": {"y": 200}}}"#;
2229            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2230            if let Ok(variant_array) = json_to_variant(&string_array) {
2231                builder.append_variant(variant_array.value(0));
2232            } else {
2233                builder.append_null();
2234            }
2235        }
2236
2237        ArrayRef::from(builder.build())
2238    }
2239
2240    /// Create simple shredded test data for depth 0 using a simplified working pattern
2241    /// Creates 2 rows: [{"x": 42}, {"x": "foo"}] with "x" shredded where possible
2242    fn create_depth_0_shredded_test_data_simple() -> ArrayRef {
2243        // Create base metadata using the working pattern
2244        let (metadata, string_x_value) = {
2245            let mut builder = parquet_variant::VariantBuilder::new();
2246            let mut obj = builder.new_object();
2247            obj.insert("x", Variant::from("foo"));
2248            obj.finish();
2249            builder.finish()
2250        };
2251
2252        // Metadata array (same for both rows)
2253        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
2254
2255        // Value array following the 3-step shredding spec:
2256        // Row 0: {} (x is shredded, no unshredded fields)
2257        // Row 1: {"x": "foo"} (x is a string, can't be shredded to Int32)
2258        let empty_object_value = {
2259            let mut builder = parquet_variant::VariantBuilder::new();
2260            let obj = builder.new_object();
2261            obj.finish();
2262            let (_, value) = builder.finish();
2263            value
2264        };
2265
2266        let value_array = BinaryViewArray::from(vec![
2267            Some(empty_object_value.as_slice()), // Row 0: {} (x shredded out)
2268            Some(string_x_value.as_slice()),     // Row 1: {"x": "foo"} (fallback)
2269        ]);
2270
2271        // Create the "x" field as a ShreddedVariantFieldArray
2272        let x_field_typed_value = Int32Array::from(vec![Some(42), None]);
2273
2274        // For the x field, only typed_value (perfect shredding when possible)
2275        let x_field_struct = StructArrayBuilder::new()
2276            .with_field("typed_value", Arc::new(x_field_typed_value), true)
2277            .build();
2278
2279        let x_field_shredded = ShreddedVariantFieldArray::try_new(&x_field_struct)
2280            .expect("should create ShreddedVariantFieldArray");
2281
2282        // Create the main typed_value as a struct containing the "x" field
2283        let typed_value_fields = Fields::from(vec![Field::new(
2284            "x",
2285            x_field_shredded.data_type().clone(),
2286            true,
2287        )]);
2288        let typed_value_struct = StructArray::try_new(
2289            typed_value_fields,
2290            vec![ArrayRef::from(x_field_shredded)],
2291            None,
2292        )
2293        .unwrap();
2294
2295        // Build final VariantArray
2296        let struct_array = StructArrayBuilder::new()
2297            .with_field("metadata", Arc::new(metadata_array), false)
2298            .with_field("value", Arc::new(value_array), true)
2299            .with_field("typed_value", Arc::new(typed_value_struct), true)
2300            .build();
2301
2302        Arc::new(struct_array)
2303    }
2304
2305    /// Create working depth 1 shredded test data based on the existing working pattern
2306    /// This creates a properly structured shredded variant for "a.x" where:
2307    /// - Row 0: {"a": {"x": 55}, "b": 42} with a.x shredded into typed_value
2308    /// - Row 1: {"a": {"x": "foo"}, "b": 42} with a.x fallback to value field due to type mismatch
2309    fn create_depth_1_shredded_test_data_working() -> ArrayRef {
2310        // Create metadata following the working pattern from shredded_object_with_x_field_variant_array
2311        let (metadata, _) = {
2312            // Create nested structure: {"a": {"x": 55}, "b": 42}
2313            let mut builder = parquet_variant::VariantBuilder::new();
2314            let mut obj = builder.new_object();
2315
2316            // Create the nested "a" object
2317            let mut a_obj = obj.new_object("a");
2318            a_obj.insert("x", Variant::Int32(55));
2319            a_obj.finish();
2320
2321            obj.insert("b", Variant::Int32(42));
2322            obj.finish();
2323            builder.finish()
2324        };
2325
2326        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
2327
2328        // Create value arrays for the fallback case
2329        // Following the spec: if field cannot be shredded, it stays in value
2330        let empty_object_value = {
2331            let mut builder = parquet_variant::VariantBuilder::new();
2332            let obj = builder.new_object();
2333            obj.finish();
2334            let (_, value) = builder.finish();
2335            value
2336        };
2337
2338        // Row 1 fallback: use the working pattern from the existing shredded test
2339        // This avoids metadata issues by using the simple fallback approach
2340        let row1_fallback = {
2341            let mut builder = parquet_variant::VariantBuilder::new();
2342            let mut obj = builder.new_object();
2343            obj.insert("fallback", Variant::from("data"));
2344            obj.finish();
2345            let (_, value) = builder.finish();
2346            value
2347        };
2348
2349        let value_array = BinaryViewArray::from(vec![
2350            Some(empty_object_value.as_slice()), // Row 0: {} (everything shredded except b in unshredded fields)
2351            Some(row1_fallback.as_slice()), // Row 1: {"a": {"x": "foo"}, "b": 42} (a.x can't be shredded)
2352        ]);
2353
2354        // Create the nested shredded structure
2355        // Level 2: x field (the deepest level)
2356        let x_typed_value = Int32Array::from(vec![Some(55), None]);
2357        let x_field_struct = StructArrayBuilder::new()
2358            .with_field("typed_value", Arc::new(x_typed_value), true)
2359            .build();
2360        let x_field_shredded = ShreddedVariantFieldArray::try_new(&x_field_struct)
2361            .expect("should create ShreddedVariantFieldArray for x");
2362
2363        // Level 1: a field containing x field + value field for fallbacks
2364        // The "a" field needs both typed_value (for shredded x) and value (for fallback cases)
2365
2366        // Create the value field for "a" (for cases where a.x can't be shredded)
2367        let a_value_data = {
2368            let mut builder = parquet_variant::VariantBuilder::new();
2369            let obj = builder.new_object();
2370            obj.finish();
2371            let (_, value) = builder.finish();
2372            value
2373        };
2374        let a_value_array = BinaryViewArray::from(vec![
2375            None,                          // Row 0: x is shredded, so no value fallback needed
2376            Some(a_value_data.as_slice()), // Row 1: fallback for a.x="foo" (but logic will check typed_value first)
2377        ]);
2378
2379        let a_inner_fields = Fields::from(vec![Field::new(
2380            "x",
2381            x_field_shredded.data_type().clone(),
2382            true,
2383        )]);
2384        let a_inner_struct = StructArrayBuilder::new()
2385            .with_field(
2386                "typed_value",
2387                Arc::new(
2388                    StructArray::try_new(
2389                        a_inner_fields,
2390                        vec![ArrayRef::from(x_field_shredded)],
2391                        None,
2392                    )
2393                    .unwrap(),
2394                ),
2395                true,
2396            )
2397            .with_field("value", Arc::new(a_value_array), true)
2398            .build();
2399        let a_field_shredded = ShreddedVariantFieldArray::try_new(&a_inner_struct)
2400            .expect("should create ShreddedVariantFieldArray for a");
2401
2402        // Level 0: main typed_value struct containing a field
2403        let typed_value_fields = Fields::from(vec![Field::new(
2404            "a",
2405            a_field_shredded.data_type().clone(),
2406            true,
2407        )]);
2408        let typed_value_struct = StructArray::try_new(
2409            typed_value_fields,
2410            vec![ArrayRef::from(a_field_shredded)],
2411            None,
2412        )
2413        .unwrap();
2414
2415        // Build final VariantArray
2416        let struct_array = StructArrayBuilder::new()
2417            .with_field("metadata", Arc::new(metadata_array), false)
2418            .with_field("value", Arc::new(value_array), true)
2419            .with_field("typed_value", Arc::new(typed_value_struct), true)
2420            .build();
2421
2422        Arc::new(struct_array)
2423    }
2424
2425    /// Create working depth 2 shredded test data for "a.b.x" paths
2426    /// This creates a 3-level nested shredded structure where:
2427    /// - Row 0: {"a": {"b": {"x": 100}}} with a.b.x shredded into typed_value
2428    /// - Row 1: {"a": {"b": {"x": "bar"}}} with type mismatch fallback
2429    /// - Row 2: {"a": {"b": {"y": 200}}} with missing field fallback
2430    fn create_depth_2_shredded_test_data_working() -> ArrayRef {
2431        // Create metadata following the working pattern
2432        let (metadata, _) = {
2433            // Create deeply nested structure: {"a": {"b": {"x": 100}}}
2434            let mut builder = parquet_variant::VariantBuilder::new();
2435            let mut obj = builder.new_object();
2436
2437            // Create the nested "a.b" structure
2438            let mut a_obj = obj.new_object("a");
2439            let mut b_obj = a_obj.new_object("b");
2440            b_obj.insert("x", Variant::Int32(100));
2441            b_obj.finish();
2442            a_obj.finish();
2443
2444            obj.finish();
2445            builder.finish()
2446        };
2447
2448        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 3));
2449
2450        // Create value arrays for fallback cases
2451        let empty_object_value = {
2452            let mut builder = parquet_variant::VariantBuilder::new();
2453            let obj = builder.new_object();
2454            obj.finish();
2455            let (_, value) = builder.finish();
2456            value
2457        };
2458
2459        // Simple fallback values - avoiding complex nested metadata
2460        let value_array = BinaryViewArray::from(vec![
2461            Some(empty_object_value.as_slice()), // Row 0: fully shredded
2462            Some(empty_object_value.as_slice()), // Row 1: fallback (simplified)
2463            Some(empty_object_value.as_slice()), // Row 2: fallback (simplified)
2464        ]);
2465
2466        // Create the deeply nested shredded structure: a.b.x
2467
2468        // Level 3: x field (deepest level)
2469        let x_typed_value = Int32Array::from(vec![Some(100), None, None]);
2470        let x_field_struct = StructArrayBuilder::new()
2471            .with_field("typed_value", Arc::new(x_typed_value), true)
2472            .build();
2473        let x_field_shredded = ShreddedVariantFieldArray::try_new(&x_field_struct)
2474            .expect("should create ShreddedVariantFieldArray for x");
2475
2476        // Level 2: b field containing x field + value field
2477        let b_value_data = {
2478            let mut builder = parquet_variant::VariantBuilder::new();
2479            let obj = builder.new_object();
2480            obj.finish();
2481            let (_, value) = builder.finish();
2482            value
2483        };
2484        let b_value_array = BinaryViewArray::from(vec![
2485            None,                          // Row 0: x is shredded
2486            Some(b_value_data.as_slice()), // Row 1: fallback for b.x="bar"
2487            Some(b_value_data.as_slice()), // Row 2: fallback for b.y=200
2488        ]);
2489
2490        let b_inner_fields = Fields::from(vec![Field::new(
2491            "x",
2492            x_field_shredded.data_type().clone(),
2493            true,
2494        )]);
2495        let b_inner_struct = StructArrayBuilder::new()
2496            .with_field(
2497                "typed_value",
2498                Arc::new(
2499                    StructArray::try_new(
2500                        b_inner_fields,
2501                        vec![ArrayRef::from(x_field_shredded)],
2502                        None,
2503                    )
2504                    .unwrap(),
2505                ),
2506                true,
2507            )
2508            .with_field("value", Arc::new(b_value_array), true)
2509            .build();
2510        let b_field_shredded = ShreddedVariantFieldArray::try_new(&b_inner_struct)
2511            .expect("should create ShreddedVariantFieldArray for b");
2512
2513        // Level 1: a field containing b field + value field
2514        let a_value_data = {
2515            let mut builder = parquet_variant::VariantBuilder::new();
2516            let obj = builder.new_object();
2517            obj.finish();
2518            let (_, value) = builder.finish();
2519            value
2520        };
2521        let a_value_array = BinaryViewArray::from(vec![
2522            None,                          // Row 0: b is shredded
2523            Some(a_value_data.as_slice()), // Row 1: fallback for a.b.*
2524            Some(a_value_data.as_slice()), // Row 2: fallback for a.b.*
2525        ]);
2526
2527        let a_inner_fields = Fields::from(vec![Field::new(
2528            "b",
2529            b_field_shredded.data_type().clone(),
2530            true,
2531        )]);
2532        let a_inner_struct = StructArrayBuilder::new()
2533            .with_field(
2534                "typed_value",
2535                Arc::new(
2536                    StructArray::try_new(
2537                        a_inner_fields,
2538                        vec![ArrayRef::from(b_field_shredded)],
2539                        None,
2540                    )
2541                    .unwrap(),
2542                ),
2543                true,
2544            )
2545            .with_field("value", Arc::new(a_value_array), true)
2546            .build();
2547        let a_field_shredded = ShreddedVariantFieldArray::try_new(&a_inner_struct)
2548            .expect("should create ShreddedVariantFieldArray for a");
2549
2550        // Level 0: main typed_value struct containing a field
2551        let typed_value_fields = Fields::from(vec![Field::new(
2552            "a",
2553            a_field_shredded.data_type().clone(),
2554            true,
2555        )]);
2556        let typed_value_struct = StructArray::try_new(
2557            typed_value_fields,
2558            vec![ArrayRef::from(a_field_shredded)],
2559            None,
2560        )
2561        .unwrap();
2562
2563        // Build final VariantArray
2564        let struct_array = StructArrayBuilder::new()
2565            .with_field("metadata", Arc::new(metadata_array), false)
2566            .with_field("value", Arc::new(value_array), true)
2567            .with_field("typed_value", Arc::new(typed_value_struct), true)
2568            .build();
2569
2570        Arc::new(struct_array)
2571    }
2572
2573    #[test]
2574    fn test_strict_cast_options_downcast_failure() {
2575        use arrow::compute::CastOptions;
2576        use arrow::datatypes::{DataType, Field};
2577        use arrow::error::ArrowError;
2578        use parquet_variant::VariantPath;
2579        use std::sync::Arc;
2580
2581        // Use the existing simple test data that has Int32 as typed_value
2582        let variant_array = perfectly_shredded_int32_variant_array();
2583
2584        // Try to access a field with safe cast options (should return NULLs)
2585        let safe_options = GetOptions {
2586            path: VariantPath::from("nonexistent_field"),
2587            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
2588            cast_options: CastOptions::default(), // safe = true
2589        };
2590
2591        let variant_array_ref: Arc<dyn Array> = variant_array.clone();
2592        let result = variant_get(&variant_array_ref, safe_options);
2593        // Should succeed and return NULLs (safe behavior)
2594        assert!(result.is_ok());
2595        let result_array = result.unwrap();
2596        assert_eq!(result_array.len(), 3);
2597        assert!(result_array.is_null(0));
2598        assert!(result_array.is_null(1));
2599        assert!(result_array.is_null(2));
2600
2601        // Try to access a field with strict cast options (should error)
2602        let strict_options = GetOptions {
2603            path: VariantPath::from("nonexistent_field"),
2604            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
2605            cast_options: CastOptions {
2606                safe: false,
2607                ..Default::default()
2608            },
2609        };
2610
2611        let result = variant_get(&variant_array_ref, strict_options);
2612        // Should fail with a cast error
2613        assert!(result.is_err());
2614        let error = result.unwrap_err();
2615        assert!(matches!(error, ArrowError::CastError(_)));
2616        assert!(
2617            error
2618                .to_string()
2619                .contains("Cannot access field 'nonexistent_field' on non-struct type")
2620        );
2621    }
2622
2623    #[test]
2624    fn test_error_message_boolean_type_display() {
2625        let mut builder = VariantArrayBuilder::new(1);
2626        builder.append_variant(Variant::Int32(123));
2627        let variant_array: ArrayRef = ArrayRef::from(builder.build());
2628
2629        // Request Boolean with strict casting to force an error
2630        let options = GetOptions {
2631            path: VariantPath::default(),
2632            as_type: Some(Arc::new(Field::new("result", DataType::Boolean, true))),
2633            cast_options: CastOptions {
2634                safe: false,
2635                ..Default::default()
2636            },
2637        };
2638
2639        let err = variant_get(&variant_array, options).unwrap_err();
2640        let msg = err.to_string();
2641        assert!(msg.contains("Failed to extract primitive of type Boolean"));
2642    }
2643
2644    #[test]
2645    fn test_error_message_numeric_type_display() {
2646        let mut builder = VariantArrayBuilder::new(1);
2647        builder.append_variant(Variant::BooleanTrue);
2648        let variant_array: ArrayRef = ArrayRef::from(builder.build());
2649
2650        // Request Boolean with strict casting to force an error
2651        let options = GetOptions {
2652            path: VariantPath::default(),
2653            as_type: Some(Arc::new(Field::new("result", DataType::Float32, true))),
2654            cast_options: CastOptions {
2655                safe: false,
2656                ..Default::default()
2657            },
2658        };
2659
2660        let err = variant_get(&variant_array, options).unwrap_err();
2661        let msg = err.to_string();
2662        assert!(msg.contains("Failed to extract primitive of type Float32"));
2663    }
2664
2665    #[test]
2666    fn test_error_message_temporal_type_display() {
2667        let mut builder = VariantArrayBuilder::new(1);
2668        builder.append_variant(Variant::BooleanFalse);
2669        let variant_array: ArrayRef = ArrayRef::from(builder.build());
2670
2671        // Request Boolean with strict casting to force an error
2672        let options = GetOptions {
2673            path: VariantPath::default(),
2674            as_type: Some(Arc::new(Field::new(
2675                "result",
2676                DataType::Timestamp(TimeUnit::Nanosecond, None),
2677                true,
2678            ))),
2679            cast_options: CastOptions {
2680                safe: false,
2681                ..Default::default()
2682            },
2683        };
2684
2685        let err = variant_get(&variant_array, options).unwrap_err();
2686        let msg = err.to_string();
2687        assert!(msg.contains("Failed to extract primitive of type Timestamp(ns)"));
2688    }
2689
2690    #[test]
2691    fn test_null_buffer_union_for_shredded_paths() {
2692        use arrow::compute::CastOptions;
2693        use arrow::datatypes::{DataType, Field};
2694        use parquet_variant::VariantPath;
2695        use std::sync::Arc;
2696
2697        // Test that null buffers are properly unioned when traversing shredded paths
2698        // This test verifies scovich's null buffer union requirement
2699
2700        // Create a depth-1 shredded variant array where:
2701        // - The top-level variant array has some nulls
2702        // - The nested typed_value also has some nulls
2703        // - The result should be the union of both null buffers
2704
2705        let variant_array = create_depth_1_shredded_test_data_working();
2706
2707        // Get the field "x" which should union nulls from:
2708        // 1. The top-level variant array nulls
2709        // 2. The "a" field's typed_value nulls
2710        // 3. The "x" field's typed_value nulls
2711        let options = GetOptions {
2712            path: VariantPath::from("a.x"),
2713            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
2714            cast_options: CastOptions::default(),
2715        };
2716
2717        let variant_array_ref: Arc<dyn Array> = variant_array.clone();
2718        let result = variant_get(&variant_array_ref, options).unwrap();
2719
2720        // Verify the result length matches input
2721        assert_eq!(result.len(), variant_array.len());
2722
2723        // The null pattern should reflect the union of all ancestor nulls
2724        // Row 0: Should have valid data (path exists and is shredded as Int32)
2725        // Row 1: Should be null (due to type mismatch - "foo" can't cast to Int32)
2726        assert!(!result.is_null(0), "Row 0 should have valid Int32 data");
2727        assert!(
2728            result.is_null(1),
2729            "Row 1 should be null due to type casting failure"
2730        );
2731
2732        // Verify the actual values
2733        let int32_result = result.as_any().downcast_ref::<Int32Array>().unwrap();
2734        assert_eq!(int32_result.value(0), 55); // The valid Int32 value
2735    }
2736
2737    #[test]
2738    fn test_struct_null_mask_union_from_children() {
2739        use arrow::compute::CastOptions;
2740        use arrow::datatypes::{DataType, Field, Fields};
2741        use parquet_variant::VariantPath;
2742        use std::sync::Arc;
2743
2744        use arrow::array::StringArray;
2745
2746        // Test that struct null masks properly union nulls from children field extractions
2747        // This verifies scovich's concern about incomplete null masks in struct construction
2748
2749        // Create test data where some fields will fail type casting
2750        let json_strings = vec![
2751            r#"{"a": 42, "b": "hello"}"#, // Row 0: a=42 (castable to int), b="hello" (not castable to int)
2752            r#"{"a": "world", "b": 100}"#, // Row 1: a="world" (not castable to int), b=100 (castable to int)
2753            r#"{"a": 55, "b": 77}"#,       // Row 2: a=55 (castable to int), b=77 (castable to int)
2754        ];
2755
2756        let string_array: Arc<dyn arrow::array::Array> = Arc::new(StringArray::from(json_strings));
2757        let variant_array = json_to_variant(&string_array).unwrap();
2758
2759        // Request extraction as a struct with both fields as Int32
2760        // This should create child arrays where some fields are null due to casting failures
2761        let struct_fields = Fields::from(vec![
2762            Field::new("a", DataType::Int32, true),
2763            Field::new("b", DataType::Int32, true),
2764        ]);
2765        let struct_type = DataType::Struct(struct_fields);
2766
2767        let options = GetOptions {
2768            path: VariantPath::default(), // Extract the whole object as struct
2769            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
2770            cast_options: CastOptions::default(),
2771        };
2772
2773        let variant_array_ref = ArrayRef::from(variant_array);
2774        let result = variant_get(&variant_array_ref, options).unwrap();
2775
2776        // Verify the result is a StructArray
2777        let struct_result = result.as_struct();
2778        assert_eq!(struct_result.len(), 3);
2779
2780        // Get the individual field arrays
2781        let field_a = struct_result
2782            .column(0)
2783            .as_any()
2784            .downcast_ref::<Int32Array>()
2785            .unwrap();
2786        let field_b = struct_result
2787            .column(1)
2788            .as_any()
2789            .downcast_ref::<Int32Array>()
2790            .unwrap();
2791
2792        // Verify field values and nulls
2793        // Row 0: a=42 (valid), b=null (casting failure)
2794        assert!(!field_a.is_null(0));
2795        assert_eq!(field_a.value(0), 42);
2796        assert!(field_b.is_null(0)); // "hello" can't cast to int
2797
2798        // Row 1: a=null (casting failure), b=100 (valid)
2799        assert!(field_a.is_null(1)); // "world" can't cast to int
2800        assert!(!field_b.is_null(1));
2801        assert_eq!(field_b.value(1), 100);
2802
2803        // Row 2: a=55 (valid), b=77 (valid)
2804        assert!(!field_a.is_null(2));
2805        assert_eq!(field_a.value(2), 55);
2806        assert!(!field_b.is_null(2));
2807        assert_eq!(field_b.value(2), 77);
2808
2809        // Verify the struct-level null mask properly unions child nulls
2810        // The struct should NOT be null in any row because each row has at least one valid field
2811        // (This tests that we're not incorrectly making the entire struct null when children fail)
2812        assert!(!struct_result.is_null(0)); // Has valid field 'a'
2813        assert!(!struct_result.is_null(1)); // Has valid field 'b'
2814        assert!(!struct_result.is_null(2)); // Has both valid fields
2815    }
2816
2817    #[test]
2818    fn test_field_nullability_preservation() {
2819        use arrow::compute::CastOptions;
2820        use arrow::datatypes::{DataType, Field};
2821        use parquet_variant::VariantPath;
2822        use std::sync::Arc;
2823
2824        use arrow::array::StringArray;
2825
2826        // Test that field nullability from GetOptions.as_type is preserved in the result
2827
2828        let json_strings = vec![
2829            r#"{"x": 42}"#,                  // Row 0: Valid int that should convert to Int32
2830            r#"{"x": "not_a_number"}"#,      // Row 1: String that can't cast to Int32
2831            r#"{"x": null}"#,                // Row 2: Explicit null value
2832            r#"{"x": "hello"}"#,             // Row 3: Another string (wrong type)
2833            r#"{"y": 100}"#,                 // Row 4: Missing "x" field (SQL NULL case)
2834            r#"{"x": 127}"#, // Row 5: Small int (could be Int8, widening cast candidate)
2835            r#"{"x": 32767}"#, // Row 6: Medium int (could be Int16, widening cast candidate)
2836            r#"{"x": 2147483647}"#, // Row 7: Max Int32 value (fits in Int32)
2837            r#"{"x": 9223372036854775807}"#, // Row 8: Large Int64 value (cannot convert to Int32)
2838        ];
2839
2840        let string_array: Arc<dyn arrow::array::Array> = Arc::new(StringArray::from(json_strings));
2841        let variant_array = json_to_variant(&string_array).unwrap();
2842
2843        // Test 1: nullable field (should allow nulls from cast failures)
2844        let nullable_field = Arc::new(Field::new("result", DataType::Int32, true));
2845        let options_nullable = GetOptions {
2846            path: VariantPath::from("x"),
2847            as_type: Some(nullable_field.clone()),
2848            cast_options: CastOptions::default(),
2849        };
2850
2851        let variant_array_ref = ArrayRef::from(variant_array);
2852        let result_nullable = variant_get(&variant_array_ref, options_nullable).unwrap();
2853
2854        // Verify we get an Int32Array with nulls for cast failures
2855        let int32_result = result_nullable
2856            .as_any()
2857            .downcast_ref::<Int32Array>()
2858            .unwrap();
2859        assert_eq!(int32_result.len(), 9);
2860
2861        // Row 0: 42 converts successfully to Int32
2862        assert!(!int32_result.is_null(0));
2863        assert_eq!(int32_result.value(0), 42);
2864
2865        // Row 1: "not_a_number" fails to convert -> NULL
2866        assert!(int32_result.is_null(1));
2867
2868        // Row 2: explicit null value -> NULL
2869        assert!(int32_result.is_null(2));
2870
2871        // Row 3: "hello" (wrong type) fails to convert -> NULL
2872        assert!(int32_result.is_null(3));
2873
2874        // Row 4: missing "x" field (SQL NULL case) -> NULL
2875        assert!(int32_result.is_null(4));
2876
2877        // Row 5: 127 (small int, potential Int8 -> Int32 widening)
2878        // Current behavior: JSON parses to Int8, should convert to Int32
2879        assert!(!int32_result.is_null(5));
2880        assert_eq!(int32_result.value(5), 127);
2881
2882        // Row 6: 32767 (medium int, potential Int16 -> Int32 widening)
2883        // Current behavior: JSON parses to Int16, should convert to Int32
2884        assert!(!int32_result.is_null(6));
2885        assert_eq!(int32_result.value(6), 32767);
2886
2887        // Row 7: 2147483647 (max Int32, fits exactly)
2888        // Current behavior: Should convert successfully
2889        assert!(!int32_result.is_null(7));
2890        assert_eq!(int32_result.value(7), 2147483647);
2891
2892        // Row 8: 9223372036854775807 (large Int64, cannot fit in Int32)
2893        // Current behavior: Should fail conversion -> NULL
2894        assert!(int32_result.is_null(8));
2895
2896        // Test 2: non-nullable field (behavior should be the same with safe casting)
2897        let non_nullable_field = Arc::new(Field::new("result", DataType::Int32, false));
2898        let options_non_nullable = GetOptions {
2899            path: VariantPath::from("x"),
2900            as_type: Some(non_nullable_field.clone()),
2901            cast_options: CastOptions::default(), // safe=true by default
2902        };
2903
2904        // Create variant array again since we moved it
2905        let variant_array_2 = json_to_variant(&string_array).unwrap();
2906        let variant_array_ref_2 = ArrayRef::from(variant_array_2);
2907        let result_non_nullable = variant_get(&variant_array_ref_2, options_non_nullable).unwrap();
2908        let int32_result_2 = result_non_nullable
2909            .as_any()
2910            .downcast_ref::<Int32Array>()
2911            .unwrap();
2912
2913        // Even with a non-nullable field, safe casting should still produce nulls for failures
2914        assert_eq!(int32_result_2.len(), 9);
2915
2916        // Row 0: 42 converts successfully to Int32
2917        assert!(!int32_result_2.is_null(0));
2918        assert_eq!(int32_result_2.value(0), 42);
2919
2920        // Rows 1-4: All should be null due to safe casting behavior
2921        // (non-nullable field specification doesn't override safe casting behavior)
2922        assert!(int32_result_2.is_null(1)); // "not_a_number"
2923        assert!(int32_result_2.is_null(2)); // explicit null
2924        assert!(int32_result_2.is_null(3)); // "hello"
2925        assert!(int32_result_2.is_null(4)); // missing field
2926
2927        // Rows 5-7: These should also convert successfully (numeric widening/fitting)
2928        assert!(!int32_result_2.is_null(5)); // 127 (Int8 -> Int32)
2929        assert_eq!(int32_result_2.value(5), 127);
2930        assert!(!int32_result_2.is_null(6)); // 32767 (Int16 -> Int32)
2931        assert_eq!(int32_result_2.value(6), 32767);
2932        assert!(!int32_result_2.is_null(7)); // 2147483647 (fits in Int32)
2933        assert_eq!(int32_result_2.value(7), 2147483647);
2934
2935        // Row 8: Large Int64 should fail conversion -> NULL
2936        assert!(int32_result_2.is_null(8)); // 9223372036854775807 (too large for Int32)
2937    }
2938
2939    #[test]
2940    fn test_struct_extraction_subset_superset_schema_perfectly_shredded() {
2941        // Create variant with diverse null patterns and empty objects
2942        let variant_array = create_comprehensive_shredded_variant();
2943
2944        // Request struct with fields "a", "b", "d" (skip existing "c", add missing "d")
2945        let struct_fields = Fields::from(vec![
2946            Field::new("a", DataType::Int32, true),
2947            Field::new("b", DataType::Int32, true),
2948            Field::new("d", DataType::Int32, true),
2949        ]);
2950        let struct_type = DataType::Struct(struct_fields);
2951
2952        let options = GetOptions {
2953            path: VariantPath::default(),
2954            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
2955            cast_options: CastOptions::default(),
2956        };
2957
2958        let result = variant_get(&variant_array, options).unwrap();
2959
2960        // Verify the result is a StructArray with 3 fields and 5 rows
2961        let struct_result = result.as_any().downcast_ref::<StructArray>().unwrap();
2962        assert_eq!(struct_result.len(), 5);
2963        assert_eq!(struct_result.num_columns(), 3);
2964
2965        let field_a = struct_result
2966            .column(0)
2967            .as_any()
2968            .downcast_ref::<Int32Array>()
2969            .unwrap();
2970        let field_b = struct_result
2971            .column(1)
2972            .as_any()
2973            .downcast_ref::<Int32Array>()
2974            .unwrap();
2975        let field_d = struct_result
2976            .column(2)
2977            .as_any()
2978            .downcast_ref::<Int32Array>()
2979            .unwrap();
2980
2981        // Row 0: Normal values {"a": 1, "b": 2, "c": 3} → {a: 1, b: 2, d: NULL}
2982        assert!(!struct_result.is_null(0));
2983        assert_eq!(field_a.value(0), 1);
2984        assert_eq!(field_b.value(0), 2);
2985        assert!(field_d.is_null(0)); // Missing field "d"
2986
2987        // Row 1: Top-level NULL → struct-level NULL
2988        assert!(struct_result.is_null(1));
2989
2990        // Row 2: Field "a" missing → {a: NULL, b: 2, d: NULL}
2991        assert!(!struct_result.is_null(2));
2992        assert!(field_a.is_null(2)); // Missing field "a"
2993        assert_eq!(field_b.value(2), 2);
2994        assert!(field_d.is_null(2)); // Missing field "d"
2995
2996        // Row 3: Field "b" missing → {a: 1, b: NULL, d: NULL}
2997        assert!(!struct_result.is_null(3));
2998        assert_eq!(field_a.value(3), 1);
2999        assert!(field_b.is_null(3)); // Missing field "b"
3000        assert!(field_d.is_null(3)); // Missing field "d"
3001
3002        // Row 4: Empty object {} → {a: NULL, b: NULL, d: NULL}
3003        assert!(!struct_result.is_null(4));
3004        assert!(field_a.is_null(4)); // Empty object
3005        assert!(field_b.is_null(4)); // Empty object
3006        assert!(field_d.is_null(4)); // Missing field "d"
3007    }
3008
3009    #[test]
3010    fn test_nested_struct_extraction_perfectly_shredded() {
3011        // Create nested variant with diverse null patterns
3012        let variant_array = create_comprehensive_nested_shredded_variant();
3013        println!("variant_array: {variant_array:?}");
3014
3015        // Request 3-level nested struct type {"outer": {"inner": INT}}
3016        let inner_field = Field::new("inner", DataType::Int32, true);
3017        let inner_type = DataType::Struct(Fields::from(vec![inner_field]));
3018        let outer_field = Field::new("outer", inner_type, true);
3019        let result_type = DataType::Struct(Fields::from(vec![outer_field]));
3020
3021        let options = GetOptions {
3022            path: VariantPath::default(),
3023            as_type: Some(Arc::new(Field::new("result", result_type, true))),
3024            cast_options: CastOptions::default(),
3025        };
3026
3027        let result = variant_get(&variant_array, options).unwrap();
3028        println!("result: {result:?}");
3029
3030        // Verify the result is a StructArray with "outer" field and 4 rows
3031        let outer_struct = result.as_any().downcast_ref::<StructArray>().unwrap();
3032        assert_eq!(outer_struct.len(), 4);
3033        assert_eq!(outer_struct.num_columns(), 1);
3034
3035        // Get the "inner" struct column
3036        let inner_struct = outer_struct
3037            .column(0)
3038            .as_any()
3039            .downcast_ref::<StructArray>()
3040            .unwrap();
3041        assert_eq!(inner_struct.num_columns(), 1);
3042
3043        // Get the "leaf" field (Int32 values)
3044        let leaf_field = inner_struct
3045            .column(0)
3046            .as_any()
3047            .downcast_ref::<Int32Array>()
3048            .unwrap();
3049
3050        // Row 0: Normal nested {"outer": {"inner": {"leaf": 42}}}
3051        assert!(!outer_struct.is_null(0));
3052        assert!(!inner_struct.is_null(0));
3053        assert_eq!(leaf_field.value(0), 42);
3054
3055        // Row 1: "inner" field missing → {outer: {inner: NULL}}
3056        assert!(!outer_struct.is_null(1));
3057        assert!(!inner_struct.is_null(1)); // outer exists, inner exists but leaf is NULL
3058        assert!(leaf_field.is_null(1)); // leaf field is NULL
3059
3060        // Row 2: "outer" field missing → {outer: NULL}
3061        assert!(!outer_struct.is_null(2));
3062        assert!(inner_struct.is_null(2)); // outer field is NULL
3063
3064        // Row 3: Top-level NULL → struct-level NULL
3065        assert!(outer_struct.is_null(3));
3066    }
3067
3068    #[test]
3069    fn test_path_based_null_masks_one_step() {
3070        // Create nested variant with diverse null patterns
3071        let variant_array = create_comprehensive_nested_shredded_variant();
3072
3073        // Extract "outer" field using path-based variant_get
3074        let path = VariantPath::from("outer");
3075        let inner_field = Field::new("inner", DataType::Int32, true);
3076        let result_type = DataType::Struct(Fields::from(vec![inner_field]));
3077
3078        let options = GetOptions {
3079            path,
3080            as_type: Some(Arc::new(Field::new("result", result_type, true))),
3081            cast_options: CastOptions::default(),
3082        };
3083
3084        let result = variant_get(&variant_array, options).unwrap();
3085
3086        // Verify the result is a StructArray with "inner" field and 4 rows
3087        let outer_result = result.as_any().downcast_ref::<StructArray>().unwrap();
3088        assert_eq!(outer_result.len(), 4);
3089        assert_eq!(outer_result.num_columns(), 1);
3090
3091        // Get the "inner" field (Int32 values)
3092        let inner_field = outer_result
3093            .column(0)
3094            .as_any()
3095            .downcast_ref::<Int32Array>()
3096            .unwrap();
3097
3098        // Row 0: Normal nested {"outer": {"inner": 42}} → {"inner": 42}
3099        assert!(!outer_result.is_null(0));
3100        assert_eq!(inner_field.value(0), 42);
3101
3102        // Row 1: Inner field null {"outer": {"inner": null}} → {"inner": null}
3103        assert!(!outer_result.is_null(1));
3104        assert!(inner_field.is_null(1));
3105
3106        // Row 2: Outer field null {"outer": null} → null (entire struct is null)
3107        assert!(outer_result.is_null(2));
3108
3109        // Row 3: Top-level null → null (entire struct is null)
3110        assert!(outer_result.is_null(3));
3111    }
3112
3113    #[test]
3114    fn test_path_based_null_masks_two_steps() {
3115        // Create nested variant with diverse null patterns
3116        let variant_array = create_comprehensive_nested_shredded_variant();
3117
3118        // Extract "outer.inner" field using path-based variant_get
3119        let path = VariantPath::from("outer").join("inner");
3120
3121        let options = GetOptions {
3122            path,
3123            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
3124            cast_options: CastOptions::default(),
3125        };
3126
3127        let result = variant_get(&variant_array, options).unwrap();
3128
3129        // Verify the result is an Int32Array with 4 rows
3130        let int_result = result.as_any().downcast_ref::<Int32Array>().unwrap();
3131        assert_eq!(int_result.len(), 4);
3132
3133        // Row 0: Normal nested {"outer": {"inner": 42}} → 42
3134        assert!(!int_result.is_null(0));
3135        assert_eq!(int_result.value(0), 42);
3136
3137        // Row 1: Inner field null {"outer": {"inner": null}} → null
3138        assert!(int_result.is_null(1));
3139
3140        // Row 2: Outer field null {"outer": null} → null (path traversal fails)
3141        assert!(int_result.is_null(2));
3142
3143        // Row 3: Top-level null → null (path traversal fails)
3144        assert!(int_result.is_null(3));
3145    }
3146
3147    #[test]
3148    fn test_struct_extraction_mixed_and_unshredded() {
3149        // Create a partially shredded variant (x shredded, y not)
3150        let variant_array = create_mixed_and_unshredded_variant();
3151
3152        // Request struct with both shredded and unshredded fields
3153        let struct_fields = Fields::from(vec![
3154            Field::new("x", DataType::Int32, true),
3155            Field::new("y", DataType::Int32, true),
3156        ]);
3157        let struct_type = DataType::Struct(struct_fields);
3158
3159        let options = GetOptions {
3160            path: VariantPath::default(),
3161            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
3162            cast_options: CastOptions::default(),
3163        };
3164
3165        let result = variant_get(&variant_array, options).unwrap();
3166
3167        // Verify the mixed shredding works (should succeed with current implementation)
3168        let struct_result = result.as_any().downcast_ref::<StructArray>().unwrap();
3169        assert_eq!(struct_result.len(), 4);
3170        assert_eq!(struct_result.num_columns(), 2);
3171
3172        let field_x = struct_result
3173            .column(0)
3174            .as_any()
3175            .downcast_ref::<Int32Array>()
3176            .unwrap();
3177        let field_y = struct_result
3178            .column(1)
3179            .as_any()
3180            .downcast_ref::<Int32Array>()
3181            .unwrap();
3182
3183        // Row 0: {"x": 1, "y": 42} - x from shredded, y from value field
3184        assert_eq!(field_x.value(0), 1);
3185        assert_eq!(field_y.value(0), 42);
3186
3187        // Row 1: {"x": 2} - x from shredded, y missing (perfect shredding)
3188        assert_eq!(field_x.value(1), 2);
3189        assert!(field_y.is_null(1));
3190
3191        // Row 2: {"x": 3, "y": null} - x from shredded, y explicitly null in value
3192        assert_eq!(field_x.value(2), 3);
3193        assert!(field_y.is_null(2));
3194
3195        // Row 3: top-level null - entire struct row should be null
3196        assert!(struct_result.is_null(3));
3197    }
3198
3199    /// Test that demonstrates the actual struct row builder gap
3200    /// This test should fail because it hits unshredded nested structs
3201    #[test]
3202    fn test_struct_row_builder_gap_demonstration() {
3203        // Create completely unshredded JSON variant (no typed_value at all)
3204        let json_strings = vec![
3205            r#"{"outer": {"inner": 42}}"#,
3206            r#"{"outer": {"inner": 100}}"#,
3207        ];
3208        let string_array: Arc<dyn Array> = Arc::new(StringArray::from(json_strings));
3209        let variant_array = json_to_variant(&string_array).unwrap();
3210
3211        // Request nested struct - this should fail at the row builder level
3212        let inner_fields = Fields::from(vec![Field::new("inner", DataType::Int32, true)]);
3213        let inner_struct_type = DataType::Struct(inner_fields);
3214        let outer_fields = Fields::from(vec![Field::new("outer", inner_struct_type, true)]);
3215        let outer_struct_type = DataType::Struct(outer_fields);
3216
3217        let options = GetOptions {
3218            path: VariantPath::default(),
3219            as_type: Some(Arc::new(Field::new("result", outer_struct_type, true))),
3220            cast_options: CastOptions::default(),
3221        };
3222
3223        let variant_array_ref = ArrayRef::from(variant_array);
3224        let result = variant_get(&variant_array_ref, options);
3225
3226        // Should fail with NotYetImplemented when the row builder tries to handle struct type
3227        assert!(result.is_err());
3228        let error = result.unwrap_err();
3229        assert!(error.to_string().contains("Not yet implemented"));
3230    }
3231
3232    /// Create comprehensive shredded variant with diverse null patterns and empty objects
3233    /// Rows: normal values, top-level null, missing field a, missing field b, empty object
3234    fn create_comprehensive_shredded_variant() -> ArrayRef {
3235        let (metadata, _) = {
3236            let mut builder = parquet_variant::VariantBuilder::new();
3237            let obj = builder.new_object();
3238            obj.finish();
3239            builder.finish()
3240        };
3241
3242        // Create null buffer for top-level nulls
3243        let nulls = NullBuffer::from(vec![
3244            true,  // row 0: normal values
3245            false, // row 1: top-level null
3246            true,  // row 2: missing field a
3247            true,  // row 3: missing field b
3248            true,  // row 4: empty object
3249        ]);
3250
3251        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 5));
3252
3253        // Create shredded fields with different null patterns
3254        // Field "a": present in rows 0,3 (missing in rows 1,2,4)
3255        let a_field_typed_value = Int32Array::from(vec![Some(1), None, None, Some(1), None]);
3256        let a_field_struct = StructArrayBuilder::new()
3257            .with_field("typed_value", Arc::new(a_field_typed_value), true)
3258            .build();
3259        let a_field_shredded = ShreddedVariantFieldArray::try_new(&a_field_struct)
3260            .expect("should create ShreddedVariantFieldArray for a");
3261
3262        // Field "b": present in rows 0,2 (missing in rows 1,3,4)
3263        let b_field_typed_value = Int32Array::from(vec![Some(2), None, Some(2), None, None]);
3264        let b_field_struct = StructArrayBuilder::new()
3265            .with_field("typed_value", Arc::new(b_field_typed_value), true)
3266            .build();
3267        let b_field_shredded = ShreddedVariantFieldArray::try_new(&b_field_struct)
3268            .expect("should create ShreddedVariantFieldArray for b");
3269
3270        // Field "c": present in row 0 only (missing in all other rows)
3271        let c_field_typed_value = Int32Array::from(vec![Some(3), None, None, None, None]);
3272        let c_field_struct = StructArrayBuilder::new()
3273            .with_field("typed_value", Arc::new(c_field_typed_value), true)
3274            .build();
3275        let c_field_shredded = ShreddedVariantFieldArray::try_new(&c_field_struct)
3276            .expect("should create ShreddedVariantFieldArray for c");
3277
3278        // Create main typed_value struct
3279        let typed_value_fields = Fields::from(vec![
3280            Field::new("a", a_field_shredded.data_type().clone(), true),
3281            Field::new("b", b_field_shredded.data_type().clone(), true),
3282            Field::new("c", c_field_shredded.data_type().clone(), true),
3283        ]);
3284        let typed_value_struct = StructArray::try_new(
3285            typed_value_fields,
3286            vec![
3287                ArrayRef::from(a_field_shredded),
3288                ArrayRef::from(b_field_shredded),
3289                ArrayRef::from(c_field_shredded),
3290            ],
3291            None,
3292        )
3293        .unwrap();
3294
3295        // Build final VariantArray with top-level nulls
3296        let struct_array = StructArrayBuilder::new()
3297            .with_field("metadata", Arc::new(metadata_array), false)
3298            .with_field("typed_value", Arc::new(typed_value_struct), true)
3299            .with_nulls(nulls)
3300            .build();
3301
3302        Arc::new(struct_array)
3303    }
3304
3305    /// Create comprehensive nested shredded variant with diverse null patterns
3306    /// Represents 3-level structure: variant -> outer -> inner (INT value)
3307    /// The shredding schema is: {"metadata": BINARY, "typed_value": {"outer": {"typed_value": {"inner": {"typed_value": INT}}}}}
3308    /// Rows: normal nested value, inner field null, outer field null, top-level null
3309    fn create_comprehensive_nested_shredded_variant() -> ArrayRef {
3310        // Create the inner level: contains typed_value with Int32 values
3311        // Row 0: has value 42, Row 1: inner null, Row 2: outer null, Row 3: top-level null
3312        let inner_typed_value = Int32Array::from(vec![Some(42), None, None, None]); // dummy value for row 2
3313        let inner = StructArrayBuilder::new()
3314            .with_field("typed_value", Arc::new(inner_typed_value), true)
3315            .build();
3316        let inner = ShreddedVariantFieldArray::try_new(&inner).unwrap();
3317
3318        let outer_typed_value_nulls = NullBuffer::from(vec![
3319            true,  // row 0: inner struct exists with typed_value=42
3320            false, // row 1: inner field NULL
3321            false, // row 2: outer field NULL
3322            false, // row 3: top-level NULL
3323        ]);
3324        let outer_typed_value = StructArrayBuilder::new()
3325            .with_field("inner", ArrayRef::from(inner), false)
3326            .with_nulls(outer_typed_value_nulls)
3327            .build();
3328
3329        let outer = StructArrayBuilder::new()
3330            .with_field("typed_value", Arc::new(outer_typed_value), true)
3331            .build();
3332        let outer = ShreddedVariantFieldArray::try_new(&outer).unwrap();
3333
3334        let typed_value_nulls = NullBuffer::from(vec![
3335            true,  // row 0: inner struct exists with typed_value=42
3336            true,  // row 1: inner field NULL
3337            false, // row 2: outer field NULL
3338            false, // row 3: top-level NULL
3339        ]);
3340        let typed_value = StructArrayBuilder::new()
3341            .with_field("outer", ArrayRef::from(outer), false)
3342            .with_nulls(typed_value_nulls)
3343            .build();
3344
3345        // Build final VariantArray with top-level nulls
3346        let metadata_array =
3347            BinaryViewArray::from_iter_values(std::iter::repeat_n(EMPTY_VARIANT_METADATA_BYTES, 4));
3348        let nulls = NullBuffer::from(vec![
3349            true,  // row 0: inner struct exists with typed_value=42
3350            true,  // row 1: inner field NULL
3351            true,  // row 2: outer field NULL
3352            false, // row 3: top-level NULL
3353        ]);
3354        let struct_array = StructArrayBuilder::new()
3355            .with_field("metadata", Arc::new(metadata_array), false)
3356            .with_field("typed_value", Arc::new(typed_value), true)
3357            .with_nulls(nulls)
3358            .build();
3359
3360        Arc::new(struct_array)
3361    }
3362
3363    /// Create variant with mixed shredding (spec-compliant) including null scenarios
3364    /// Field "x" is globally shredded, field "y" is never shredded
3365    fn create_mixed_and_unshredded_variant() -> ArrayRef {
3366        // Create spec-compliant mixed shredding:
3367        // - Field "x" is globally shredded (has typed_value column)
3368        // - Field "y" is never shredded (only appears in value field when present)
3369
3370        let (metadata, y_field_value) = {
3371            let mut builder = parquet_variant::VariantBuilder::new();
3372            let mut obj = builder.new_object();
3373            obj.insert("y", Variant::from(42));
3374            obj.finish();
3375            builder.finish()
3376        };
3377
3378        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 4));
3379
3380        // Value field contains objects with unshredded fields only (never contains "x")
3381        // Row 0: {"y": "foo"} - x is shredded out, y remains in value
3382        // Row 1: {} - both x and y are absent (perfect shredding for x, y missing)
3383        // Row 2: {"y": null} - x is shredded out, y explicitly null
3384        // Row 3: top-level null (encoded in VariantArray's null mask, but fields contain valid data)
3385
3386        let empty_object_value = {
3387            let mut builder = parquet_variant::VariantBuilder::new();
3388            builder.new_object().finish();
3389            let (_, value) = builder.finish();
3390            value
3391        };
3392
3393        let y_null_value = {
3394            let mut builder = parquet_variant::VariantBuilder::new();
3395            builder.new_object().with_field("y", Variant::Null).finish();
3396            let (_, value) = builder.finish();
3397            value
3398        };
3399
3400        let value_array = BinaryViewArray::from(vec![
3401            Some(y_field_value.as_slice()),      // Row 0: {"y": 42}
3402            Some(empty_object_value.as_slice()), // Row 1: {}
3403            Some(y_null_value.as_slice()),       // Row 2: {"y": null}
3404            Some(empty_object_value.as_slice()), // Row 3: top-level null (but value field contains valid data)
3405        ]);
3406
3407        // Create shredded field "x" (globally shredded - never appears in value field)
3408        // For top-level null row, the field still needs valid content (not null)
3409        let x_field_typed_value = Int32Array::from(vec![Some(1), Some(2), Some(3), Some(0)]);
3410        let x_field_struct = StructArrayBuilder::new()
3411            .with_field("typed_value", Arc::new(x_field_typed_value), true)
3412            .build();
3413        let x_field_shredded = ShreddedVariantFieldArray::try_new(&x_field_struct)
3414            .expect("should create ShreddedVariantFieldArray for x");
3415
3416        // Create main typed_value struct (only contains shredded fields)
3417        let typed_value_struct = StructArrayBuilder::new()
3418            .with_field("x", ArrayRef::from(x_field_shredded), false)
3419            .build();
3420
3421        // Build VariantArray with both value and typed_value (PartiallyShredded)
3422        // Top-level null is encoded in the main StructArray's null mask
3423        let variant_nulls = NullBuffer::from(vec![true, true, true, false]); // Row 3 is top-level null
3424        let struct_array = StructArrayBuilder::new()
3425            .with_field("metadata", Arc::new(metadata_array), false)
3426            .with_field("value", Arc::new(value_array), true)
3427            .with_field("typed_value", Arc::new(typed_value_struct), true)
3428            .with_nulls(variant_nulls)
3429            .build();
3430
3431        Arc::new(struct_array)
3432    }
3433
3434    #[test]
3435    fn get_decimal32_rescaled_to_scale2() {
3436        // Build unshredded variant values with different scales
3437        let mut builder = crate::VariantArrayBuilder::new(5);
3438        builder.append_variant(VariantDecimal4::try_new(1234, 2).unwrap().into()); // 12.34
3439        builder.append_variant(VariantDecimal4::try_new(1234, 3).unwrap().into()); // 1.234
3440        builder.append_variant(VariantDecimal4::try_new(1234, 0).unwrap().into()); // 1234
3441        builder.append_null();
3442        builder.append_variant(
3443            VariantDecimal8::try_new((VariantDecimal4::MAX_UNSCALED_VALUE as i64) + 1, 3)
3444                .unwrap()
3445                .into(),
3446        ); // should fit into Decimal32
3447        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3448
3449        let field = Field::new("result", DataType::Decimal32(9, 2), true);
3450        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3451        let result = variant_get(&variant_array, options).unwrap();
3452        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3453
3454        assert_eq!(result.precision(), 9);
3455        assert_eq!(result.scale(), 2);
3456        assert_eq!(result.value(0), 1234);
3457        assert_eq!(result.value(1), 123);
3458        assert_eq!(result.value(2), 123400);
3459        assert!(result.is_null(3));
3460        assert_eq!(
3461            result.value(4),
3462            VariantDecimal4::MAX_UNSCALED_VALUE / 10 + 1
3463        ); // should not be null as the final result fits into Decimal32
3464    }
3465
3466    #[test]
3467    fn get_decimal32_scale_down_rounding() {
3468        let mut builder = crate::VariantArrayBuilder::new(7);
3469        builder.append_variant(VariantDecimal4::try_new(1235, 0).unwrap().into());
3470        builder.append_variant(VariantDecimal4::try_new(1245, 0).unwrap().into());
3471        builder.append_variant(VariantDecimal4::try_new(-1235, 0).unwrap().into());
3472        builder.append_variant(VariantDecimal4::try_new(-1245, 0).unwrap().into());
3473        builder.append_variant(VariantDecimal4::try_new(1235, 2).unwrap().into()); // 12.35 rounded down to 10 for scale -1
3474        builder.append_variant(VariantDecimal4::try_new(1235, 3).unwrap().into()); // 1.235 rounded down to 0 for scale -1
3475        builder.append_variant(VariantDecimal4::try_new(5235, 3).unwrap().into()); // 5.235 rounded up to 10 for scale -1
3476        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3477
3478        let field = Field::new("result", DataType::Decimal32(9, -1), true);
3479        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3480        let result = variant_get(&variant_array, options).unwrap();
3481        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3482
3483        assert_eq!(result.precision(), 9);
3484        assert_eq!(result.scale(), -1);
3485        assert_eq!(result.value(0), 124);
3486        assert_eq!(result.value(1), 125);
3487        assert_eq!(result.value(2), -124);
3488        assert_eq!(result.value(3), -125);
3489        assert_eq!(result.value(4), 1);
3490        assert!(result.is_valid(5));
3491        assert_eq!(result.value(5), 0);
3492        assert_eq!(result.value(6), 1);
3493    }
3494
3495    #[test]
3496    fn get_decimal32_large_scale_reduction() {
3497        let mut builder = crate::VariantArrayBuilder::new(2);
3498        builder.append_variant(
3499            VariantDecimal4::try_new(-VariantDecimal4::MAX_UNSCALED_VALUE, 0)
3500                .unwrap()
3501                .into(),
3502        );
3503        builder.append_variant(
3504            VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0)
3505                .unwrap()
3506                .into(),
3507        );
3508        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3509
3510        let field = Field::new("result", DataType::Decimal32(9, -9), true);
3511        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3512        let result = variant_get(&variant_array, options).unwrap();
3513        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3514
3515        assert_eq!(result.precision(), 9);
3516        assert_eq!(result.scale(), -9);
3517        assert_eq!(result.value(0), -1);
3518        assert_eq!(result.value(1), 1);
3519
3520        let field = Field::new("result", DataType::Decimal32(9, -10), true);
3521        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3522        let result = variant_get(&variant_array, options).unwrap();
3523        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3524
3525        assert_eq!(result.precision(), 9);
3526        assert_eq!(result.scale(), -10);
3527        assert!(result.is_valid(0));
3528        assert_eq!(result.value(0), 0);
3529        assert!(result.is_valid(1));
3530        assert_eq!(result.value(1), 0);
3531    }
3532
3533    #[test]
3534    fn get_decimal32_precision_overflow_safe() {
3535        // Exceed Decimal32 after scaling and rounding
3536        let mut builder = crate::VariantArrayBuilder::new(2);
3537        builder.append_variant(
3538            VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0)
3539                .unwrap()
3540                .into(),
3541        );
3542        builder.append_variant(
3543            VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 9)
3544                .unwrap()
3545                .into(),
3546        ); // integer value round up overflows
3547        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3548
3549        let field = Field::new("result", DataType::Decimal32(2, 2), true);
3550        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3551        let result = variant_get(&variant_array, options).unwrap();
3552        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3553
3554        assert!(result.is_null(0));
3555        assert!(result.is_null(1)); // should overflow because 1.00 does not fit into precision (2)
3556    }
3557
3558    #[test]
3559    fn get_decimal32_precision_overflow_unsafe_errors() {
3560        let mut builder = crate::VariantArrayBuilder::new(1);
3561        builder.append_variant(
3562            VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0)
3563                .unwrap()
3564                .into(),
3565        );
3566        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3567
3568        let field = Field::new("result", DataType::Decimal32(9, 2), true);
3569        let cast_options = CastOptions {
3570            safe: false,
3571            ..Default::default()
3572        };
3573        let options = GetOptions::new()
3574            .with_as_type(Some(FieldRef::from(field)))
3575            .with_cast_options(cast_options);
3576        let err = variant_get(&variant_array, options).unwrap_err();
3577
3578        assert!(
3579            err.to_string().contains(
3580                "Failed to cast to Decimal32(precision=9, scale=2) from variant Decimal4"
3581            )
3582        );
3583    }
3584
3585    #[test]
3586    fn get_decimal64_rescaled_to_scale2() {
3587        let mut builder = crate::VariantArrayBuilder::new(5);
3588        builder.append_variant(VariantDecimal8::try_new(1234, 2).unwrap().into()); // 12.34
3589        builder.append_variant(VariantDecimal8::try_new(1234, 3).unwrap().into()); // 1.234
3590        builder.append_variant(VariantDecimal8::try_new(1234, 0).unwrap().into()); // 1234
3591        builder.append_null();
3592        builder.append_variant(
3593            VariantDecimal16::try_new((VariantDecimal8::MAX_UNSCALED_VALUE as i128) + 1, 3)
3594                .unwrap()
3595                .into(),
3596        ); // should fit into Decimal64
3597        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3598
3599        let field = Field::new("result", DataType::Decimal64(18, 2), true);
3600        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3601        let result = variant_get(&variant_array, options).unwrap();
3602        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
3603
3604        assert_eq!(result.precision(), 18);
3605        assert_eq!(result.scale(), 2);
3606        assert_eq!(result.value(0), 1234);
3607        assert_eq!(result.value(1), 123);
3608        assert_eq!(result.value(2), 123400);
3609        assert!(result.is_null(3));
3610        assert_eq!(
3611            result.value(4),
3612            VariantDecimal8::MAX_UNSCALED_VALUE / 10 + 1
3613        ); // should not be null as the final result fits into Decimal64
3614    }
3615
3616    #[test]
3617    fn get_decimal64_scale_down_rounding() {
3618        let mut builder = crate::VariantArrayBuilder::new(7);
3619        builder.append_variant(VariantDecimal8::try_new(1235, 0).unwrap().into());
3620        builder.append_variant(VariantDecimal8::try_new(1245, 0).unwrap().into());
3621        builder.append_variant(VariantDecimal8::try_new(-1235, 0).unwrap().into());
3622        builder.append_variant(VariantDecimal8::try_new(-1245, 0).unwrap().into());
3623        builder.append_variant(VariantDecimal8::try_new(1235, 2).unwrap().into()); // 12.35 rounded down to 10 for scale -1
3624        builder.append_variant(VariantDecimal8::try_new(1235, 3).unwrap().into()); // 1.235 rounded down to 0 for scale -1
3625        builder.append_variant(VariantDecimal8::try_new(5235, 3).unwrap().into()); // 5.235 rounded up to 10 for scale -1
3626        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3627
3628        let field = Field::new("result", DataType::Decimal64(18, -1), true);
3629        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3630        let result = variant_get(&variant_array, options).unwrap();
3631        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
3632
3633        assert_eq!(result.precision(), 18);
3634        assert_eq!(result.scale(), -1);
3635        assert_eq!(result.value(0), 124);
3636        assert_eq!(result.value(1), 125);
3637        assert_eq!(result.value(2), -124);
3638        assert_eq!(result.value(3), -125);
3639        assert_eq!(result.value(4), 1);
3640        assert!(result.is_valid(5));
3641        assert_eq!(result.value(5), 0);
3642        assert_eq!(result.value(6), 1);
3643    }
3644
3645    #[test]
3646    fn get_decimal64_large_scale_reduction() {
3647        let mut builder = crate::VariantArrayBuilder::new(2);
3648        builder.append_variant(
3649            VariantDecimal8::try_new(-VariantDecimal8::MAX_UNSCALED_VALUE, 0)
3650                .unwrap()
3651                .into(),
3652        );
3653        builder.append_variant(
3654            VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0)
3655                .unwrap()
3656                .into(),
3657        );
3658        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3659
3660        let field = Field::new("result", DataType::Decimal64(18, -18), true);
3661        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3662        let result = variant_get(&variant_array, options).unwrap();
3663        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
3664
3665        assert_eq!(result.precision(), 18);
3666        assert_eq!(result.scale(), -18);
3667        assert_eq!(result.value(0), -1);
3668        assert_eq!(result.value(1), 1);
3669
3670        let field = Field::new("result", DataType::Decimal64(18, -19), true);
3671        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3672        let result = variant_get(&variant_array, options).unwrap();
3673        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
3674
3675        assert_eq!(result.precision(), 18);
3676        assert_eq!(result.scale(), -19);
3677        assert!(result.is_valid(0));
3678        assert_eq!(result.value(0), 0);
3679        assert!(result.is_valid(1));
3680        assert_eq!(result.value(1), 0);
3681    }
3682
3683    #[test]
3684    fn get_decimal64_precision_overflow_safe() {
3685        // Exceed Decimal64 after scaling and rounding
3686        let mut builder = crate::VariantArrayBuilder::new(2);
3687        builder.append_variant(
3688            VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0)
3689                .unwrap()
3690                .into(),
3691        );
3692        builder.append_variant(
3693            VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 18)
3694                .unwrap()
3695                .into(),
3696        ); // integer value round up overflows
3697        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3698
3699        let field = Field::new("result", DataType::Decimal64(2, 2), true);
3700        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3701        let result = variant_get(&variant_array, options).unwrap();
3702        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
3703
3704        assert!(result.is_null(0));
3705        assert!(result.is_null(1));
3706    }
3707
3708    #[test]
3709    fn get_decimal64_precision_overflow_unsafe_errors() {
3710        let mut builder = crate::VariantArrayBuilder::new(1);
3711        builder.append_variant(
3712            VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0)
3713                .unwrap()
3714                .into(),
3715        );
3716        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3717
3718        let field = Field::new("result", DataType::Decimal64(18, 2), true);
3719        let cast_options = CastOptions {
3720            safe: false,
3721            ..Default::default()
3722        };
3723        let options = GetOptions::new()
3724            .with_as_type(Some(FieldRef::from(field)))
3725            .with_cast_options(cast_options);
3726        let err = variant_get(&variant_array, options).unwrap_err();
3727
3728        assert!(
3729            err.to_string().contains(
3730                "Failed to cast to Decimal64(precision=18, scale=2) from variant Decimal8"
3731            )
3732        );
3733    }
3734
3735    #[test]
3736    fn get_decimal128_rescaled_to_scale2() {
3737        let mut builder = crate::VariantArrayBuilder::new(4);
3738        builder.append_variant(VariantDecimal16::try_new(1234, 2).unwrap().into());
3739        builder.append_variant(VariantDecimal16::try_new(1234, 3).unwrap().into());
3740        builder.append_variant(VariantDecimal16::try_new(1234, 0).unwrap().into());
3741        builder.append_null();
3742        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3743
3744        let field = Field::new("result", DataType::Decimal128(38, 2), true);
3745        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3746        let result = variant_get(&variant_array, options).unwrap();
3747        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
3748
3749        assert_eq!(result.precision(), 38);
3750        assert_eq!(result.scale(), 2);
3751        assert_eq!(result.value(0), 1234);
3752        assert_eq!(result.value(1), 123);
3753        assert_eq!(result.value(2), 123400);
3754        assert!(result.is_null(3));
3755    }
3756
3757    #[test]
3758    fn get_decimal128_scale_down_rounding() {
3759        let mut builder = crate::VariantArrayBuilder::new(7);
3760        builder.append_variant(VariantDecimal16::try_new(1235, 0).unwrap().into());
3761        builder.append_variant(VariantDecimal16::try_new(1245, 0).unwrap().into());
3762        builder.append_variant(VariantDecimal16::try_new(-1235, 0).unwrap().into());
3763        builder.append_variant(VariantDecimal16::try_new(-1245, 0).unwrap().into());
3764        builder.append_variant(VariantDecimal16::try_new(1235, 2).unwrap().into()); // 12.35 rounded down to 10 for scale -1
3765        builder.append_variant(VariantDecimal16::try_new(1235, 3).unwrap().into()); // 1.235 rounded down to 0 for scale -1
3766        builder.append_variant(VariantDecimal16::try_new(5235, 3).unwrap().into()); // 5.235 rounded up to 10 for scale -1
3767        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3768
3769        let field = Field::new("result", DataType::Decimal128(38, -1), true);
3770        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3771        let result = variant_get(&variant_array, options).unwrap();
3772        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
3773
3774        assert_eq!(result.precision(), 38);
3775        assert_eq!(result.scale(), -1);
3776        assert_eq!(result.value(0), 124);
3777        assert_eq!(result.value(1), 125);
3778        assert_eq!(result.value(2), -124);
3779        assert_eq!(result.value(3), -125);
3780        assert_eq!(result.value(4), 1);
3781        assert!(result.is_valid(5));
3782        assert_eq!(result.value(5), 0);
3783        assert_eq!(result.value(6), 1);
3784    }
3785
3786    #[test]
3787    fn get_decimal128_precision_overflow_safe() {
3788        // Exceed Decimal128 after scaling and rounding
3789        let mut builder = crate::VariantArrayBuilder::new(2);
3790        builder.append_variant(
3791            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0)
3792                .unwrap()
3793                .into(),
3794        );
3795        builder.append_variant(
3796            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 38)
3797                .unwrap()
3798                .into(),
3799        ); // integer value round up overflows
3800        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3801
3802        let field = Field::new("result", DataType::Decimal128(2, 2), true);
3803        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3804        let result = variant_get(&variant_array, options).unwrap();
3805        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
3806
3807        assert!(result.is_null(0));
3808        assert!(result.is_null(1)); // should overflow because 1.00 does not fit into precision (2)
3809    }
3810
3811    #[test]
3812    fn get_decimal128_precision_overflow_unsafe_errors() {
3813        let mut builder = crate::VariantArrayBuilder::new(1);
3814        builder.append_variant(
3815            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0)
3816                .unwrap()
3817                .into(),
3818        );
3819        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3820
3821        let field = Field::new("result", DataType::Decimal128(38, 2), true);
3822        let cast_options = CastOptions {
3823            safe: false,
3824            ..Default::default()
3825        };
3826        let options = GetOptions::new()
3827            .with_as_type(Some(FieldRef::from(field)))
3828            .with_cast_options(cast_options);
3829        let err = variant_get(&variant_array, options).unwrap_err();
3830
3831        assert!(err.to_string().contains(
3832            "Failed to cast to Decimal128(precision=38, scale=2) from variant Decimal16"
3833        ));
3834    }
3835
3836    #[test]
3837    fn get_decimal256_rescaled_to_scale2() {
3838        // Build unshredded variant values with different scales using Decimal16 source
3839        let mut builder = crate::VariantArrayBuilder::new(4);
3840        builder.append_variant(VariantDecimal16::try_new(1234, 2).unwrap().into()); // 12.34
3841        builder.append_variant(VariantDecimal16::try_new(1234, 3).unwrap().into()); // 1.234
3842        builder.append_variant(VariantDecimal16::try_new(1234, 0).unwrap().into()); // 1234
3843        builder.append_null();
3844        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3845
3846        let field = Field::new("result", DataType::Decimal256(76, 2), true);
3847        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3848        let result = variant_get(&variant_array, options).unwrap();
3849        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
3850
3851        assert_eq!(result.precision(), 76);
3852        assert_eq!(result.scale(), 2);
3853        assert_eq!(result.value(0), i256::from_i128(1234));
3854        assert_eq!(result.value(1), i256::from_i128(123));
3855        assert_eq!(result.value(2), i256::from_i128(123400));
3856        assert!(result.is_null(3));
3857    }
3858
3859    #[test]
3860    fn get_decimal256_scale_down_rounding() {
3861        let mut builder = crate::VariantArrayBuilder::new(7);
3862        builder.append_variant(VariantDecimal16::try_new(1235, 0).unwrap().into());
3863        builder.append_variant(VariantDecimal16::try_new(1245, 0).unwrap().into());
3864        builder.append_variant(VariantDecimal16::try_new(-1235, 0).unwrap().into());
3865        builder.append_variant(VariantDecimal16::try_new(-1245, 0).unwrap().into());
3866        builder.append_variant(VariantDecimal16::try_new(1235, 2).unwrap().into()); // 12.35 rounded down to 10 for scale -1
3867        builder.append_variant(VariantDecimal16::try_new(1235, 3).unwrap().into()); // 1.235 rounded down to 0 for scale -1
3868        builder.append_variant(VariantDecimal16::try_new(5235, 3).unwrap().into()); // 5.235 rounded up to 10 for scale -1
3869        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3870
3871        let field = Field::new("result", DataType::Decimal256(76, -1), true);
3872        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3873        let result = variant_get(&variant_array, options).unwrap();
3874        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
3875
3876        assert_eq!(result.precision(), 76);
3877        assert_eq!(result.scale(), -1);
3878        assert_eq!(result.value(0), i256::from_i128(124));
3879        assert_eq!(result.value(1), i256::from_i128(125));
3880        assert_eq!(result.value(2), i256::from_i128(-124));
3881        assert_eq!(result.value(3), i256::from_i128(-125));
3882        assert_eq!(result.value(4), i256::from_i128(1));
3883        assert!(result.is_valid(5));
3884        assert_eq!(result.value(5), i256::from_i128(0));
3885        assert_eq!(result.value(6), i256::from_i128(1));
3886    }
3887
3888    #[test]
3889    fn get_decimal256_precision_overflow_safe() {
3890        // Exceed Decimal128 max precision (38) after scaling
3891        let mut builder = crate::VariantArrayBuilder::new(2);
3892        builder.append_variant(
3893            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 1)
3894                .unwrap()
3895                .into(),
3896        );
3897        builder.append_variant(
3898            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0)
3899                .unwrap()
3900                .into(),
3901        );
3902        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3903
3904        let field = Field::new("result", DataType::Decimal256(76, 39), true);
3905        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3906        let result = variant_get(&variant_array, options).unwrap();
3907        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
3908
3909        // Input is Decimal16 with integer = 10^38-1 and scale = 1, target scale = 39
3910        // So expected integer is (10^38-1) * 10^(39-1) = (10^38-1) * 10^38
3911        let base = i256::from_i128(10);
3912        let factor = base.checked_pow(38).unwrap();
3913        let expected = i256::from_i128(VariantDecimal16::MAX_UNSCALED_VALUE)
3914            .checked_mul(factor)
3915            .unwrap();
3916        assert_eq!(result.value(0), expected);
3917        assert!(result.is_null(1));
3918    }
3919
3920    #[test]
3921    fn get_decimal256_precision_overflow_unsafe_errors() {
3922        // Exceed Decimal128 max precision (38) after scaling
3923        let mut builder = crate::VariantArrayBuilder::new(2);
3924        builder.append_variant(
3925            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 1)
3926                .unwrap()
3927                .into(),
3928        );
3929        builder.append_variant(
3930            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0)
3931                .unwrap()
3932                .into(),
3933        );
3934        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3935
3936        let field = Field::new("result", DataType::Decimal256(76, 39), true);
3937        let cast_options = CastOptions {
3938            safe: false,
3939            ..Default::default()
3940        };
3941        let options = GetOptions::new()
3942            .with_as_type(Some(FieldRef::from(field)))
3943            .with_cast_options(cast_options);
3944        let err = variant_get(&variant_array, options).unwrap_err();
3945
3946        assert!(err.to_string().contains(
3947            "Failed to cast to Decimal256(precision=76, scale=39) from variant Decimal16"
3948        ));
3949    }
3950
3951    #[test]
3952    fn get_non_supported_temporal_types_error() {
3953        let values = vec![None, Some(Variant::Null), Some(Variant::BooleanFalse)];
3954        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter(values));
3955
3956        let test_cases = vec![
3957            FieldRef::from(Field::new(
3958                "result",
3959                DataType::Duration(TimeUnit::Microsecond),
3960                true,
3961            )),
3962            FieldRef::from(Field::new(
3963                "result",
3964                DataType::Interval(IntervalUnit::YearMonth),
3965                true,
3966            )),
3967        ];
3968
3969        for field in test_cases {
3970            let options = GetOptions::new().with_as_type(Some(field));
3971            let err = variant_get(&variant_array, options).unwrap_err();
3972            assert!(
3973                err.to_string()
3974                    .contains("Casting Variant to duration/interval types is not supported")
3975            );
3976        }
3977    }
3978
3979    fn invalid_time_variant_array() -> ArrayRef {
3980        let mut builder = VariantArrayBuilder::new(3);
3981        // 86401000000 is invalid for Time64Microsecond (max is 86400000000)
3982        builder.append_variant(Variant::Int64(86401000000));
3983        builder.append_variant(Variant::Int64(86401000000));
3984        builder.append_variant(Variant::Int64(86401000000));
3985        Arc::new(builder.build().into_inner())
3986    }
3987
3988    #[test]
3989    fn test_variant_get_error_when_cast_failure_and_safe_false() {
3990        let variant_array = invalid_time_variant_array();
3991
3992        let field = Field::new("result", DataType::Time64(TimeUnit::Microsecond), true);
3993        let cast_options = CastOptions {
3994            safe: false, // Will error on cast failure
3995            ..Default::default()
3996        };
3997        let options = GetOptions::new()
3998            .with_as_type(Some(FieldRef::from(field)))
3999            .with_cast_options(cast_options);
4000        let err = variant_get(&variant_array, options).unwrap_err();
4001        assert!(
4002            err.to_string().contains(
4003                "Cast error: Failed to extract primitive of type Time64(µs) from variant Int64(86401000000) at path VariantPath([])"
4004            ),
4005            "actual: {err}",
4006        );
4007    }
4008
4009    #[test]
4010    fn test_variant_get_return_null_when_cast_failure_and_safe_true() {
4011        let variant_array = invalid_time_variant_array();
4012
4013        let field = Field::new("result", DataType::Time64(TimeUnit::Microsecond), true);
4014        let cast_options = CastOptions {
4015            safe: true, // Will return null on cast failure
4016            ..Default::default()
4017        };
4018        let options = GetOptions::new()
4019            .with_as_type(Some(FieldRef::from(field)))
4020            .with_cast_options(cast_options);
4021        let result = variant_get(&variant_array, options).unwrap();
4022        assert_eq!(3, result.len());
4023
4024        for i in 0..3 {
4025            assert!(result.is_null(i));
4026        }
4027    }
4028
4029    #[test]
4030    fn test_perfect_shredding_returns_same_arc_ptr() {
4031        let variant_array = perfectly_shredded_int32_variant_array();
4032
4033        let variant_array_ref = VariantArray::try_new(&variant_array).unwrap();
4034        let typed_value_arc = variant_array_ref.typed_value_field().unwrap().clone();
4035
4036        let field = Field::new("result", DataType::Int32, true);
4037        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4038        let result = variant_get(&variant_array, options).unwrap();
4039
4040        assert!(Arc::ptr_eq(&typed_value_arc, &result));
4041    }
4042
4043    #[test]
4044    fn test_perfect_shredding_three_typed_value_columns() {
4045        // Column 1: perfectly shredded primitive with all nulls
4046        let all_nulls_values: Arc<Int32Array> = Arc::new(Int32Array::from(vec![
4047            Option::<i32>::None,
4048            Option::<i32>::None,
4049            Option::<i32>::None,
4050        ]));
4051        let all_nulls_erased: ArrayRef = all_nulls_values.clone();
4052        let all_nulls_field =
4053            ShreddedVariantFieldArray::from_parts(None, Some(all_nulls_erased.clone()), None);
4054        let all_nulls_type = all_nulls_field.data_type().clone();
4055        let all_nulls_struct: ArrayRef = ArrayRef::from(all_nulls_field);
4056
4057        // Column 2: perfectly shredded primitive with some nulls
4058        let some_nulls_values: Arc<Int32Array> =
4059            Arc::new(Int32Array::from(vec![Some(10), None, Some(30)]));
4060        let some_nulls_erased: ArrayRef = some_nulls_values.clone();
4061        let some_nulls_field =
4062            ShreddedVariantFieldArray::from_parts(None, Some(some_nulls_erased.clone()), None);
4063        let some_nulls_type = some_nulls_field.data_type().clone();
4064        let some_nulls_struct: ArrayRef = ArrayRef::from(some_nulls_field);
4065
4066        // Column 3: perfectly shredded nested struct
4067        let inner_values: Arc<Int32Array> =
4068            Arc::new(Int32Array::from(vec![Some(111), None, Some(333)]));
4069        let inner_erased: ArrayRef = inner_values.clone();
4070        let inner_field =
4071            ShreddedVariantFieldArray::from_parts(None, Some(inner_erased.clone()), None);
4072        let inner_field_type = inner_field.data_type().clone();
4073        let inner_struct_array: ArrayRef = ArrayRef::from(inner_field);
4074
4075        let nested_struct = Arc::new(
4076            StructArray::try_new(
4077                Fields::from(vec![Field::new("inner", inner_field_type, true)]),
4078                vec![inner_struct_array],
4079                None,
4080            )
4081            .unwrap(),
4082        );
4083        let nested_struct_erased: ArrayRef = nested_struct.clone();
4084        let struct_field =
4085            ShreddedVariantFieldArray::from_parts(None, Some(nested_struct_erased.clone()), None);
4086        let struct_field_type = struct_field.data_type().clone();
4087        let struct_field_struct: ArrayRef = ArrayRef::from(struct_field);
4088
4089        // Assemble the top-level typed_value struct with the three columns above
4090        let typed_value_struct = StructArray::try_new(
4091            Fields::from(vec![
4092                Field::new("all_nulls", all_nulls_type, true),
4093                Field::new("some_nulls", some_nulls_type, true),
4094                Field::new("struct_field", struct_field_type, true),
4095            ]),
4096            vec![all_nulls_struct, some_nulls_struct, struct_field_struct],
4097            None,
4098        )
4099        .unwrap();
4100
4101        let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(
4102            EMPTY_VARIANT_METADATA_BYTES,
4103            all_nulls_values.len(),
4104        ));
4105        let variant_struct = StructArrayBuilder::new()
4106            .with_field("metadata", Arc::new(metadata), false)
4107            .with_field("typed_value", Arc::new(typed_value_struct), true)
4108            .build();
4109        let variant_array: ArrayRef = VariantArray::try_new(&variant_struct).unwrap().into();
4110
4111        // Case 1: all-null primitive column should reuse the typed_value Arc directly
4112        let all_nulls_field_ref = FieldRef::from(Field::new("result", DataType::Int32, true));
4113        let all_nulls_result = variant_get(
4114            &variant_array,
4115            GetOptions::new_with_path(VariantPath::from("all_nulls"))
4116                .with_as_type(Some(all_nulls_field_ref)),
4117        )
4118        .unwrap();
4119        assert!(Arc::ptr_eq(&all_nulls_result, &all_nulls_erased));
4120
4121        // Case 2: primitive column with some nulls should also reuse its typed_value Arc
4122        let some_nulls_field_ref = FieldRef::from(Field::new("result", DataType::Int32, true));
4123        let some_nulls_result = variant_get(
4124            &variant_array,
4125            GetOptions::new_with_path(VariantPath::from("some_nulls"))
4126                .with_as_type(Some(some_nulls_field_ref)),
4127        )
4128        .unwrap();
4129        assert!(Arc::ptr_eq(&some_nulls_result, &some_nulls_erased));
4130
4131        // Case 3: struct column should return a StructArray composed from the nested field
4132        let struct_child_fields = Fields::from(vec![Field::new("inner", DataType::Int32, true)]);
4133        let struct_field_ref = FieldRef::from(Field::new(
4134            "result",
4135            DataType::Struct(struct_child_fields.clone()),
4136            true,
4137        ));
4138        let struct_result = variant_get(
4139            &variant_array,
4140            GetOptions::new_with_path(VariantPath::from("struct_field"))
4141                .with_as_type(Some(struct_field_ref)),
4142        )
4143        .unwrap();
4144        let struct_array = struct_result
4145            .as_any()
4146            .downcast_ref::<StructArray>()
4147            .unwrap();
4148        assert_eq!(struct_array.len(), 3);
4149        assert_eq!(struct_array.null_count(), 0);
4150
4151        let inner_values_result = struct_array
4152            .column(0)
4153            .as_any()
4154            .downcast_ref::<Int32Array>()
4155            .unwrap();
4156        assert_eq!(inner_values_result.len(), 3);
4157        assert_eq!(inner_values_result.value(0), 111);
4158        assert!(inner_values_result.is_null(1));
4159        assert_eq!(inner_values_result.value(2), 333);
4160    }
4161}