Skip to main content

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