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