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::{
19        self, Array, ArrayRef, GenericListArray, GenericListViewArray, ListLikeArray, StructArray,
20        UInt64Array, make_array,
21    },
22    buffer::NullBuffer,
23    compute::{CastOptions, take},
24    datatypes::Field,
25    error::Result,
26};
27use arrow_schema::{ArrowError, DataType, FieldRef};
28use parquet_variant::{VariantPath, VariantPathElement};
29
30use crate::ShreddingState;
31use crate::variant_array::all_null_value_column;
32use crate::variant_to_arrow::make_variant_to_arrow_row_builder;
33use crate::{VariantArray, VariantType, unshred_variant};
34
35use arrow::array::AsArray;
36use std::sync::Arc;
37
38pub(crate) enum ShreddedPathStep {
39    /// Path step succeeded, return the new shredding state
40    Success(ShreddingState),
41    /// The path element is not present in the `typed_value` column and the `value` column is
42    /// all-null, so we know it does not exist. It, and all paths under it, are all-NULL.
43    Missing,
44    /// The path element is not present in the `typed_value` column and must be retrieved from the `value`
45    /// column instead. The caller should be prepared to handle any value, including the requested
46    /// type, an arbitrary "wrong" type, or `Variant::Null`.
47    NotShredded,
48}
49
50/// Build the next shredding state by taking one list-like element (at `index`) per input row.
51///
52fn take_list_like_index_as_shredding_state<L: ListLikeArray + 'static>(
53    typed_value: &dyn Array,
54    index: usize,
55) -> Result<Option<ShreddingState>> {
56    let list_array = typed_value.as_any().downcast_ref::<L>().ok_or_else(|| {
57        ArrowError::ComputeError(format!(
58            "Expected array type '{}' while handling list-like path step, got '{}'",
59            std::any::type_name::<L>(),
60            typed_value.data_type()
61        ))
62    })?;
63
64    let values = list_array.values();
65
66    let Some(struct_array) = values.as_struct_opt() else {
67        return Ok(None);
68    };
69    let shredding_state = ShreddingState::try_from(struct_array)?;
70
71    let value_array = shredding_state.value_column();
72    let typed_array = shredding_state.typed_value_column();
73
74    // If list elements have neither typed nor fallback values, this path step is missing.
75    if typed_array.is_none() && value_array.null_count() == value_array.len() {
76        return Ok(None);
77    }
78
79    let mut take_indices = Vec::with_capacity(list_array.len());
80    for row in 0..list_array.len() {
81        let row_range = list_array.element_range(row);
82        let take_index = (index < row_range.len()).then(|| (row_range.start + index) as u64);
83        take_indices.push(take_index);
84    }
85
86    let index_array = UInt64Array::from(take_indices);
87
88    // Gather both typed and fallback values at the requested element index.
89    let taken_value = take(value_array, &index_array, None)?;
90    let taken_typed = typed_array
91        .map(|typed| take(typed, &index_array, None))
92        .transpose()?;
93
94    Ok(Some(ShreddingState::new(taken_value, taken_typed)))
95}
96
97/// Given a shredded variant field -- a `(value?, typed_value?)` pair -- try to take one path step
98/// deeper. For a `VariantPathElement::Field`, if there is no `typed_value` at this level, if
99/// `typed_value` is not a struct, or if the requested field name does not exist, traversal returns
100/// a missing-path step (`Missing` or `NotShredded` depending on whether `value` exists).
101///
102/// Safe-cast behavior (`cast_options.safe = true`):
103/// - Type mismatch during path traversal (for example field access on non-struct, index access on
104///   non-list) returns [`ShreddedPathStep::Missing`] or [`ShreddedPathStep::NotShredded`], allowing
105///   the caller to continue with null/fallback semantics.
106/// - List index out-of-bounds produces nulls for the corresponding rows.
107///
108/// Unsafe-cast behavior (`cast_options.safe = false`):
109/// - Field access on non-struct returns [`ArrowError::CastError`].
110/// - List index path steps follow JSONPath semantics and return missing/null for non-list or
111///   out-of-bounds rows.
112pub(crate) fn follow_shredded_path_element(
113    shredding_state: &ShreddingState,
114    path_element: &VariantPathElement<'_>,
115    _cast_options: &CastOptions,
116) -> Result<ShreddedPathStep> {
117    // If the requested path element is not present in `typed_value`, and `value` is all-null, then
118    // we know it does not exist; it, and all paths under it, are all-NULL.
119    let missing_path_step = || {
120        let value = shredding_state.value_column();
121        if value.null_count() == value.len() {
122            ShreddedPathStep::Missing
123        } else {
124            ShreddedPathStep::NotShredded
125        }
126    };
127
128    let Some(typed_value) = shredding_state.typed_value_column() else {
129        return Ok(missing_path_step());
130    };
131
132    match path_element {
133        VariantPathElement::Field { name } => {
134            // Try to step into the requested field name of a struct.
135            // First, try to downcast to StructArray
136            let Some(struct_array) = typed_value.as_struct_opt() else {
137                // Object field path step follows JSONPath semantics and returns missing path step (NotShredded/Missing) on non-struct path
138                return Ok(missing_path_step());
139            };
140
141            // Now try to find the column - missing column in a present struct is just missing data
142            let Some(field) = struct_array.column_by_name(name) else {
143                // Missing column in a present struct is just missing, not wrong - return Ok
144                return Ok(missing_path_step());
145            };
146
147            let struct_array = field.as_struct_opt().ok_or_else(|| {
148                // Each named child of a shredded object represents a shredded Variant field,
149                // whose physical layout is a Struct containing `value` and/or `typed_value`.
150                ArrowError::InvalidArgumentError(format!(
151                    "Shredded object field '{name}' must be a Struct containing 'value' and/or \
152                     'typed_value', got {}",
153                    field.data_type(),
154                ))
155            })?;
156
157            let state = ShreddingState::try_from(struct_array)?;
158            Ok(ShreddedPathStep::Success(state))
159        }
160        VariantPathElement::Index { index } => {
161            let state = match typed_value.data_type() {
162                DataType::List(_) => take_list_like_index_as_shredding_state::<
163                    GenericListArray<i32>,
164                >(typed_value.as_ref(), *index)?,
165                DataType::LargeList(_) => take_list_like_index_as_shredding_state::<
166                    GenericListArray<i64>,
167                >(typed_value.as_ref(), *index)?,
168                DataType::ListView(_) => take_list_like_index_as_shredding_state::<
169                    GenericListViewArray<i32>,
170                >(typed_value.as_ref(), *index)?,
171                DataType::LargeListView(_) => take_list_like_index_as_shredding_state::<
172                    GenericListViewArray<i64>,
173                >(typed_value.as_ref(), *index)?,
174                _ => {
175                    // JSONPath semantics: indexing a non-list yields no match.
176                    return Ok(missing_path_step());
177                }
178            };
179
180            match state {
181                Some(state) => Ok(ShreddedPathStep::Success(state)),
182                None => Ok(missing_path_step()),
183            }
184        }
185        VariantPathElement::ListElement => Err(ArrowError::InvalidArgumentError(
186            "variant_get does not support [*] path elements".to_string(),
187        )),
188    }
189}
190
191/// Follows the given path as far as possible through shredded variant fields. If the path ends on a
192/// shredded field, return it directly. Otherwise, use a row shredder to follow the rest of the path
193/// and extract the requested value on a per-row basis.
194fn shredded_get_path(
195    input: &VariantArray,
196    path: &[VariantPathElement<'_>],
197    as_field: Option<&Field>,
198    cast_options: &CastOptions,
199) -> Result<ArrayRef> {
200    // Helper that creates a new VariantArray from the given nested value and typed_value columns,
201    // properly accounting for accumulated nulls from path traversal
202    let make_target_variant =
203        |value: ArrayRef, typed_value: Option<ArrayRef>, accumulated_nulls: Option<NullBuffer>| {
204            let metadata = input.metadata_column().clone();
205            VariantArray::from_parts(metadata, value, typed_value, accumulated_nulls)
206        };
207
208    // Helper that extracts the value at `path` and casts it to the requested type, or returns it as
209    // an unshredded binary variant when `Variant` output is requested.
210    let shred_basic_variant =
211        |target: VariantArray, path: VariantPath<'_>, as_field: Option<&Field>| {
212            // A `VariantType` extension on `as_field` requests `Variant` output: return an
213            // unshredded binary variant instead of casting to a concrete Arrow type.
214            let requested_variant =
215                as_field.is_some_and(Field::has_valid_extension_type::<VariantType>);
216
217            // A `typed_value` in that field requests shredded output -- a `VariantArray` with
218            // `typed_value` columns. We produce only unshredded variant output. Shredded output is
219            // tracked in https://github.com/apache/arrow-rs/issues/8153. Reject such a request
220            // instead of silently dropping the shredding it asked for.
221            if requested_variant && requested_field_is_shredded(as_field) {
222                return Err(ArrowError::NotYetImplemented(
223                    "variant_get with shredded `Variant` output is not yet supported".to_string(),
224                ));
225            }
226
227            // Collapse any shredding back to binary. Only the `NotShredded` step below passes a
228            // non-empty `path`, and there `target` is already a plain `value` column (no
229            // `typed_value`) -- so `unshred_variant` hits its clone fast-path, with nothing deeper
230            // to shred. The builder then walks any remaining path per-row, emitting variant output
231            // because `as_type` is `None`.
232            let target = if requested_variant {
233                unshred_variant(&target)?
234            } else {
235                target
236            };
237
238            // Path exhausted, variant requested: return the target directly.
239            if requested_variant && path.is_empty() {
240                return Ok(ArrayRef::from(target));
241            }
242
243            let as_type = if requested_variant {
244                None
245            } else {
246                as_field.map(|f| f.data_type())
247            };
248            let mut builder = make_variant_to_arrow_row_builder(
249                target.metadata_column(),
250                path,
251                as_type,
252                cast_options,
253                target.len(),
254            )?;
255            for i in 0..target.len() {
256                if target.is_null(i) {
257                    builder.append_null()?;
258                } else if !cast_options.safe {
259                    let value = target.try_value(i)?;
260                    builder.append_value(value)?;
261                } else {
262                    let _ = match target.try_value(i) {
263                        Ok(v) => builder.append_value(v)?,
264                        Err(_) => {
265                            builder.append_null()?;
266                            false // add this to make match arms have the same return type
267                        }
268                    };
269                }
270            }
271            builder.finish()
272        };
273
274    // Peel away the prefix of path elements that traverses the shredded parts of this variant
275    // column. Shredding will traverse the rest of the path on a per-row basis.
276    let mut shredding_state = input.shredding_state().clone();
277    let mut accumulated_nulls = input.inner().nulls().cloned();
278    let mut path_index = 0;
279    for path_element in path {
280        match follow_shredded_path_element(&shredding_state, path_element, cast_options)? {
281            ShreddedPathStep::Success(state) => {
282                // Union nulls from the typed_value we just accessed
283                if let Some(typed_value) = shredding_state.typed_value_column() {
284                    accumulated_nulls =
285                        NullBuffer::union(accumulated_nulls.as_ref(), typed_value.nulls());
286                }
287                shredding_state = state;
288                path_index += 1;
289            }
290            ShreddedPathStep::Missing => {
291                let num_rows = input.len();
292                if as_field.is_some_and(Field::has_valid_extension_type::<VariantType>) {
293                    let all_nulls = Some(arrow::buffer::NullBuffer::from(vec![false; num_rows]));
294                    // Propagating metadata is not necessary for an all-NULL array, but is cheaper than constructing
295                    // a new empty metadata array. (n * 3 bytes vs Arc bump)
296                    let metadata = input.metadata_column().clone();
297                    let arr = VariantArray::from_parts_unshredded(
298                        metadata,
299                        all_null_value_column(num_rows),
300                        all_nulls,
301                    );
302                    return Ok(ArrayRef::from(arr));
303                }
304                let arr = match as_field.map(|f| f.data_type()) {
305                    Some(data_type) => array::new_null_array(data_type, num_rows),
306                    None => Arc::new(array::NullArray::new(num_rows)) as _,
307                };
308                return Ok(arr);
309            }
310            ShreddedPathStep::NotShredded => {
311                let target = make_target_variant(
312                    shredding_state.value_column().clone(),
313                    None,
314                    accumulated_nulls,
315                );
316                return shred_basic_variant(target, path[path_index..].into(), as_field);
317            }
318        }
319    }
320
321    // Path exhausted! Create a new `VariantArray` for the location we landed on.
322    let target = make_target_variant(
323        shredding_state.value_column().clone(),
324        shredding_state.typed_value_column().cloned(),
325        accumulated_nulls,
326    );
327
328    // If our caller did not request any specific type, we can just return whatever we landed on.
329    let Some(as_field) = as_field else {
330        return Ok(ArrayRef::from(target));
331    };
332
333    // Try to return the typed value directly when we have a perfect shredding match.
334    if let Some(shredded) = try_perfect_shredding(&target, as_field) {
335        return Ok(shredded);
336    }
337
338    // Structs are special.
339    //
340    // For fully unshredded targets (`typed_value` absent), delegate to the row builder so we
341    // preserve struct-level cast semantics:
342    // - safe mode: non-object rows become NULL structs
343    // - strict mode: non-object rows raise a cast error
344    //
345    // For shredded/partially-shredded targets (`typed_value` present), recurse into each field
346    // separately to take advantage of deeper shredding in child fields.
347    if !as_field.has_valid_extension_type::<VariantType>()
348        && let DataType::Struct(fields) = as_field.data_type()
349    {
350        if target.typed_value_column().is_none() {
351            return shred_basic_variant(target, VariantPath::default(), Some(as_field));
352        }
353
354        let children = fields
355            .iter()
356            .map(|field| {
357                let path = &[VariantPathElement::from(field.name().as_str())];
358                shredded_get_path(&target, path, Some(field), cast_options)
359            })
360            .collect::<Result<Vec<_>>>()?;
361
362        return Ok(Arc::new(StructArray::try_new(
363            fields.clone(),
364            children,
365            target.nulls().cloned(),
366        )?));
367    }
368
369    // Not a struct, so directly shred the variant as the requested type
370    shred_basic_variant(target, VariantPath::default(), Some(as_field))
371}
372
373/// Returns true if `as_field` requests *shredded* `Variant` output.
374///
375/// Its struct carries a `typed_value` field naming the type to shred to.
376/// A plain variant request has only `metadata` and `value`.
377fn requested_field_is_shredded(as_field: Option<&Field>) -> bool {
378    as_field.is_some_and(|f| match f.data_type() {
379        DataType::Struct(fields) => fields.iter().any(|field| field.name() == "typed_value"),
380        _ => false,
381    })
382}
383
384fn try_perfect_shredding(variant_array: &VariantArray, as_field: &Field) -> Option<ArrayRef> {
385    // Try to return the typed value directly when we have a perfect shredding match.
386    if matches!(as_field.data_type(), DataType::Struct(_)) {
387        return None;
388    }
389    let typed_value = variant_array.typed_value_column()?;
390
391    let value = variant_array.value_column();
392    if typed_value.data_type() == as_field.data_type() && value.null_count() == value.len() {
393        // Here we need to gate against the case where the `typed_value` is null
394        // but data is in the `value` column: only an all-null `value` column
395        // qualifies as perfect shredding.
396
397        // This is a perfect shredding, where the value is entirely shredded out,
398        // so we can just return the typed value after merging the accumulated nulls.
399        let parent_nulls = variant_array.nulls();
400
401        // If we have no nulls OR the shredded array is `Null`, which doesn't support external nulls.
402        let target_array = if parent_nulls.is_none() || typed_value.data_type().is_null() {
403            typed_value.clone()
404        } else {
405            let merged_nulls = NullBuffer::union(parent_nulls, typed_value.nulls());
406            let data = typed_value
407                .to_data()
408                .into_builder()
409                .nulls(merged_nulls)
410                .build()
411                .ok()?;
412            make_array(data)
413        };
414
415        return Some(target_array);
416    }
417
418    None
419}
420
421/// Returns an array with the specified path extracted from the variant values.
422///
423/// The return array type depends on the `as_type` field of the options parameter
424/// 1. `as_type: None`: a VariantArray is returned. The values in this new VariantArray will point
425///    to the specified path.
426/// 2. `as_type: Some(<specific field>)`: an array of the specified type is returned.
427///
428/// # Casting Semantics
429///
430/// Scalar conversion semantics intentionally follow Arrow cast behavior where applicable.
431/// Conversions in this module delegate to Arrow compute cast helpers such as
432/// `num_cast`, `cast_num_to_bool`, `single_bool_to_numeric`, and
433/// `cast_single_string_to_boolean_default`.
434///
435/// - Getting `DataType::Boolean` accepts boolean, numeric, and string variants.
436///   Numeric zero maps to `false`; non-zero maps to `true`. String parsing follows
437///   Arrow UTF8-to-boolean cast rules.
438/// - Getting numeric datatypes such as `DataType::Int8`, `DataType::Int16`, `DataType::Int32`,
439///   `DataType::Int64`, `DataType::UInt8`, `DataType::UInt16`, `DataType::UInt32`, `DataType::UInt64`,
440///   `DataType::Float16`, `DataType::Float32`, `DataType::Float64` accept
441///   boolean and numeric variants (integers, floating-point, and decimals).
442///   They return `None` when conversion is not possible.
443/// - Getting decimals such as `DataType::Decimal32`, `DataType::Decimal64`, `DataType::Decimal128`,
444///   `DataType::Decimal256` accept compatible decimal variants, integer variants,
445///   float variants and string variants.
446///   They return `None` when conversion is not possible.
447///
448/// TODO: How would a caller request a struct or list type where the fields/elements can be any
449/// variant? Caller can pass None as the requested type to fetch a specific path, but it would
450/// quickly become annoying (and inefficient) to call `variant_get` for each leaf value in a struct or
451/// list and then try to assemble the results.
452pub fn variant_get(input: &ArrayRef, options: GetOptions) -> Result<ArrayRef> {
453    let variant_array = VariantArray::try_new(input)?;
454
455    let GetOptions {
456        as_type,
457        path,
458        cast_options,
459    } = options;
460
461    if path
462        .iter()
463        .any(|element| matches!(element, VariantPathElement::ListElement))
464    {
465        return Err(ArrowError::InvalidArgumentError(
466            "variant_get does not support [*] path elements".to_string(),
467        ));
468    }
469
470    shredded_get_path(&variant_array, &path, as_type.as_deref(), &cast_options)
471}
472
473/// Controls the action of the variant_get kernel.
474#[derive(Debug, Clone, Default)]
475pub struct GetOptions<'a> {
476    /// What path to extract
477    pub path: VariantPath<'a>,
478    /// if `as_type` is None, the returned array will itself be a VariantArray.
479    ///
480    /// if `as_type` is `Some(type)` the field is returned as the specified type.
481    pub as_type: Option<FieldRef>,
482    /// Controls the casting behavior (e.g. error vs substituting null on cast error).
483    pub cast_options: CastOptions<'a>,
484}
485
486impl<'a> GetOptions<'a> {
487    /// Construct default options to get the specified path as a variant.
488    pub fn new() -> Self {
489        Default::default()
490    }
491
492    /// Construct options to get the specified path as a variant.
493    pub fn new_with_path(path: VariantPath<'a>) -> Self {
494        Self {
495            path,
496            as_type: None,
497            cast_options: Default::default(),
498        }
499    }
500
501    /// Specify the type to return.
502    pub fn with_as_type(mut self, as_type: Option<FieldRef>) -> Self {
503        self.as_type = as_type;
504        self
505    }
506
507    /// Specify the cast options to use when casting to the specified type.
508    pub fn with_cast_options(mut self, cast_options: CastOptions<'a>) -> Self {
509        self.cast_options = cast_options;
510        self
511    }
512}
513
514#[cfg(test)]
515mod test {
516    use std::str::FromStr;
517    use std::sync::Arc;
518
519    use super::{GetOptions, requested_field_is_shredded, variant_get};
520    use crate::variant_array::{ShreddedVariantFieldArray, all_null_value_column};
521    use crate::{
522        ShreddedSchemaBuilder, VariantArray, VariantArrayBuilder, cast_to_variant, json_to_variant,
523        shred_variant,
524    };
525    use arrow::array::{
526        Array, ArrayRef, AsArray, BinaryArray, BinaryViewArray, BooleanArray, Date32Array,
527        Date64Array, Decimal32Array, Decimal64Array, Decimal128Array, Decimal256Array,
528        FixedSizeListArray, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array,
529        Int64Array, Int64Builder, LargeBinaryArray, LargeListArray, LargeListViewArray,
530        LargeStringArray, ListArray, ListBuilder, ListViewArray, MapBuilder, NullArray,
531        NullBuilder, StringArray, StringBuilder, StringViewArray, StructArray,
532        StructArrayAssembler, Time32MillisecondArray, Time32SecondArray, Time64MicrosecondArray,
533        Time64NanosecondArray, UnionArray,
534    };
535    use arrow::buffer::{NullBuffer, OffsetBuffer, ScalarBuffer};
536    use arrow::compute::{CastOptions, cast};
537    use arrow::datatypes::DataType::{Int16, Int32, Int64};
538    use arrow::datatypes::i256;
539    use arrow::util::display::FormatOptions;
540    use arrow_schema::ArrowError;
541    use arrow_schema::DataType::{Boolean, Float32, Float64, Int8};
542    use arrow_schema::{
543        DataType, Field, FieldRef, Fields, IntervalUnit, TimeUnit, UnionFields, UnionMode,
544    };
545    use chrono::DateTime;
546    use parquet_variant::{
547        EMPTY_VARIANT_METADATA_BYTES, Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16,
548        VariantDecimalType, VariantPath,
549    };
550
551    fn single_variant_get_test(input_json: &str, path: VariantPath, expected_json: &str) {
552        // Create input array from JSON string
553        let input_array_ref: ArrayRef = Arc::new(StringArray::from(vec![Some(input_json)]));
554        let input_variant_array_ref = ArrayRef::from(json_to_variant(&input_array_ref).unwrap());
555
556        let result =
557            variant_get(&input_variant_array_ref, GetOptions::new_with_path(path)).unwrap();
558
559        // Create expected array from JSON string
560        let expected_array_ref: ArrayRef = Arc::new(StringArray::from(vec![Some(expected_json)]));
561        let expected_variant_array = json_to_variant(&expected_array_ref).unwrap();
562
563        let result_array = VariantArray::try_new(&result).unwrap();
564        assert_eq!(
565            result_array.len(),
566            1,
567            "Expected result array to have length 1"
568        );
569        assert!(
570            result_array.nulls().is_none(),
571            "Expected no nulls in result array"
572        );
573        let result_variant = result_array.value(0);
574        let expected_variant = expected_variant_array.value(0);
575        assert_eq!(
576            result_variant, expected_variant,
577            "Result variant does not match expected variant"
578        );
579    }
580
581    #[test]
582    fn get_primitive_variant_field() {
583        single_variant_get_test(
584            r#"{"some_field": 1234}"#,
585            VariantPath::try_from("some_field").unwrap(),
586            "1234",
587        );
588    }
589
590    #[test]
591    fn get_primitive_variant_list_index() {
592        single_variant_get_test("[1234, 5678]", VariantPath::from(0), "1234");
593    }
594
595    #[test]
596    fn get_primitive_variant_inside_object_of_object() {
597        single_variant_get_test(
598            r#"{"top_level_field": {"inner_field": 1234}}"#,
599            VariantPath::try_from("top_level_field")
600                .unwrap()
601                .join("inner_field"),
602            "1234",
603        );
604    }
605
606    #[test]
607    fn get_primitive_variant_inside_list_of_object() {
608        single_variant_get_test(
609            r#"[{"some_field": 1234}]"#,
610            VariantPath::from(0).join("some_field"),
611            "1234",
612        );
613    }
614
615    #[test]
616    fn get_primitive_variant_inside_object_of_list() {
617        single_variant_get_test(
618            r#"{"some_field": [1234]}"#,
619            VariantPath::try_from("some_field[0]").unwrap(),
620            "1234",
621        );
622    }
623
624    #[test]
625    fn get_complex_variant() {
626        single_variant_get_test(
627            r#"{"top_level_field": {"inner_field": 1234}}"#,
628            VariantPath::try_from("top_level_field").unwrap(),
629            r#"{"inner_field": 1234}"#,
630        );
631    }
632
633    /// Partial Shredding: extract a value as a VariantArray
634    macro_rules! numeric_partially_shredded_test {
635        ($primitive_type:ty, $data_fn:ident) => {
636            let array = $data_fn();
637            let options = GetOptions::new();
638            let result = variant_get(&array, options).unwrap();
639
640            // expect the result is a VariantArray
641            let result = VariantArray::try_new(&result).unwrap();
642            assert_eq!(result.len(), 4);
643
644            // Expect the values are the same as the original values
645            assert_eq!(
646                result.value(0),
647                Variant::from(<$primitive_type>::try_from(34u8).unwrap())
648            );
649            assert!(!result.is_valid(1));
650            assert_eq!(result.value(2), Variant::from("n/a"));
651            assert_eq!(
652                result.value(3),
653                Variant::from(<$primitive_type>::try_from(100u8).unwrap())
654            );
655        };
656    }
657
658    /// Build a mixed input [typed, null, fallback, typed] and let shred_variant
659    /// generate the shredded fixture for the requested type.
660    macro_rules! partially_shredded_variant_array_gen {
661        ($func_name:ident,  $typed_value_array_gen: expr) => {
662            partially_shredded_variant_array_gen!(
663                $func_name,
664                $typed_value_array_gen,
665                Variant::from("n/a")
666            );
667        };
668        ($func_name:ident,  $typed_value_array_gen: expr, $fallback_variant:expr) => {
669            fn $func_name() -> ArrayRef {
670                let typed_value: ArrayRef = Arc::new($typed_value_array_gen());
671                let typed_as_variant = cast_to_variant(typed_value.as_ref())
672                    .expect("should cast typed array to variant");
673                let mut input_builder = VariantArrayBuilder::new(typed_as_variant.len());
674                input_builder.append_variant(typed_as_variant.value(0));
675                input_builder.append_null();
676                input_builder.append_variant($fallback_variant);
677                input_builder.append_variant(typed_as_variant.value(3));
678
679                let variant_array = shred_variant(&input_builder.build(), typed_value.data_type())
680                    .expect("should shred variant array");
681                ArrayRef::from(variant_array)
682            }
683        };
684    }
685
686    // Fixture definitions grouped with the partially-shredded tests.
687    macro_rules! numeric_partially_shredded_variant_array_fn {
688        ($func:ident, $array_type:ident, $primitive_type:ty) => {
689            partially_shredded_variant_array_gen!($func, || $array_type::from(vec![
690                Some(<$primitive_type>::try_from(34u8).unwrap()),
691                None,
692                None,
693                Some(<$primitive_type>::try_from(100u8).unwrap()),
694            ]));
695        };
696    }
697
698    numeric_partially_shredded_variant_array_fn!(
699        partially_shredded_int8_variant_array,
700        Int8Array,
701        i8
702    );
703    numeric_partially_shredded_variant_array_fn!(
704        partially_shredded_int16_variant_array,
705        Int16Array,
706        i16
707    );
708    numeric_partially_shredded_variant_array_fn!(
709        partially_shredded_int32_variant_array,
710        Int32Array,
711        i32
712    );
713    numeric_partially_shredded_variant_array_fn!(
714        partially_shredded_int64_variant_array,
715        Int64Array,
716        i64
717    );
718    numeric_partially_shredded_variant_array_fn!(
719        partially_shredded_float32_variant_array,
720        Float32Array,
721        f32
722    );
723    numeric_partially_shredded_variant_array_fn!(
724        partially_shredded_float64_variant_array,
725        Float64Array,
726        f64
727    );
728
729    partially_shredded_variant_array_gen!(partially_shredded_bool_variant_array, || {
730        arrow::array::BooleanArray::from(vec![Some(true), None, None, Some(false)])
731    });
732
733    partially_shredded_variant_array_gen!(
734        partially_shredded_utf8_variant_array,
735        || { StringArray::from(vec![Some("hello"), None, None, Some("world")]) },
736        Variant::from(42i32)
737    );
738
739    partially_shredded_variant_array_gen!(partially_shredded_date32_variant_array, || {
740        Date32Array::from(vec![
741            Some(20348), // 2025-09-17
742            None,
743            None,
744            Some(20340), // 2025-09-09
745        ])
746    });
747
748    #[test]
749    fn get_variant_partially_shredded_int8_as_variant() {
750        numeric_partially_shredded_test!(i8, partially_shredded_int8_variant_array);
751    }
752
753    #[test]
754    fn get_variant_partially_shredded_int16_as_variant() {
755        numeric_partially_shredded_test!(i16, partially_shredded_int16_variant_array);
756    }
757
758    #[test]
759    fn get_variant_partially_shredded_int32_as_variant() {
760        numeric_partially_shredded_test!(i32, partially_shredded_int32_variant_array);
761    }
762
763    #[test]
764    fn get_variant_partially_shredded_int64_as_variant() {
765        numeric_partially_shredded_test!(i64, partially_shredded_int64_variant_array);
766    }
767
768    #[test]
769    fn get_variant_partially_shredded_float32_as_variant() {
770        numeric_partially_shredded_test!(f32, partially_shredded_float32_variant_array);
771    }
772
773    #[test]
774    fn get_variant_partially_shredded_float64_as_variant() {
775        numeric_partially_shredded_test!(f64, partially_shredded_float64_variant_array);
776    }
777
778    #[test]
779    fn get_variant_partially_shredded_bool_as_variant() {
780        let array = partially_shredded_bool_variant_array();
781        let options = GetOptions::new();
782        let result = variant_get(&array, options).unwrap();
783
784        // expect the result is a VariantArray
785        let result = VariantArray::try_new(&result).unwrap();
786        assert_eq!(result.len(), 4);
787
788        // Expect the values are the same as the original values
789        assert_eq!(result.value(0), Variant::from(true));
790        assert!(!result.is_valid(1));
791        assert_eq!(result.value(2), Variant::from("n/a"));
792        assert_eq!(result.value(3), Variant::from(false));
793    }
794
795    #[test]
796    fn get_variant_partially_shredded_utf8_as_variant() {
797        let array = partially_shredded_utf8_variant_array();
798        let options = GetOptions::new();
799        let result = variant_get(&array, options).unwrap();
800
801        // expect the result is a VariantArray
802        let result = VariantArray::try_new(&result).unwrap();
803        assert_eq!(result.len(), 4);
804
805        // Expect the values are the same as the original values
806        assert_eq!(result.value(0), Variant::from("hello"));
807        assert!(!result.is_valid(1));
808        assert_eq!(result.value(2), Variant::from(42i32));
809        assert_eq!(result.value(3), Variant::from("world"));
810    }
811
812    partially_shredded_variant_array_gen!(partially_shredded_binary_view_variant_array, || {
813        BinaryViewArray::from(vec![
814            Some(&[1u8, 2u8, 3u8][..]), // row 0 is shredded
815            None,                       // row 1 is null
816            None,                       // row 2 is a string
817            Some(&[4u8, 5u8, 6u8][..]), // row 3 is shredded
818        ])
819    });
820
821    #[test]
822    fn get_variant_partially_shredded_date32_as_variant() {
823        let array = partially_shredded_date32_variant_array();
824        let options = GetOptions::new();
825        let result = variant_get(&array, options).unwrap();
826
827        // expect the result is a VariantArray
828        let result = VariantArray::try_new(&result).unwrap();
829        assert_eq!(result.len(), 4);
830
831        // Expect the values are the same as the original values
832        use chrono::NaiveDate;
833        let date1 = NaiveDate::from_ymd_opt(2025, 9, 17).unwrap();
834        let date2 = NaiveDate::from_ymd_opt(2025, 9, 9).unwrap();
835        assert_eq!(result.value(0), Variant::from(date1));
836        assert!(!result.is_valid(1));
837        assert_eq!(result.value(2), Variant::from("n/a"));
838        assert_eq!(result.value(3), Variant::from(date2));
839    }
840
841    #[test]
842    fn get_variant_partially_shredded_binary_view_as_variant() {
843        let array = partially_shredded_binary_view_variant_array();
844        let options = GetOptions::new();
845        let result = variant_get(&array, options).unwrap();
846
847        // expect the result is a VariantArray
848        let result = VariantArray::try_new(&result).unwrap();
849        assert_eq!(result.len(), 4);
850
851        // Expect the values are the same as the original values
852        assert_eq!(result.value(0), Variant::from(&[1u8, 2u8, 3u8][..]));
853        assert!(!result.is_valid(1));
854        assert_eq!(result.value(2), Variant::from("n/a"));
855        assert_eq!(result.value(3), Variant::from(&[4u8, 5u8, 6u8][..]));
856    }
857
858    // Timestamp partially-shredded tests grouped with the other partially-shredded cases.
859    macro_rules! assert_variant_get_as_variant_array_with_default_option {
860        ($variant_array: expr, $array_expected: expr) => {{
861            let options = GetOptions::new();
862            let array = $variant_array;
863            let result = variant_get(&array, options).unwrap();
864            let result = VariantArray::try_new(&result).unwrap();
865
866            assert_eq!(result.len(), $array_expected.len());
867
868            for (idx, item) in $array_expected.into_iter().enumerate() {
869                match item {
870                    Some(item) => assert_eq!(result.value(idx), item),
871                    None => assert!(result.is_null(idx)),
872                }
873            }
874        }};
875    }
876
877    partially_shredded_variant_array_gen!(
878        partially_shredded_timestamp_micro_ntz_variant_array,
879        || {
880            arrow::array::TimestampMicrosecondArray::from(vec![
881                Some(-456000),
882                None,
883                None,
884                Some(1758602096000000),
885            ])
886        }
887    );
888
889    #[test]
890    fn get_variant_partial_shredded_timestamp_micro_ntz_as_variant() {
891        let array = partially_shredded_timestamp_micro_ntz_variant_array();
892        assert_variant_get_as_variant_array_with_default_option!(
893            array,
894            vec![
895                Some(Variant::from(
896                    DateTime::from_timestamp_micros(-456000i64)
897                        .unwrap()
898                        .naive_utc(),
899                )),
900                None,
901                Some(Variant::from("n/a")),
902                Some(Variant::from(
903                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
904                        .unwrap()
905                        .naive_utc(),
906                )),
907            ]
908        )
909    }
910
911    partially_shredded_variant_array_gen!(partially_shredded_timestamp_micro_variant_array, || {
912        arrow::array::TimestampMicrosecondArray::from(vec![
913            Some(-456000),
914            None,
915            None,
916            Some(1758602096000000),
917        ])
918        .with_timezone("+00:00")
919    });
920
921    #[test]
922    fn get_variant_partial_shredded_timestamp_micro_as_variant() {
923        let array = partially_shredded_timestamp_micro_variant_array();
924        assert_variant_get_as_variant_array_with_default_option!(
925            array,
926            vec![
927                Some(Variant::from(
928                    DateTime::from_timestamp_micros(-456000i64)
929                        .unwrap()
930                        .to_utc(),
931                )),
932                None,
933                Some(Variant::from("n/a")),
934                Some(Variant::from(
935                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
936                        .unwrap()
937                        .to_utc(),
938                )),
939            ]
940        )
941    }
942
943    partially_shredded_variant_array_gen!(
944        partially_shredded_timestamp_nano_ntz_variant_array,
945        || {
946            arrow::array::TimestampNanosecondArray::from(vec![
947                Some(-4999999561),
948                None,
949                None,
950                Some(1758602096000000000),
951            ])
952        }
953    );
954
955    #[test]
956    fn get_variant_partial_shredded_timestamp_nano_ntz_as_variant() {
957        let array = partially_shredded_timestamp_nano_ntz_variant_array();
958        assert_variant_get_as_variant_array_with_default_option!(
959            array,
960            vec![
961                Some(Variant::from(
962                    DateTime::from_timestamp(-5, 439).unwrap().naive_utc()
963                )),
964                None,
965                Some(Variant::from("n/a")),
966                Some(Variant::from(
967                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
968                        .unwrap()
969                        .naive_utc()
970                )),
971            ]
972        )
973    }
974
975    partially_shredded_variant_array_gen!(partially_shredded_timestamp_nano_variant_array, || {
976        arrow::array::TimestampNanosecondArray::from(vec![
977            Some(-4999999561),
978            None,
979            None,
980            Some(1758602096000000000),
981        ])
982        .with_timezone("+00:00")
983    });
984
985    #[test]
986    fn get_variant_partial_shredded_timestamp_nano_as_variant() {
987        let array = partially_shredded_timestamp_nano_variant_array();
988        assert_variant_get_as_variant_array_with_default_option!(
989            array,
990            vec![
991                Some(Variant::from(
992                    DateTime::from_timestamp(-5, 439).unwrap().to_utc()
993                )),
994                None,
995                Some(Variant::from("n/a")),
996                Some(Variant::from(
997                    DateTime::parse_from_rfc3339("2025-09-23T12:34:56+08:00")
998                        .unwrap()
999                        .to_utc()
1000                )),
1001            ]
1002        )
1003    }
1004
1005    /// Shredding: extract a value as an Int32Array
1006    #[test]
1007    fn get_variant_shredded_int32_as_int32_safe_cast() {
1008        // Extract the typed value as Int32Array
1009        let array = partially_shredded_int32_variant_array();
1010        // specify we want the typed value as Int32
1011        let field = Field::new("typed_value", DataType::Int32, true);
1012        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
1013        let result = variant_get(&array, options).unwrap();
1014        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
1015            Some(34),
1016            None,
1017            None, // "n/a" is not an Int32 so converted to null
1018            Some(100),
1019        ]));
1020        assert_eq!(&result, &expected)
1021    }
1022
1023    /// Shredding: extract a value as an Int32Array, unsafe cast (should error on "n/a")
1024    #[test]
1025    fn get_variant_shredded_int32_as_int32_unsafe_cast() {
1026        // Extract the typed value as Int32Array
1027        let array = partially_shredded_int32_variant_array();
1028        let field = Field::new("typed_value", DataType::Int32, true);
1029        let cast_options = CastOptions {
1030            safe: false, // unsafe cast
1031            ..Default::default()
1032        };
1033        let options = GetOptions::new()
1034            .with_as_type(Some(FieldRef::from(field)))
1035            .with_cast_options(cast_options);
1036
1037        let err = variant_get(&array, options).unwrap_err();
1038        // TODO make this error message nicer (not Debug format)
1039        assert_eq!(
1040            err.to_string(),
1041            "Cast error: Failed to extract primitive of type Int32 from variant ShortString(ShortString(\"n/a\")) at path VariantPath([])"
1042        );
1043    }
1044
1045    /// Perfect Shredding: extract the typed value as a VariantArray
1046    macro_rules! numeric_perfectly_shredded_test {
1047        ($primitive_type:ty, $data_fn:ident) => {
1048            let array = $data_fn();
1049            let options = GetOptions::new();
1050            let result = variant_get(&array, options).unwrap();
1051
1052            // expect the result is a VariantArray
1053            let result = VariantArray::try_new(&result).unwrap();
1054            assert_eq!(result.len(), 3);
1055
1056            // Expect the values are the same as the original values
1057            assert_eq!(
1058                result.value(0),
1059                Variant::from(<$primitive_type>::try_from(1u8).unwrap())
1060            );
1061            assert_eq!(
1062                result.value(1),
1063                Variant::from(<$primitive_type>::try_from(2u8).unwrap())
1064            );
1065            assert_eq!(
1066                result.value(2),
1067                Variant::from(<$primitive_type>::try_from(3u8).unwrap())
1068            );
1069        };
1070    }
1071
1072    #[test]
1073    fn get_variant_perfectly_shredded_int8_as_variant() {
1074        numeric_perfectly_shredded_test!(i8, perfectly_shredded_int8_variant_array);
1075    }
1076
1077    #[test]
1078    fn get_variant_perfectly_shredded_int16_as_variant() {
1079        numeric_perfectly_shredded_test!(i16, perfectly_shredded_int16_variant_array);
1080    }
1081
1082    #[test]
1083    fn get_variant_perfectly_shredded_int32_as_variant() {
1084        numeric_perfectly_shredded_test!(i32, perfectly_shredded_int32_variant_array);
1085    }
1086
1087    #[test]
1088    fn get_variant_perfectly_shredded_int64_as_variant() {
1089        numeric_perfectly_shredded_test!(i64, perfectly_shredded_int64_variant_array);
1090    }
1091
1092    #[test]
1093    fn get_variant_perfectly_shredded_float32_as_variant() {
1094        numeric_perfectly_shredded_test!(f32, perfectly_shredded_float32_variant_array);
1095    }
1096
1097    #[test]
1098    fn get_variant_perfectly_shredded_float64_as_variant() {
1099        numeric_perfectly_shredded_test!(f64, perfectly_shredded_float64_variant_array);
1100    }
1101
1102    /// AllNull: extract a value as a VariantArray
1103    #[test]
1104    fn get_variant_all_null_as_variant() {
1105        let array = all_null_variant_array();
1106        let options = GetOptions::new();
1107        let result = variant_get(&array, options).unwrap();
1108
1109        // expect the result is a VariantArray
1110        let result = VariantArray::try_new(&result).unwrap();
1111        assert_eq!(result.len(), 3);
1112
1113        // All values should be null
1114        assert!(!result.is_valid(0));
1115        assert!(!result.is_valid(1));
1116        assert!(!result.is_valid(2));
1117    }
1118
1119    /// AllNull: extract a value as an Int32Array
1120    #[test]
1121    fn get_variant_all_null_as_int32() {
1122        let array = all_null_variant_array();
1123        // specify we want the typed value as Int32
1124        let field = Field::new("typed_value", DataType::Int32, true);
1125        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
1126        let result = variant_get(&array, options).unwrap();
1127
1128        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
1129            Option::<i32>::None,
1130            Option::<i32>::None,
1131            Option::<i32>::None,
1132        ]));
1133        assert_eq!(&result, &expected)
1134    }
1135
1136    macro_rules! perfectly_shredded_to_arrow_primitive_test {
1137        ($name:ident, $primitive_type:expr, $perfectly_shredded_array_gen_fun:ident, $expected_array:expr) => {
1138            #[test]
1139            fn $name() {
1140                let array = $perfectly_shredded_array_gen_fun();
1141                let field = Field::new("typed_value", $primitive_type, true);
1142                let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
1143                let result = variant_get(&array, options).unwrap();
1144                let expected_array: ArrayRef = Arc::new($expected_array);
1145                assert_eq!(&result, &expected_array);
1146            }
1147        };
1148    }
1149
1150    perfectly_shredded_to_arrow_primitive_test!(
1151        get_variant_perfectly_shredded_int18_as_int8,
1152        Int8,
1153        perfectly_shredded_int8_variant_array,
1154        Int8Array::from(vec![Some(1), Some(2), Some(3)])
1155    );
1156
1157    perfectly_shredded_to_arrow_primitive_test!(
1158        get_variant_perfectly_shredded_int16_as_int16,
1159        Int16,
1160        perfectly_shredded_int16_variant_array,
1161        Int16Array::from(vec![Some(1), Some(2), Some(3)])
1162    );
1163
1164    perfectly_shredded_to_arrow_primitive_test!(
1165        get_variant_perfectly_shredded_int32_as_int32,
1166        Int32,
1167        perfectly_shredded_int32_variant_array,
1168        Int32Array::from(vec![Some(1), Some(2), Some(3)])
1169    );
1170
1171    perfectly_shredded_to_arrow_primitive_test!(
1172        get_variant_perfectly_shredded_int64_as_int64,
1173        Int64,
1174        perfectly_shredded_int64_variant_array,
1175        Int64Array::from(vec![Some(1), Some(2), Some(3)])
1176    );
1177
1178    perfectly_shredded_to_arrow_primitive_test!(
1179        get_variant_perfectly_shredded_float32_as_float32,
1180        Float32,
1181        perfectly_shredded_float32_variant_array,
1182        Float32Array::from(vec![Some(1.0), Some(2.0), Some(3.0)])
1183    );
1184
1185    perfectly_shredded_to_arrow_primitive_test!(
1186        get_variant_perfectly_shredded_float64_as_float64,
1187        Float64,
1188        perfectly_shredded_float64_variant_array,
1189        Float64Array::from(vec![Some(1.0), Some(2.0), Some(3.0)])
1190    );
1191
1192    perfectly_shredded_to_arrow_primitive_test!(
1193        get_variant_perfectly_shredded_boolean_as_boolean,
1194        Boolean,
1195        perfectly_shredded_bool_variant_array,
1196        BooleanArray::from(vec![Some(true), Some(false), Some(true)])
1197    );
1198
1199    perfectly_shredded_to_arrow_primitive_test!(
1200        get_variant_perfectly_shredded_utf8_as_utf8,
1201        DataType::Utf8,
1202        perfectly_shredded_utf8_variant_array,
1203        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1204    );
1205
1206    perfectly_shredded_to_arrow_primitive_test!(
1207        get_variant_perfectly_shredded_large_utf8_as_utf8,
1208        DataType::Utf8,
1209        perfectly_shredded_large_utf8_variant_array,
1210        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1211    );
1212
1213    perfectly_shredded_to_arrow_primitive_test!(
1214        get_variant_perfectly_shredded_utf8_view_as_utf8,
1215        DataType::Utf8,
1216        perfectly_shredded_utf8_view_variant_array,
1217        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1218    );
1219
1220    macro_rules! perfectly_shredded_variant_array_fn {
1221        ($func:ident, $typed_value_gen:expr) => {
1222            fn $func() -> ArrayRef {
1223                // Prefer producing fixtures with shred_variant from unshredded input.
1224                // Fall back for remaining non-shreddable test-only Arrow types (currently Null).
1225                let typed_value: ArrayRef = Arc::new($typed_value_gen());
1226                if let Some(shredded) = cast_to_variant(typed_value.as_ref())
1227                    .ok()
1228                    .and_then(|unshredded| shred_variant(&unshredded, typed_value.data_type()).ok())
1229                {
1230                    return shredded.into();
1231                }
1232
1233                let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(
1234                    EMPTY_VARIANT_METADATA_BYTES,
1235                    typed_value.len(),
1236                ));
1237                VariantArray::perfectly_shredded(Arc::new(metadata), typed_value, None).into()
1238            }
1239        };
1240    }
1241
1242    perfectly_shredded_variant_array_fn!(perfectly_shredded_utf8_variant_array, || {
1243        StringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1244    });
1245
1246    perfectly_shredded_variant_array_fn!(perfectly_shredded_large_utf8_variant_array, || {
1247        LargeStringArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1248    });
1249
1250    perfectly_shredded_variant_array_fn!(perfectly_shredded_utf8_view_variant_array, || {
1251        StringViewArray::from(vec![Some("foo"), Some("bar"), Some("baz")])
1252    });
1253
1254    perfectly_shredded_variant_array_fn!(perfectly_shredded_bool_variant_array, || {
1255        BooleanArray::from(vec![Some(true), Some(false), Some(true)])
1256    });
1257
1258    /// Return a VariantArray that represents a perfectly "shredded" variant
1259    /// for the given typed value.
1260    ///
1261    /// The schema of the corresponding `StructArray` would look like this:
1262    ///
1263    /// ```text
1264    /// StructArray {
1265    ///   metadata: BinaryViewArray,
1266    ///   typed_value: Int32Array,
1267    /// }
1268    /// ```
1269    macro_rules! numeric_perfectly_shredded_variant_array_fn {
1270        ($func:ident, $array_type:ident, $primitive_type:ty) => {
1271            perfectly_shredded_variant_array_fn!($func, || {
1272                $array_type::from(vec![
1273                    Some(<$primitive_type>::try_from(1u8).unwrap()),
1274                    Some(<$primitive_type>::try_from(2u8).unwrap()),
1275                    Some(<$primitive_type>::try_from(3u8).unwrap()),
1276                ])
1277            });
1278        };
1279    }
1280
1281    numeric_perfectly_shredded_variant_array_fn!(
1282        perfectly_shredded_int8_variant_array,
1283        Int8Array,
1284        i8
1285    );
1286    numeric_perfectly_shredded_variant_array_fn!(
1287        perfectly_shredded_int16_variant_array,
1288        Int16Array,
1289        i16
1290    );
1291    numeric_perfectly_shredded_variant_array_fn!(
1292        perfectly_shredded_int32_variant_array,
1293        Int32Array,
1294        i32
1295    );
1296    numeric_perfectly_shredded_variant_array_fn!(
1297        perfectly_shredded_int64_variant_array,
1298        Int64Array,
1299        i64
1300    );
1301    numeric_perfectly_shredded_variant_array_fn!(
1302        perfectly_shredded_float32_variant_array,
1303        Float32Array,
1304        f32
1305    );
1306    numeric_perfectly_shredded_variant_array_fn!(
1307        perfectly_shredded_float64_variant_array,
1308        Float64Array,
1309        f64
1310    );
1311
1312    perfectly_shredded_variant_array_fn!(
1313        perfectly_shredded_timestamp_micro_ntz_variant_array,
1314        || {
1315            arrow::array::TimestampMicrosecondArray::from(vec![
1316                Some(-456000),
1317                Some(1758602096000001),
1318                Some(1758602096000002),
1319            ])
1320        }
1321    );
1322
1323    perfectly_shredded_to_arrow_primitive_test!(
1324        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_micro_ntz,
1325        DataType::Timestamp(TimeUnit::Microsecond, None),
1326        perfectly_shredded_timestamp_micro_ntz_variant_array,
1327        arrow::array::TimestampMicrosecondArray::from(vec![
1328            Some(-456000),
1329            Some(1758602096000001),
1330            Some(1758602096000002),
1331        ])
1332    );
1333
1334    // test converting micro to nano
1335    perfectly_shredded_to_arrow_primitive_test!(
1336        get_variant_perfectly_shredded_timestamp_micro_ntz_as_nano_ntz,
1337        DataType::Timestamp(TimeUnit::Nanosecond, None),
1338        perfectly_shredded_timestamp_micro_ntz_variant_array,
1339        arrow::array::TimestampNanosecondArray::from(vec![
1340            Some(-456000000),
1341            Some(1758602096000001000),
1342            Some(1758602096000002000)
1343        ])
1344    );
1345
1346    perfectly_shredded_variant_array_fn!(perfectly_shredded_timestamp_micro_variant_array, || {
1347        arrow::array::TimestampMicrosecondArray::from(vec![
1348            Some(-456000),
1349            Some(1758602096000001),
1350            Some(1758602096000002),
1351        ])
1352        .with_timezone("+00:00")
1353    });
1354
1355    perfectly_shredded_to_arrow_primitive_test!(
1356        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_micro,
1357        DataType::Timestamp(TimeUnit::Microsecond, Some(Arc::from("+00:00"))),
1358        perfectly_shredded_timestamp_micro_variant_array,
1359        arrow::array::TimestampMicrosecondArray::from(vec![
1360            Some(-456000),
1361            Some(1758602096000001),
1362            Some(1758602096000002),
1363        ])
1364        .with_timezone("+00:00")
1365    );
1366
1367    // test converting micro to nano
1368    perfectly_shredded_to_arrow_primitive_test!(
1369        get_variant_perfectly_shredded_timestamp_micro_as_nano,
1370        DataType::Timestamp(TimeUnit::Nanosecond, Some(Arc::from("+00:00"))),
1371        perfectly_shredded_timestamp_micro_variant_array,
1372        arrow::array::TimestampNanosecondArray::from(vec![
1373            Some(-456000000),
1374            Some(1758602096000001000),
1375            Some(1758602096000002000)
1376        ])
1377        .with_timezone("+00:00")
1378    );
1379
1380    perfectly_shredded_variant_array_fn!(
1381        perfectly_shredded_timestamp_nano_ntz_variant_array,
1382        || {
1383            arrow::array::TimestampNanosecondArray::from(vec![
1384                Some(-4999999561),
1385                Some(1758602096000000001),
1386                Some(1758602096000000002),
1387            ])
1388        }
1389    );
1390
1391    perfectly_shredded_variant_array_fn!(
1392        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1393        || {
1394            arrow::array::TimestampMicrosecondArray::from(vec![
1395                Some(1234),       // can't be cast to second & millisecond
1396                Some(1234000),    // can be cast to millisecond, but not second
1397                Some(1234000000), // can be cast to second & millisecond
1398            ])
1399            .with_timezone("+00:00")
1400        }
1401    );
1402
1403    // The following two tests wants to cover the micro with timezone -> milli/second cases
1404    // there are three test items, which contains some items can be cast safely, and some can't
1405    perfectly_shredded_to_arrow_primitive_test!(
1406        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_second,
1407        DataType::Timestamp(TimeUnit::Second, Some(Arc::from("+00:00"))),
1408        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1409        arrow::array::TimestampSecondArray::from(vec![
1410            None,
1411            None, // Return None if can't be cast to second safely
1412            Some(1234)
1413        ])
1414        .with_timezone("+00:00")
1415    );
1416
1417    perfectly_shredded_to_arrow_primitive_test!(
1418        get_variant_perfectly_shredded_timestamp_micro_as_timestamp_milli,
1419        DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from("+00:00"))),
1420        perfectly_shredded_timestamp_micro_variant_array_for_second_and_milli_second,
1421        arrow::array::TimestampMillisecondArray::from(vec![
1422            None, // Return None if can't be cast to millisecond safely
1423            Some(1234),
1424            Some(1234000)
1425        ])
1426        .with_timezone("+00:00")
1427    );
1428
1429    perfectly_shredded_variant_array_fn!(
1430        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1431        || {
1432            arrow::array::TimestampMicrosecondArray::from(vec![
1433                Some(1234),       // can't be cast to second & millisecond
1434                Some(1234000),    // can be cast to millisecond, but not second
1435                Some(1234000000), // can be cast to second & millisecond
1436            ])
1437        }
1438    );
1439
1440    // The following two tests wants to cover the micro_ntz -> milli/second cases
1441    // there are three test items, which contains some items can be cast safely, and some can't
1442    perfectly_shredded_to_arrow_primitive_test!(
1443        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_second,
1444        DataType::Timestamp(TimeUnit::Second, None),
1445        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1446        arrow::array::TimestampSecondArray::from(vec![
1447            None,
1448            None, // Return None if can't be cast to second safely
1449            Some(1234)
1450        ])
1451    );
1452
1453    perfectly_shredded_to_arrow_primitive_test!(
1454        get_variant_perfectly_shredded_timestamp_micro_ntz_as_timestamp_milli,
1455        DataType::Timestamp(TimeUnit::Millisecond, None),
1456        perfectly_shredded_timestamp_micro_ntz_variant_array_for_second_and_milli_second,
1457        arrow::array::TimestampMillisecondArray::from(vec![
1458            None, // Return None if can't be cast to millisecond safely
1459            Some(1234),
1460            Some(1234000)
1461        ])
1462    );
1463
1464    perfectly_shredded_variant_array_fn!(
1465        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1466        || {
1467            arrow::array::TimestampNanosecondArray::from(vec![
1468                Some(1234000),       // can't be cast to second & millisecond
1469                Some(1234000000),    // can be cast to millisecond, but not second
1470                Some(1234000000000), // can be cast to second & millisecond
1471            ])
1472            .with_timezone("+00:00")
1473        }
1474    );
1475
1476    // The following two tests wants to cover the nano with timezone -> milli/second cases
1477    // there are three test items, which contains some items can be cast safely, and some can't
1478    perfectly_shredded_to_arrow_primitive_test!(
1479        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_second,
1480        DataType::Timestamp(TimeUnit::Second, Some(Arc::from("+00:00"))),
1481        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1482        arrow::array::TimestampSecondArray::from(vec![
1483            None,
1484            None, // Return None if can't be cast to second safely
1485            Some(1234)
1486        ])
1487        .with_timezone("+00:00")
1488    );
1489
1490    perfectly_shredded_to_arrow_primitive_test!(
1491        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_milli,
1492        DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from("+00:00"))),
1493        perfectly_shredded_timestamp_nano_variant_array_for_second_and_milli_second,
1494        arrow::array::TimestampMillisecondArray::from(vec![
1495            None, // Return None if can't be cast to millisecond safely
1496            Some(1234),
1497            Some(1234000)
1498        ])
1499        .with_timezone("+00:00")
1500    );
1501
1502    perfectly_shredded_variant_array_fn!(
1503        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1504        || {
1505            arrow::array::TimestampNanosecondArray::from(vec![
1506                Some(1234000),       // can't be cast to second & millisecond
1507                Some(1234000000),    // can be cast to millisecond, but not second
1508                Some(1234000000000), // can be cast to second & millisecond
1509            ])
1510        }
1511    );
1512
1513    // The following two tests wants to cover the nano_ntz -> milli/second cases
1514    // there are three test items, which contains some items can be cast safely, and some can't
1515    perfectly_shredded_to_arrow_primitive_test!(
1516        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_second,
1517        DataType::Timestamp(TimeUnit::Second, None),
1518        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1519        arrow::array::TimestampSecondArray::from(vec![
1520            None,
1521            None, // Return None if can't be cast to second safely
1522            Some(1234)
1523        ])
1524    );
1525
1526    perfectly_shredded_to_arrow_primitive_test!(
1527        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_milli,
1528        DataType::Timestamp(TimeUnit::Millisecond, None),
1529        perfectly_shredded_timestamp_nano_ntz_variant_array_for_second_and_milli_second,
1530        arrow::array::TimestampMillisecondArray::from(vec![
1531            None, // Return None if can't be cast to millisecond safely
1532            Some(1234),
1533            Some(1234000)
1534        ])
1535    );
1536
1537    perfectly_shredded_to_arrow_primitive_test!(
1538        get_variant_perfectly_shredded_timestamp_nano_ntz_as_timestamp_nano_ntz,
1539        DataType::Timestamp(TimeUnit::Nanosecond, None),
1540        perfectly_shredded_timestamp_nano_ntz_variant_array,
1541        arrow::array::TimestampNanosecondArray::from(vec![
1542            Some(-4999999561),
1543            Some(1758602096000000001),
1544            Some(1758602096000000002),
1545        ])
1546    );
1547
1548    perfectly_shredded_variant_array_fn!(perfectly_shredded_timestamp_nano_variant_array, || {
1549        arrow::array::TimestampNanosecondArray::from(vec![
1550            Some(-4999999561),
1551            Some(1758602096000000001),
1552            Some(1758602096000000002),
1553        ])
1554        .with_timezone("+00:00")
1555    });
1556
1557    perfectly_shredded_to_arrow_primitive_test!(
1558        get_variant_perfectly_shredded_timestamp_nano_as_timestamp_nano,
1559        DataType::Timestamp(TimeUnit::Nanosecond, Some(Arc::from("+00:00"))),
1560        perfectly_shredded_timestamp_nano_variant_array,
1561        arrow::array::TimestampNanosecondArray::from(vec![
1562            Some(-4999999561),
1563            Some(1758602096000000001),
1564            Some(1758602096000000002),
1565        ])
1566        .with_timezone("+00:00")
1567    );
1568
1569    perfectly_shredded_variant_array_fn!(perfectly_shredded_date_variant_array, || {
1570        Date32Array::from(vec![Some(-12345), Some(17586), Some(20000)])
1571    });
1572
1573    perfectly_shredded_to_arrow_primitive_test!(
1574        get_variant_perfectly_shredded_date_as_date,
1575        DataType::Date32,
1576        perfectly_shredded_date_variant_array,
1577        Date32Array::from(vec![Some(-12345), Some(17586), Some(20000)])
1578    );
1579
1580    perfectly_shredded_to_arrow_primitive_test!(
1581        get_variant_perfectly_shredded_date_as_date64,
1582        DataType::Date64,
1583        perfectly_shredded_date_variant_array,
1584        Date64Array::from(vec![
1585            Some(-1066608000000),
1586            Some(1519430400000),
1587            Some(1728000000000)
1588        ])
1589    );
1590
1591    perfectly_shredded_variant_array_fn!(perfectly_shredded_time_variant_array, || {
1592        Time64MicrosecondArray::from(vec![Some(12345000), Some(87654000), Some(135792000)])
1593    });
1594
1595    perfectly_shredded_to_arrow_primitive_test!(
1596        get_variant_perfectly_shredded_time_as_time,
1597        DataType::Time64(TimeUnit::Microsecond),
1598        perfectly_shredded_time_variant_array,
1599        Time64MicrosecondArray::from(vec![Some(12345000), Some(87654000), Some(135792000)])
1600    );
1601
1602    perfectly_shredded_to_arrow_primitive_test!(
1603        get_variant_perfectly_shredded_time_as_time64_nano,
1604        DataType::Time64(TimeUnit::Nanosecond),
1605        perfectly_shredded_time_variant_array,
1606        Time64NanosecondArray::from(vec![
1607            Some(12345000000),
1608            Some(87654000000),
1609            Some(135792000000)
1610        ])
1611    );
1612
1613    perfectly_shredded_variant_array_fn!(perfectly_shredded_time_variant_array_for_time32, || {
1614        Time64MicrosecondArray::from(vec![
1615            Some(1234),        // This can't be cast to Time32 losslessly
1616            Some(7654000),     // This can be cast to Time32(Millisecond), but not Time32(Second)
1617            Some(35792000000), // This can be cast to Time32(Second) & Time32(Millisecond)
1618        ])
1619    });
1620
1621    perfectly_shredded_to_arrow_primitive_test!(
1622        get_variant_perfectly_shredded_time_as_time32_second,
1623        DataType::Time32(TimeUnit::Second),
1624        perfectly_shredded_time_variant_array_for_time32,
1625        Time32SecondArray::from(vec![
1626            None,
1627            None, // Return None if can't be cast to Time32(Second) safely
1628            Some(35792)
1629        ])
1630    );
1631
1632    perfectly_shredded_to_arrow_primitive_test!(
1633        get_variant_perfectly_shredded_time_as_time32_milli,
1634        DataType::Time32(TimeUnit::Millisecond),
1635        perfectly_shredded_time_variant_array_for_time32,
1636        Time32MillisecondArray::from(vec![
1637            None, // Return None if can't be cast to Time32(Second) safely
1638            Some(7654),
1639            Some(35792000)
1640        ])
1641    );
1642
1643    perfectly_shredded_variant_array_fn!(perfectly_shredded_null_variant_array, || {
1644        let mut builder = NullBuilder::new();
1645        builder.append_nulls(3);
1646        builder.finish()
1647    });
1648
1649    perfectly_shredded_to_arrow_primitive_test!(
1650        get_variant_perfectly_shredded_null_as_null,
1651        DataType::Null,
1652        perfectly_shredded_null_variant_array,
1653        arrow::array::NullArray::new(3)
1654    );
1655
1656    perfectly_shredded_variant_array_fn!(perfectly_shredded_null_variant_array_with_int, || {
1657        Int32Array::from(vec![Some(32), Some(64), Some(48)])
1658    });
1659
1660    // We append null values if type miss match happens in safe mode
1661    perfectly_shredded_to_arrow_primitive_test!(
1662        get_variant_perfectly_shredded_null_with_type_mismatch_in_safe_mode,
1663        DataType::Null,
1664        perfectly_shredded_null_variant_array_with_int,
1665        arrow::array::NullArray::new(3)
1666    );
1667
1668    // We'll return an error if type miss match happens in strict mode
1669    #[test]
1670    fn get_variant_perfectly_shredded_null_as_null_with_type_mismatch_in_strict_mode() {
1671        let array = perfectly_shredded_null_variant_array_with_int();
1672        let field = Field::new("typed_value", DataType::Null, true);
1673        let options = GetOptions::new()
1674            .with_as_type(Some(FieldRef::from(field)))
1675            .with_cast_options(CastOptions {
1676                safe: false,
1677                format_options: FormatOptions::default(),
1678            });
1679
1680        let result = variant_get(&array, options);
1681
1682        assert!(result.is_err());
1683        let error_msg = format!("{}", result.unwrap_err());
1684        assert!(
1685            error_msg
1686                .contains("Cast error: Failed to extract primitive of type Null from variant Int32(32) at path VariantPath([])"),
1687            "Expected=[Cast error: Failed to extract primitive of type Null from variant Int32(32) at path VariantPath([])],\
1688                Got error message=[{error_msg}]"
1689        );
1690    }
1691
1692    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal4_variant_array, || {
1693        Decimal32Array::from(vec![Some(12345), Some(23400), Some(-12342)])
1694            .with_precision_and_scale(5, 2)
1695            .unwrap()
1696    });
1697
1698    perfectly_shredded_to_arrow_primitive_test!(
1699        get_variant_perfectly_shredded_decimal4_as_decimal4,
1700        DataType::Decimal32(5, 2),
1701        perfectly_shredded_decimal4_variant_array,
1702        Decimal32Array::from(vec![Some(12345), Some(23400), Some(-12342)])
1703            .with_precision_and_scale(5, 2)
1704            .unwrap()
1705    );
1706
1707    perfectly_shredded_variant_array_fn!(
1708        perfectly_shredded_decimal8_variant_array_cast2decimal32,
1709        || {
1710            Decimal64Array::from(vec![Some(123456), Some(145678), Some(-123456)])
1711                .with_precision_and_scale(6, 1)
1712                .unwrap()
1713        }
1714    );
1715
1716    // The input will be cast to Decimal32 when transformed to Variant
1717    // This tests will covert the logic DataType::Decimal64(the original array)
1718    // -> Variant::Decimal4(VariantArray) -> DataType::Decimal64(the result array)
1719    perfectly_shredded_to_arrow_primitive_test!(
1720        get_variant_perfectly_shredded_decimal8_through_decimal32_as_decimal8,
1721        DataType::Decimal64(6, 1),
1722        perfectly_shredded_decimal8_variant_array_cast2decimal32,
1723        Decimal64Array::from(vec![Some(123456), Some(145678), Some(-123456)])
1724            .with_precision_and_scale(6, 1)
1725            .unwrap()
1726    );
1727
1728    // This tests will covert the logic DataType::Decimal64(the original array)
1729    //  -> Variant::Decimal8(VariantArray) -> DataType::Decimal64(the result array)
1730    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal8_variant_array, || {
1731        Decimal64Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1732            .with_precision_and_scale(10, 1)
1733            .unwrap()
1734    });
1735
1736    perfectly_shredded_to_arrow_primitive_test!(
1737        get_variant_perfectly_shredded_decimal8_as_decimal8,
1738        DataType::Decimal64(10, 1),
1739        perfectly_shredded_decimal8_variant_array,
1740        Decimal64Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1741            .with_precision_and_scale(10, 1)
1742            .unwrap()
1743    );
1744
1745    // This tests will covert the logic DataType::Decimal128(the original array)
1746    //  -> Variant::Decimal4(VariantArray) -> DataType::Decimal128(the result array)
1747    perfectly_shredded_variant_array_fn!(
1748        perfectly_shredded_decimal16_within_decimal4_variant_array,
1749        || {
1750            Decimal128Array::from(vec![
1751                Some(i128::from(1234589)),
1752                Some(i128::from(2344444)),
1753                Some(i128::from(-1234789)),
1754            ])
1755            .with_precision_and_scale(7, 3)
1756            .unwrap()
1757        }
1758    );
1759
1760    // This tests will covert the logic DataType::Decimal128(the original array)
1761    // -> Variant::Decimal4(VariantArray) -> DataType::Decimal128(the result array)
1762    perfectly_shredded_to_arrow_primitive_test!(
1763        get_variant_perfectly_shredded_decimal16_within_decimal4_as_decimal16,
1764        DataType::Decimal128(7, 3),
1765        perfectly_shredded_decimal16_within_decimal4_variant_array,
1766        Decimal128Array::from(vec![
1767            Some(i128::from(1234589)),
1768            Some(i128::from(2344444)),
1769            Some(i128::from(-1234789)),
1770        ])
1771        .with_precision_and_scale(7, 3)
1772        .unwrap()
1773    );
1774
1775    perfectly_shredded_variant_array_fn!(
1776        perfectly_shredded_decimal16_within_decimal8_variant_array,
1777        || {
1778            Decimal128Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1779                .with_precision_and_scale(10, 1)
1780                .unwrap()
1781        }
1782    );
1783
1784    // This tests will covert the logic DataType::Decimal128(the original array)
1785    // -> Variant::Decimal8(VariantArray) -> DataType::Decimal128(the result array)
1786    perfectly_shredded_to_arrow_primitive_test!(
1787        get_variant_perfectly_shredded_decimal16_within8_as_decimal16,
1788        DataType::Decimal128(10, 1),
1789        perfectly_shredded_decimal16_within_decimal8_variant_array,
1790        Decimal128Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
1791            .with_precision_and_scale(10, 1)
1792            .unwrap()
1793    );
1794
1795    perfectly_shredded_variant_array_fn!(perfectly_shredded_decimal16_variant_array, || {
1796        Decimal128Array::from(vec![
1797            Some(i128::from_str("12345678901234567899").unwrap()),
1798            Some(i128::from_str("23445677483748324300").unwrap()),
1799            Some(i128::from_str("-12345678901234567899").unwrap()),
1800        ])
1801        .with_precision_and_scale(20, 3)
1802        .unwrap()
1803    });
1804
1805    // This tests will covert the logic DataType::Decimal128(the original array)
1806    // -> Variant::Decimal16(VariantArray) -> DataType::Decimal128(the result array)
1807    perfectly_shredded_to_arrow_primitive_test!(
1808        get_variant_perfectly_shredded_decimal16_as_decimal16,
1809        DataType::Decimal128(20, 3),
1810        perfectly_shredded_decimal16_variant_array,
1811        Decimal128Array::from(vec![
1812            Some(i128::from_str("12345678901234567899").unwrap()),
1813            Some(i128::from_str("23445677483748324300").unwrap()),
1814            Some(i128::from_str("-12345678901234567899").unwrap())
1815        ])
1816        .with_precision_and_scale(20, 3)
1817        .unwrap()
1818    );
1819
1820    perfectly_shredded_variant_array_fn!(perfectly_shredded_binary_variant_array, || {
1821        BinaryArray::from(vec![
1822            Some(b"Apache" as &[u8]),
1823            Some(b"Arrow-rs" as &[u8]),
1824            Some(b"Parquet-variant" as &[u8]),
1825        ])
1826    });
1827
1828    perfectly_shredded_to_arrow_primitive_test!(
1829        get_variant_perfectly_shredded_binary_as_binary,
1830        DataType::Binary,
1831        perfectly_shredded_binary_variant_array,
1832        BinaryArray::from(vec![
1833            Some(b"Apache" as &[u8]),
1834            Some(b"Arrow-rs" as &[u8]),
1835            Some(b"Parquet-variant" as &[u8]),
1836        ])
1837    );
1838
1839    perfectly_shredded_variant_array_fn!(perfectly_shredded_large_binary_variant_array, || {
1840        LargeBinaryArray::from(vec![
1841            Some(b"Apache" as &[u8]),
1842            Some(b"Arrow-rs" as &[u8]),
1843            Some(b"Parquet-variant" as &[u8]),
1844        ])
1845    });
1846
1847    perfectly_shredded_to_arrow_primitive_test!(
1848        get_variant_perfectly_shredded_large_binary_as_large_binary,
1849        DataType::LargeBinary,
1850        perfectly_shredded_large_binary_variant_array,
1851        LargeBinaryArray::from(vec![
1852            Some(b"Apache" as &[u8]),
1853            Some(b"Arrow-rs" as &[u8]),
1854            Some(b"Parquet-variant" as &[u8]),
1855        ])
1856    );
1857
1858    perfectly_shredded_variant_array_fn!(perfectly_shredded_binary_view_variant_array, || {
1859        BinaryViewArray::from(vec![
1860            Some(b"Apache" as &[u8]),
1861            Some(b"Arrow-rs" as &[u8]),
1862            Some(b"Parquet-variant" as &[u8]),
1863        ])
1864    });
1865
1866    perfectly_shredded_to_arrow_primitive_test!(
1867        get_variant_perfectly_shredded_binary_view_as_binary_view,
1868        DataType::BinaryView,
1869        perfectly_shredded_binary_view_variant_array,
1870        BinaryViewArray::from(vec![
1871            Some(b"Apache" as &[u8]),
1872            Some(b"Arrow-rs" as &[u8]),
1873            Some(b"Parquet-variant" as &[u8]),
1874        ])
1875    );
1876
1877    /// Return a VariantArray that represents an "all null" variant
1878    /// for the following example (3 null values):
1879    ///
1880    /// ```text
1881    /// null
1882    /// null
1883    /// null
1884    /// ```
1885    ///
1886    /// The schema of the corresponding `StructArray` would look like this:
1887    ///
1888    /// ```text
1889    /// StructArray {
1890    ///   metadata: BinaryViewArray,
1891    /// }
1892    /// ```
1893    fn all_null_variant_array() -> ArrayRef {
1894        let nulls = NullBuffer::from(vec![
1895            false, // row 0 is null
1896            false, // row 1 is null
1897            false, // row 2 is null
1898        ]);
1899
1900        // metadata is the same for all rows (though they're all null)
1901        let metadata =
1902            BinaryViewArray::from_iter_values(std::iter::repeat_n(EMPTY_VARIANT_METADATA_BYTES, 3));
1903
1904        ArrayRef::from(VariantArray::from_parts(
1905            Arc::new(metadata),
1906            all_null_value_column(3),
1907            None,
1908            Some(nulls),
1909        ))
1910    }
1911
1912    /// This test manually constructs a shredded variant array representing objects
1913    /// like {"x": 1, "y": "foo"} and {"x": 42} and tests extracting the "x" field
1914    /// as VariantArray using variant_get.
1915    #[test]
1916    fn test_shredded_object_field_access() {
1917        let array = shredded_object_with_x_field_variant_array();
1918
1919        // Test: Extract the "x" field as VariantArray first
1920        let options = GetOptions::new_with_path(VariantPath::try_from("x").unwrap());
1921        let result = variant_get(&array, options).unwrap();
1922
1923        let result_variant = VariantArray::try_new(&result).unwrap();
1924        assert_eq!(result_variant.len(), 2);
1925
1926        // Row 0: expect x=1
1927        assert_eq!(result_variant.value(0), Variant::Int32(1));
1928        // Row 1: expect x=42
1929        assert_eq!(result_variant.value(1), Variant::Int32(42));
1930    }
1931
1932    #[test]
1933    fn test_malformed_shredded_object_field_reports_field_and_type() {
1934        let metadata =
1935            BinaryViewArray::from_iter_values(std::iter::repeat_n(EMPTY_VARIANT_METADATA_BYTES, 2));
1936        let typed_value = StructArray::try_new(
1937            Fields::from(vec![Field::new("x", DataType::Int32, true)]),
1938            vec![Arc::new(Int32Array::from(vec![Some(1), Some(42)]))],
1939            None,
1940        )
1941        .unwrap();
1942        let array = ArrayRef::from(VariantArray::from_parts(
1943            Arc::new(metadata),
1944            all_null_value_column(2),
1945            Some(Arc::new(typed_value)),
1946            None,
1947        ));
1948
1949        let options = GetOptions::new_with_path(VariantPath::try_from("x").unwrap());
1950        let err = variant_get(&array, options).unwrap_err();
1951
1952        assert_eq!(
1953            err.to_string(),
1954            "Invalid argument error: Shredded object field 'x' must be a Struct containing \
1955             'value' and/or 'typed_value', got Int32"
1956        );
1957    }
1958
1959    /// Test extracting shredded object field with type conversion
1960    #[test]
1961    fn test_shredded_object_field_as_int32() {
1962        let array = shredded_object_with_x_field_variant_array();
1963
1964        // Test: Extract the "x" field as Int32Array (type conversion)
1965        let field = Field::new("x", DataType::Int32, false);
1966        let options = GetOptions::new_with_path(VariantPath::try_from("x").unwrap())
1967            .with_as_type(Some(FieldRef::from(field)));
1968        let result = variant_get(&array, options).unwrap();
1969
1970        // Should get Int32Array
1971        let expected: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(42)]));
1972        assert_eq!(&result, &expected);
1973    }
1974
1975    type ShreddedListLikeArrayGen = fn() -> ArrayRef;
1976    type ShreddedListLikeCase = (&'static str, ShreddedListLikeArrayGen);
1977
1978    fn shredded_list_like_cases() -> [ShreddedListLikeCase; 4] {
1979        [
1980            ("list", shredded_list_variant_array),
1981            ("large_list", shredded_large_list_variant_array),
1982            ("list_view", shredded_list_view_variant_array),
1983            ("large_list_view", shredded_large_list_view_variant_array),
1984        ]
1985    }
1986
1987    #[test]
1988    fn test_shredded_list_like_index_access_from_value_field() {
1989        let options = GetOptions::new_with_path(VariantPath::from(1));
1990
1991        for (case, array_gen) in shredded_list_like_cases() {
1992            let array = array_gen();
1993            let result = variant_get(&array, options.clone()).unwrap();
1994            let result_variant = VariantArray::try_new(&result).unwrap();
1995
1996            assert_eq!(result_variant.value(0), Variant::from("drama"), "{case}");
1997            assert_eq!(result_variant.value(1).as_int64(), Some(123), "{case}");
1998        }
1999    }
2000
2001    #[test]
2002    fn test_shredded_list_like_index_out_of_bounds_unsafe_cast_returns_null() {
2003        let options =
2004            GetOptions::new_with_path(VariantPath::from(10)).with_cast_options(CastOptions {
2005                safe: false,
2006                ..Default::default()
2007            });
2008
2009        for (case, array_gen) in shredded_list_like_cases() {
2010            let result = variant_get(&array_gen(), options.clone()).unwrap();
2011            let result_variant = VariantArray::try_new(&result).unwrap();
2012            assert_eq!(result_variant.value(0), Variant::Null, "{case}");
2013            assert_eq!(result_variant.value(1), Variant::Null, "{case}");
2014        }
2015    }
2016
2017    /// Test extracting shredded list-like field with type conversion.
2018    #[test]
2019    fn test_shredded_list_like_as_string() {
2020        let field = Field::new("typed_value", DataType::Utf8, false);
2021        let options = GetOptions::new_with_path(VariantPath::from(0))
2022            .with_as_type(Some(FieldRef::from(field)));
2023        let expected: ArrayRef = Arc::new(StringArray::from(vec![Some("comedy"), Some("horror")]));
2024
2025        for (case, array_gen) in shredded_list_like_cases() {
2026            let result = variant_get(&array_gen(), options.clone()).unwrap();
2027            assert_eq!(&result, &expected, "{case}");
2028        }
2029    }
2030
2031    #[test]
2032    fn test_shredded_list_like_index_access_from_value_field_as_int64() {
2033        let field = Field::new("typed_value", DataType::Int64, true);
2034        let options = GetOptions::new_with_path(VariantPath::from(1))
2035            .with_as_type(Some(FieldRef::from(field)));
2036        let expected: ArrayRef = Arc::new(Int64Array::from(vec![None, Some(123)]));
2037
2038        for (case, array_gen) in shredded_list_like_cases() {
2039            let result = variant_get(&array_gen(), options.clone()).unwrap();
2040            // "drama" -> NULL, 123 -> 123.
2041            assert_eq!(&result, &expected, "{case}");
2042        }
2043    }
2044
2045    #[test]
2046    fn test_shredded_list_in_struct_index_access() {
2047        let array = shredded_struct_with_list_variant_array();
2048        let options = GetOptions::new_with_path(VariantPath::try_from("a[1]").unwrap());
2049        let result = variant_get(&array, options).unwrap();
2050        let result_variant = VariantArray::try_new(&result).unwrap();
2051
2052        assert_eq!(result_variant.value(0), Variant::from("drama"));
2053        assert_eq!(result_variant.value(1).as_int64(), Some(123));
2054    }
2055
2056    #[test]
2057    fn test_shredded_struct_in_list_field_access() {
2058        let array = shredded_list_of_struct_variant_array();
2059        let field = Field::new("x", DataType::Int32, true);
2060        let path = VariantPath::from(0).join("x");
2061        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2062        let result = variant_get(&array, options).unwrap();
2063
2064        let expected: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(3)]));
2065        assert_eq!(&result, &expected);
2066    }
2067
2068    #[test]
2069    fn test_shredded_list_of_lists_index_access() {
2070        let array = shredded_list_of_lists_variant_array();
2071        let path = VariantPath::from(0).join(1);
2072
2073        let result = variant_get(&array, GetOptions::new_with_path(path.clone())).unwrap();
2074        let result_variant = VariantArray::try_new(&result).unwrap();
2075        assert_eq!(result_variant.value(0), Variant::from("b"));
2076        assert_eq!(result_variant.value(1).as_int64(), Some(123));
2077
2078        let field = Field::new("typed_value", DataType::Int64, true);
2079        let casted = variant_get(
2080            &array,
2081            GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field))),
2082        )
2083        .unwrap();
2084        let expected: ArrayRef = Arc::new(Int64Array::from(vec![None, Some(123)]));
2085        assert_eq!(&casted, &expected);
2086    }
2087
2088    /// Helper to create a shredded list-like variant array used by list index tests.
2089    ///
2090    /// Rows:
2091    /// 1. `["comedy", "drama"]` (fully shred-able as `Utf8`)
2092    /// 2. `["horror", 123]` (partially shredded, with fallback for the numeric element)
2093    fn shredded_list_like_variant_array(list_schema: DataType) -> ArrayRef {
2094        let json_rows: ArrayRef = Arc::new(StringArray::from(vec![
2095            Some(r#"["comedy", "drama"]"#),
2096            Some(r#"["horror", 123]"#),
2097        ]));
2098        let input = json_to_variant(&json_rows).unwrap();
2099
2100        let shredded = shred_variant(&input, &list_schema).unwrap();
2101        ArrayRef::from(shredded)
2102    }
2103
2104    fn shredded_list_of_lists_variant_array() -> ArrayRef {
2105        let json_rows: ArrayRef = Arc::new(StringArray::from(vec![
2106            Some(r#"[["a", "b"], ["c", "d"]]"#),
2107            Some(r#"[["x", 123], ["y", "z"]]"#),
2108        ]));
2109        let input = json_to_variant(&json_rows).unwrap();
2110
2111        let inner_list = DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
2112        let outer_list = DataType::List(Arc::new(Field::new("item", inner_list, true)));
2113        let shredded = shred_variant(&input, &outer_list).unwrap();
2114        ArrayRef::from(shredded)
2115    }
2116
2117    fn shredded_list_variant_array() -> ArrayRef {
2118        shredded_list_like_variant_array(DataType::List(Arc::new(Field::new(
2119            "item",
2120            DataType::Utf8,
2121            true,
2122        ))))
2123    }
2124
2125    fn shredded_large_list_variant_array() -> ArrayRef {
2126        shredded_list_like_variant_array(DataType::LargeList(Arc::new(Field::new(
2127            "item",
2128            DataType::Utf8,
2129            true,
2130        ))))
2131    }
2132
2133    fn shredded_list_view_variant_array() -> ArrayRef {
2134        shredded_list_like_variant_array(DataType::ListView(Arc::new(Field::new(
2135            "item",
2136            DataType::Utf8,
2137            true,
2138        ))))
2139    }
2140
2141    fn shredded_large_list_view_variant_array() -> ArrayRef {
2142        shredded_list_like_variant_array(DataType::LargeListView(Arc::new(Field::new(
2143            "item",
2144            DataType::Utf8,
2145            true,
2146        ))))
2147    }
2148
2149    fn shredded_struct_with_list_variant_array() -> ArrayRef {
2150        let json_rows: ArrayRef = Arc::new(StringArray::from(vec![
2151            Some(r#"{"a": ["comedy", "drama"]}"#),
2152            Some(r#"{"a": ["horror", 123]}"#),
2153        ]));
2154        let input = json_to_variant(&json_rows).unwrap();
2155
2156        let list_schema = DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
2157        let shredding_schema = ShreddedSchemaBuilder::default()
2158            .with_path("a", &list_schema)
2159            .unwrap()
2160            .build();
2161        let shredded = shred_variant(&input, &shredding_schema).unwrap();
2162        ArrayRef::from(shredded)
2163    }
2164
2165    fn shredded_list_of_struct_variant_array() -> ArrayRef {
2166        let json_rows: ArrayRef = Arc::new(StringArray::from(vec![
2167            Some(r#"[{"x": 1}, {"x": 2}]"#),
2168            Some(r#"[{"x": 3}, {"y": 4}]"#),
2169        ]));
2170        let input = json_to_variant(&json_rows).unwrap();
2171
2172        let struct_type =
2173            DataType::Struct(Fields::from(vec![Field::new("x", DataType::Int32, true)]));
2174        let list_schema = DataType::List(Arc::new(Field::new("item", struct_type, true)));
2175        let shredded = shred_variant(&input, &list_schema).unwrap();
2176        ArrayRef::from(shredded)
2177    }
2178
2179    /// Helper function to create a shredded variant array representing objects
2180    ///
2181    /// This creates an array that represents:
2182    /// Row 0: {"x": 1, "y": "foo"}  (x is shredded, y is in value field)
2183    /// Row 1: {"x": 42}             (x is shredded, perfect shredding)
2184    ///
2185    /// The physical layout follows the shredding spec where:
2186    /// - metadata: contains object metadata
2187    /// - typed_value: StructArray with field "x" (ShreddedVariantFieldArray)
2188    /// - value: contains fallback for unshredded fields like {"y": "foo"}
2189    /// - The "x" field has typed_value=Int32Array and value=NULL (perfect shredding)
2190    fn shredded_object_with_x_field_variant_array() -> ArrayRef {
2191        // Create the base metadata for objects
2192        let (metadata, y_field_value) = {
2193            let mut builder = parquet_variant::VariantBuilder::new();
2194            let mut obj = builder.new_object();
2195            obj.insert("x", Variant::Int32(42));
2196            obj.insert("y", Variant::from("foo"));
2197            obj.finish();
2198            builder.finish()
2199        };
2200
2201        // Create metadata array (same for both rows)
2202        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
2203
2204        // Create the main value field per the 3-step shredding spec:
2205        // Step 2: If field not in shredding schema, check value field
2206        // Row 0: {"y": "foo"} (y is not shredded, stays in value for step 2)
2207        // Row 1: {} (empty object - no unshredded fields)
2208        let empty_object_value = {
2209            let mut builder = parquet_variant::VariantBuilder::new();
2210            let obj = builder.new_object();
2211            obj.finish();
2212            let (_, value) = builder.finish();
2213            value
2214        };
2215
2216        let value_array = BinaryViewArray::from(vec![
2217            Some(y_field_value.as_slice()),      // Row 0 has {"y": "foo"}
2218            Some(empty_object_value.as_slice()), // Row 1 has {}
2219        ]);
2220
2221        // Create the "x" field as a ShreddedVariantFieldArray
2222        // This represents the shredded Int32 values for the "x" field
2223        let x_field_typed_value = Int32Array::from(vec![Some(1), Some(42)]);
2224
2225        // For perfect shredding of the x field, no "value" column, only typed_value
2226        let x_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
2227            x_field_typed_value,
2228        ) as ArrayRef);
2229
2230        // Create the main typed_value as a struct containing the "x" field
2231        let typed_value_fields = Fields::from(vec![Field::new(
2232            "x",
2233            x_field_shredded.data_type().clone(),
2234            true,
2235        )]);
2236        let typed_value_struct = StructArray::try_new(
2237            typed_value_fields,
2238            vec![ArrayRef::from(x_field_shredded)],
2239            None, // No nulls - both rows have the object structure
2240        )
2241        .unwrap();
2242
2243        // Create the main VariantArray
2244        ArrayRef::from(VariantArray::from_parts(
2245            Arc::new(metadata_array),
2246            Arc::new(value_array),
2247            Some(Arc::new(typed_value_struct)),
2248            None,
2249        ))
2250    }
2251
2252    /// Simple test to check if nested paths are supported by current implementation
2253    #[test]
2254    fn test_simple_nested_path_support() {
2255        // Check: How does VariantPath parse different strings?
2256        println!("Testing path parsing:");
2257
2258        let path_x = VariantPath::try_from("x").unwrap();
2259        let elements_x: Vec<_> = path_x.iter().collect();
2260        println!("  'x' -> {} elements: {:?}", elements_x.len(), elements_x);
2261
2262        let path_ax = VariantPath::try_from("a.x").unwrap();
2263        let elements_ax: Vec<_> = path_ax.iter().collect();
2264        println!(
2265            "  'a.x' -> {} elements: {:?}",
2266            elements_ax.len(),
2267            elements_ax
2268        );
2269
2270        let path_ax_alt = VariantPath::try_from("$.a.x").unwrap();
2271        let elements_ax_alt: Vec<_> = path_ax_alt.iter().collect();
2272        println!(
2273            "  '$.a.x' -> {} elements: {:?}",
2274            elements_ax_alt.len(),
2275            elements_ax_alt
2276        );
2277
2278        let path_nested = VariantPath::try_from("a").unwrap().join("x");
2279        let elements_nested: Vec<_> = path_nested.iter().collect();
2280        println!(
2281            "  VariantPath::try_from('a').unwrap().join('x') -> {} elements: {:?}",
2282            elements_nested.len(),
2283            elements_nested
2284        );
2285
2286        // Use your existing simple test data but try "a.x" instead of "x"
2287        let array = shredded_object_with_x_field_variant_array();
2288
2289        // Test if variant_get with REAL nested path throws not implemented error
2290        let real_nested_path = VariantPath::try_from("a").unwrap().join("x");
2291        let options = GetOptions::new_with_path(real_nested_path);
2292        let result = variant_get(&array, options);
2293
2294        match result {
2295            Ok(_) => {
2296                println!("Nested path 'a.x' works unexpectedly!");
2297            }
2298            Err(e) => {
2299                println!("Nested path 'a.x' error: {e}");
2300                if e.to_string().contains("Not yet implemented")
2301                    || e.to_string().contains("NotYetImplemented")
2302                {
2303                    println!("This is expected - nested paths are not implemented");
2304                    return;
2305                }
2306                // Any other error is also expected for now
2307                println!("This shows nested paths need implementation");
2308            }
2309        }
2310    }
2311
2312    /// Test comprehensive variant_get scenarios with Int32 conversion
2313    /// Test depth 0: Direct field access "x" with Int32 conversion
2314    /// Covers shredded vs non-shredded VariantArrays for simple field access
2315    #[test]
2316    fn test_depth_0_int32_conversion() {
2317        println!("=== Testing Depth 0: Direct field access ===");
2318
2319        // Non-shredded test data: [{"x": 42}, {"x": "foo"}, {"y": 10}]
2320        let unshredded_array = create_depth_0_test_data();
2321
2322        let field = Field::new("result", DataType::Int32, true);
2323        let path = VariantPath::try_from("x").unwrap();
2324        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2325        let result = variant_get(&unshredded_array, options).unwrap();
2326
2327        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2328            Some(42), // {"x": 42} -> 42
2329            None,     // {"x": "foo"} -> NULL (type mismatch)
2330            None,     // {"y": 10} -> NULL (field missing)
2331        ]));
2332        assert_eq!(&result, &expected);
2333        println!("Depth 0 (unshredded) passed");
2334
2335        // Shredded test data: using simplified approach based on working pattern
2336        let shredded_array = create_depth_0_shredded_test_data_simple();
2337
2338        let field = Field::new("result", DataType::Int32, true);
2339        let path = VariantPath::try_from("x").unwrap();
2340        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2341        let result = variant_get(&shredded_array, options).unwrap();
2342
2343        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2344            Some(42), // {"x": 42} -> 42 (from typed_value)
2345            None,     // {"x": "foo"} -> NULL (type mismatch, from value field)
2346        ]));
2347        assert_eq!(&result, &expected);
2348        println!("Depth 0 (shredded) passed");
2349    }
2350
2351    /// Test depth 1: Single nested field access "a.x" with Int32 conversion
2352    /// Covers shredded vs non-shredded VariantArrays for nested field access
2353    #[test]
2354    fn test_depth_1_int32_conversion() {
2355        println!("=== Testing Depth 1: Single nested field access ===");
2356
2357        // Non-shredded test data from the GitHub issue
2358        let unshredded_array = create_nested_path_test_data();
2359
2360        let field = Field::new("result", DataType::Int32, true);
2361        let path = VariantPath::try_from("a.x").unwrap(); // Dot notation!
2362        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2363        let result = variant_get(&unshredded_array, options).unwrap();
2364
2365        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2366            Some(55), // {"a": {"x": 55}} -> 55
2367            None,     // {"a": {"x": "foo"}} -> NULL (type mismatch)
2368        ]));
2369        assert_eq!(&result, &expected);
2370        println!("Depth 1 (unshredded) passed");
2371
2372        // Shredded test data: depth 1 nested shredding
2373        let shredded_array = create_depth_1_shredded_test_data_working();
2374
2375        let field = Field::new("result", DataType::Int32, true);
2376        let path = VariantPath::try_from("a.x").unwrap(); // Dot notation!
2377        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2378        let result = variant_get(&shredded_array, options).unwrap();
2379
2380        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2381            Some(55), // {"a": {"x": 55}} -> 55 (from nested shredded x)
2382            None,     // {"a": {"x": "foo"}} -> NULL (type mismatch in nested value)
2383        ]));
2384        assert_eq!(&result, &expected);
2385        println!("Depth 1 (shredded) passed");
2386    }
2387
2388    /// Test depth 2: Double nested field access "a.b.x" with Int32 conversion
2389    /// Covers shredded vs non-shredded VariantArrays for deeply nested field access
2390    #[test]
2391    fn test_depth_2_int32_conversion() {
2392        println!("=== Testing Depth 2: Double nested field access ===");
2393
2394        // Non-shredded test data: [{"a": {"b": {"x": 100}}}, {"a": {"b": {"x": "bar"}}}, {"a": {"b": {"y": 200}}}]
2395        let unshredded_array = create_depth_2_test_data();
2396
2397        let field = Field::new("result", DataType::Int32, true);
2398        let path = VariantPath::try_from("a.b.x").unwrap(); // Double nested dot notation!
2399        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2400        let result = variant_get(&unshredded_array, options).unwrap();
2401
2402        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2403            Some(100), // {"a": {"b": {"x": 100}}} -> 100
2404            None,      // {"a": {"b": {"x": "bar"}}} -> NULL (type mismatch)
2405            None,      // {"a": {"b": {"y": 200}}} -> NULL (field missing)
2406        ]));
2407        assert_eq!(&result, &expected);
2408        println!("Depth 2 (unshredded) passed");
2409
2410        // Shredded test data: depth 2 nested shredding
2411        let shredded_array = create_depth_2_shredded_test_data_working();
2412
2413        let field = Field::new("result", DataType::Int32, true);
2414        let path = VariantPath::try_from("a.b.x").unwrap(); // Double nested dot notation!
2415        let options = GetOptions::new_with_path(path).with_as_type(Some(FieldRef::from(field)));
2416        let result = variant_get(&shredded_array, options).unwrap();
2417
2418        let expected: ArrayRef = Arc::new(Int32Array::from(vec![
2419            Some(100), // {"a": {"b": {"x": 100}}} -> 100 (from deeply nested shredded x)
2420            None,      // {"a": {"b": {"x": "bar"}}} -> NULL (type mismatch in deep value)
2421            None,      // {"a": {"b": {"y": 200}}} -> NULL (field missing in deep structure)
2422        ]));
2423        assert_eq!(&result, &expected);
2424        println!("Depth 2 (shredded) passed");
2425    }
2426
2427    /// Test that demonstrates what CURRENTLY WORKS
2428    ///
2429    /// This shows that nested path functionality does work, but only when the
2430    /// test data matches what the current implementation expects
2431    #[test]
2432    fn test_current_nested_path_functionality() {
2433        let array = shredded_object_with_x_field_variant_array();
2434
2435        // Test: Extract the "x" field (single level) - this works
2436        let single_path = VariantPath::try_from("x").unwrap();
2437        let field = Field::new("result", DataType::Int32, true);
2438        let options =
2439            GetOptions::new_with_path(single_path).with_as_type(Some(FieldRef::from(field)));
2440        let result = variant_get(&array, options).unwrap();
2441
2442        println!("Single path 'x' works - result: {result:?}");
2443
2444        // Test: Try nested path "a.x" - this is what we need to implement
2445        let nested_path = VariantPath::try_from("a").unwrap().join("x");
2446        let field = Field::new("result", DataType::Int32, true);
2447        let options =
2448            GetOptions::new_with_path(nested_path).with_as_type(Some(FieldRef::from(field)));
2449        let result = variant_get(&array, options).unwrap();
2450
2451        println!("Nested path 'a.x' result: {result:?}");
2452    }
2453
2454    #[test]
2455    fn test_variant_get_as_variant_from_unshredded_input() {
2456        let (unshredded, _) = create_variant_get_as_variant_test_data();
2457        let unshredded_field = VariantArray::try_new(&unshredded).unwrap().field("result");
2458        assert_variant_field_extraction_returns_unshredded_variant(&unshredded, &unshredded_field);
2459    }
2460
2461    #[test]
2462    fn test_variant_get_as_variant_from_shredded_input() {
2463        let (unshredded, shredded) = create_variant_get_as_variant_test_data();
2464        let unshredded_field = VariantArray::try_new(&unshredded).unwrap().field("result");
2465        assert_variant_field_extraction_returns_unshredded_variant(&shredded, &unshredded_field);
2466    }
2467
2468    #[test]
2469    fn test_variant_get_as_shredded_variant_is_not_yet_supported() {
2470        let (_, shredded) = create_variant_get_as_variant_test_data();
2471        // Deriving the request field from the shredded array yields a `VariantType` field whose
2472        // struct carries a `typed_value` -- a request to shred the output. That is unsupported
2473        // (https://github.com/apache/arrow-rs/issues/8153) and must error, not silently return a
2474        // plain binary variant.
2475        let shredded_field = VariantArray::try_new(&shredded).unwrap().field("result");
2476        assert!(requested_field_is_shredded(Some(&shredded_field)));
2477
2478        let options = GetOptions::new_with_path(VariantPath::try_from("field_name").unwrap())
2479            .with_as_type(Some(FieldRef::from(shredded_field)));
2480        let err = variant_get(&shredded, options).unwrap_err();
2481        assert!(
2482            matches!(err, ArrowError::NotYetImplemented(_)),
2483            "expected NotYetImplemented, got {err:?}"
2484        );
2485    }
2486
2487    #[test]
2488    fn test_variant_get_list_element_wildcard_is_invalid_argument() {
2489        let (unshredded, _) = create_variant_get_as_variant_test_data();
2490        let options = GetOptions::new_with_path(VariantPath::try_from("field_name[*]").unwrap());
2491        let err = variant_get(&unshredded, options).unwrap_err();
2492        assert!(
2493            matches!(err, ArrowError::InvalidArgumentError(_)),
2494            "expected InvalidArgumentError, got {err:?}"
2495        );
2496    }
2497
2498    #[test]
2499    fn test_variant_get_missing_path_as_variant_annotates_value_non_nullable() {
2500        let (unshredded, shredded) = create_variant_get_as_variant_test_data();
2501        let variant_field = VariantArray::try_new(&unshredded).unwrap().field("result");
2502
2503        // indexing into a struct typed_value can never match: all-null variant output
2504        let options = GetOptions::new_with_path(VariantPath::try_from("field_name[0]").unwrap())
2505            .with_as_type(Some(FieldRef::from(variant_field)));
2506        let result = variant_get(&shredded, options).unwrap();
2507        let result_variant = VariantArray::try_new(&result).unwrap();
2508
2509        assert_eq!(result_variant.inner().null_count(), result_variant.len());
2510        let value_field = result_variant.inner().field_by_name("value").unwrap();
2511        assert!(!value_field.is_nullable());
2512    }
2513
2514    fn create_variant_get_as_variant_test_data() -> (ArrayRef, ArrayRef) {
2515        let input_json: ArrayRef = Arc::new(StringArray::from(vec![
2516            Some(r#"{"field_name": {"k": 100000}}"#),
2517            Some(r#"{"field_name": {"k": "s"}}"#),
2518        ]));
2519
2520        let unshredded = ArrayRef::from(json_to_variant(&input_json).unwrap());
2521        let unshredded_variant = VariantArray::try_new(&unshredded).unwrap();
2522
2523        let as_type = DataType::Struct(Fields::from(vec![Field::new(
2524            "field_name",
2525            DataType::Struct(Fields::from(vec![Field::new("k", DataType::Int32, true)])),
2526            true,
2527        )]));
2528        let shredded = ArrayRef::from(shred_variant(&unshredded_variant, &as_type).unwrap());
2529
2530        (unshredded, shredded)
2531    }
2532
2533    fn assert_variant_field_extraction_returns_unshredded_variant(
2534        input: &ArrayRef,
2535        variant_field: &Field,
2536    ) {
2537        let options = GetOptions::new_with_path(VariantPath::try_from("field_name").unwrap())
2538            .with_as_type(Some(FieldRef::from(variant_field.clone())));
2539
2540        let result = variant_get(input, options).unwrap();
2541        let result_variant = VariantArray::try_new(&result).unwrap();
2542
2543        assert!(result_variant.typed_value_column().is_none());
2544        assert!(result_variant.value_column().null_count() < result_variant.len());
2545        let value_field = result_variant.inner().field_by_name("value").unwrap();
2546        assert!(!value_field.is_nullable());
2547
2548        let expected_json: ArrayRef = Arc::new(StringArray::from(vec![
2549            Some(r#"{"k":100000}"#),
2550            Some(r#"{"k":"s"}"#),
2551        ]));
2552        let expected = json_to_variant(&expected_json).unwrap();
2553
2554        assert_eq!(result_variant.len(), expected.len());
2555        for i in 0..result_variant.len() {
2556            assert_eq!(result_variant.is_null(i), expected.is_null(i));
2557            if !result_variant.is_null(i) {
2558                assert_eq!(result_variant.value(i), expected.value(i));
2559            }
2560        }
2561    }
2562
2563    /// Create test data for depth 0 (direct field access)
2564    /// [{"x": 42}, {"x": "foo"}, {"y": 10}]
2565    fn create_depth_0_test_data() -> ArrayRef {
2566        let mut builder = crate::VariantArrayBuilder::new(3);
2567
2568        // Row 1: {"x": 42}
2569        {
2570            let json_str = r#"{"x": 42}"#;
2571            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2572            if let Ok(variant_array) = json_to_variant(&string_array) {
2573                builder.append_variant(variant_array.value(0));
2574            } else {
2575                builder.append_null();
2576            }
2577        }
2578
2579        // Row 2: {"x": "foo"}
2580        {
2581            let json_str = r#"{"x": "foo"}"#;
2582            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2583            if let Ok(variant_array) = json_to_variant(&string_array) {
2584                builder.append_variant(variant_array.value(0));
2585            } else {
2586                builder.append_null();
2587            }
2588        }
2589
2590        // Row 3: {"y": 10} (missing "x" field)
2591        {
2592            let json_str = r#"{"y": 10}"#;
2593            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2594            if let Ok(variant_array) = json_to_variant(&string_array) {
2595                builder.append_variant(variant_array.value(0));
2596            } else {
2597                builder.append_null();
2598            }
2599        }
2600
2601        ArrayRef::from(builder.build())
2602    }
2603
2604    /// Create test data for depth 1 (single nested field)
2605    /// This represents the exact scenarios from the GitHub issue: "a.x"
2606    fn create_nested_path_test_data() -> ArrayRef {
2607        let mut builder = crate::VariantArrayBuilder::new(2);
2608
2609        // Row 1: {"a": {"x": 55}, "b": 42}
2610        {
2611            let json_str = r#"{"a": {"x": 55}, "b": 42}"#;
2612            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2613            if let Ok(variant_array) = json_to_variant(&string_array) {
2614                builder.append_variant(variant_array.value(0));
2615            } else {
2616                builder.append_null();
2617            }
2618        }
2619
2620        // Row 2: {"a": {"x": "foo"}, "b": 42}
2621        {
2622            let json_str = r#"{"a": {"x": "foo"}, "b": 42}"#;
2623            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2624            if let Ok(variant_array) = json_to_variant(&string_array) {
2625                builder.append_variant(variant_array.value(0));
2626            } else {
2627                builder.append_null();
2628            }
2629        }
2630
2631        ArrayRef::from(builder.build())
2632    }
2633
2634    /// Create test data for depth 2 (double nested field)
2635    /// [{"a": {"b": {"x": 100}}}, {"a": {"b": {"x": "bar"}}}, {"a": {"b": {"y": 200}}}]
2636    fn create_depth_2_test_data() -> ArrayRef {
2637        let mut builder = crate::VariantArrayBuilder::new(3);
2638
2639        // Row 1: {"a": {"b": {"x": 100}}}
2640        {
2641            let json_str = r#"{"a": {"b": {"x": 100}}}"#;
2642            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2643            if let Ok(variant_array) = json_to_variant(&string_array) {
2644                builder.append_variant(variant_array.value(0));
2645            } else {
2646                builder.append_null();
2647            }
2648        }
2649
2650        // Row 2: {"a": {"b": {"x": "bar"}}}
2651        {
2652            let json_str = r#"{"a": {"b": {"x": "bar"}}}"#;
2653            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2654            if let Ok(variant_array) = json_to_variant(&string_array) {
2655                builder.append_variant(variant_array.value(0));
2656            } else {
2657                builder.append_null();
2658            }
2659        }
2660
2661        // Row 3: {"a": {"b": {"y": 200}}} (missing "x" field)
2662        {
2663            let json_str = r#"{"a": {"b": {"y": 200}}}"#;
2664            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json_str]));
2665            if let Ok(variant_array) = json_to_variant(&string_array) {
2666                builder.append_variant(variant_array.value(0));
2667            } else {
2668                builder.append_null();
2669            }
2670        }
2671
2672        ArrayRef::from(builder.build())
2673    }
2674
2675    /// Create simple shredded test data for depth 0 using a simplified working pattern
2676    /// Creates 2 rows: [{"x": 42}, {"x": "foo"}] with "x" shredded where possible
2677    fn create_depth_0_shredded_test_data_simple() -> ArrayRef {
2678        // Create base metadata using the working pattern
2679        let (metadata, string_x_value) = {
2680            let mut builder = parquet_variant::VariantBuilder::new();
2681            let mut obj = builder.new_object();
2682            obj.insert("x", Variant::from("foo"));
2683            obj.finish();
2684            builder.finish()
2685        };
2686
2687        // Metadata array (same for both rows)
2688        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
2689
2690        // Value array following the 3-step shredding spec:
2691        // Row 0: {} (x is shredded, no unshredded fields)
2692        // Row 1: {"x": "foo"} (x is a string, can't be shredded to Int32)
2693        let empty_object_value = {
2694            let mut builder = parquet_variant::VariantBuilder::new();
2695            let obj = builder.new_object();
2696            obj.finish();
2697            let (_, value) = builder.finish();
2698            value
2699        };
2700
2701        let value_array = BinaryViewArray::from(vec![
2702            Some(empty_object_value.as_slice()), // Row 0: {} (x shredded out)
2703            Some(string_x_value.as_slice()),     // Row 1: {"x": "foo"} (fallback)
2704        ]);
2705
2706        // Create the "x" field as a ShreddedVariantFieldArray
2707        let x_field_typed_value = Int32Array::from(vec![Some(42), None]);
2708
2709        // For the x field, only typed_value (perfect shredding when possible)
2710        let x_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
2711            x_field_typed_value,
2712        ) as ArrayRef);
2713
2714        // Create the main typed_value as a struct containing the "x" field
2715        let typed_value_fields = Fields::from(vec![Field::new(
2716            "x",
2717            x_field_shredded.data_type().clone(),
2718            true,
2719        )]);
2720        let typed_value_struct = StructArray::try_new(
2721            typed_value_fields,
2722            vec![ArrayRef::from(x_field_shredded)],
2723            None,
2724        )
2725        .unwrap();
2726
2727        // Build final VariantArray
2728        ArrayRef::from(VariantArray::from_parts(
2729            Arc::new(metadata_array),
2730            Arc::new(value_array),
2731            Some(Arc::new(typed_value_struct)),
2732            None,
2733        ))
2734    }
2735
2736    /// Create working depth 1 shredded test data based on the existing working pattern
2737    /// This creates a properly structured shredded variant for "a.x" where:
2738    /// - Row 0: {"a": {"x": 55}, "b": 42} with a.x shredded into typed_value
2739    /// - Row 1: {"a": {"x": "foo"}, "b": 42} with a.x fallback to value field due to type mismatch
2740    fn create_depth_1_shredded_test_data_working() -> ArrayRef {
2741        // Create metadata following the working pattern from shredded_object_with_x_field_variant_array
2742        let (metadata, _) = {
2743            // Create nested structure: {"a": {"x": 55}, "b": 42}
2744            let mut builder = parquet_variant::VariantBuilder::new();
2745            let mut obj = builder.new_object();
2746
2747            // Create the nested "a" object
2748            let mut a_obj = obj.new_object("a");
2749            a_obj.insert("x", Variant::Int32(55));
2750            a_obj.finish();
2751
2752            obj.insert("b", Variant::Int32(42));
2753            obj.finish();
2754            builder.finish()
2755        };
2756
2757        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 2));
2758
2759        // Create value arrays for the fallback case
2760        // Following the spec: if field cannot be shredded, it stays in value
2761        let empty_object_value = {
2762            let mut builder = parquet_variant::VariantBuilder::new();
2763            let obj = builder.new_object();
2764            obj.finish();
2765            let (_, value) = builder.finish();
2766            value
2767        };
2768
2769        // Row 1 fallback: use the working pattern from the existing shredded test
2770        // This avoids metadata issues by using the simple fallback approach
2771        let row1_fallback = {
2772            let mut builder = parquet_variant::VariantBuilder::new();
2773            let mut obj = builder.new_object();
2774            obj.insert("fallback", Variant::from("data"));
2775            obj.finish();
2776            let (_, value) = builder.finish();
2777            value
2778        };
2779
2780        let value_array = BinaryViewArray::from(vec![
2781            Some(empty_object_value.as_slice()), // Row 0: {} (everything shredded except b in unshredded fields)
2782            Some(row1_fallback.as_slice()), // Row 1: {"a": {"x": "foo"}, "b": 42} (a.x can't be shredded)
2783        ]);
2784
2785        // Create the nested shredded structure
2786        // Level 2: x field (the deepest level)
2787        let x_typed_value = Int32Array::from(vec![Some(55), None]);
2788        let x_field_shredded =
2789            ShreddedVariantFieldArray::perfectly_shredded(Arc::new(x_typed_value) as ArrayRef);
2790
2791        // Level 1: a field containing x field + value field for fallbacks
2792        // The "a" field needs both typed_value (for shredded x) and value (for fallback cases)
2793
2794        // Create the value field for "a" (for cases where a.x can't be shredded)
2795        let a_value_data = {
2796            let mut builder = parquet_variant::VariantBuilder::new();
2797            let obj = builder.new_object();
2798            obj.finish();
2799            let (_, value) = builder.finish();
2800            value
2801        };
2802        let a_value_array = BinaryViewArray::from(vec![
2803            None,                          // Row 0: x is shredded, so no value fallback needed
2804            Some(a_value_data.as_slice()), // Row 1: fallback for a.x="foo" (but logic will check typed_value first)
2805        ]);
2806
2807        let a_inner_fields = Fields::from(vec![Field::new(
2808            "x",
2809            x_field_shredded.data_type().clone(),
2810            true,
2811        )]);
2812        let a_inner_typed_value = Arc::new(
2813            StructArray::try_new(a_inner_fields, vec![ArrayRef::from(x_field_shredded)], None)
2814                .unwrap(),
2815        ) as ArrayRef;
2816        let a_field_shredded = ShreddedVariantFieldArray::from_parts(
2817            Arc::new(a_value_array),
2818            Some(a_inner_typed_value),
2819            None,
2820        );
2821
2822        // Level 0: main typed_value struct containing a field
2823        let typed_value_fields = Fields::from(vec![Field::new(
2824            "a",
2825            a_field_shredded.data_type().clone(),
2826            true,
2827        )]);
2828        let typed_value_struct = StructArray::try_new(
2829            typed_value_fields,
2830            vec![ArrayRef::from(a_field_shredded)],
2831            None,
2832        )
2833        .unwrap();
2834
2835        // Build final VariantArray
2836        ArrayRef::from(VariantArray::from_parts(
2837            Arc::new(metadata_array),
2838            Arc::new(value_array),
2839            Some(Arc::new(typed_value_struct)),
2840            None,
2841        ))
2842    }
2843
2844    /// Create working depth 2 shredded test data for "a.b.x" paths
2845    /// This creates a 3-level nested shredded structure where:
2846    /// - Row 0: {"a": {"b": {"x": 100}}} with a.b.x shredded into typed_value
2847    /// - Row 1: {"a": {"b": {"x": "bar"}}} with type mismatch fallback
2848    /// - Row 2: {"a": {"b": {"y": 200}}} with missing field fallback
2849    fn create_depth_2_shredded_test_data_working() -> ArrayRef {
2850        // Create metadata following the working pattern
2851        let (metadata, _) = {
2852            // Create deeply nested structure: {"a": {"b": {"x": 100}}}
2853            let mut builder = parquet_variant::VariantBuilder::new();
2854            let mut obj = builder.new_object();
2855
2856            // Create the nested "a.b" structure
2857            let mut a_obj = obj.new_object("a");
2858            let mut b_obj = a_obj.new_object("b");
2859            b_obj.insert("x", Variant::Int32(100));
2860            b_obj.finish();
2861            a_obj.finish();
2862
2863            obj.finish();
2864            builder.finish()
2865        };
2866
2867        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 3));
2868
2869        // Create value arrays for fallback cases
2870        let empty_object_value = {
2871            let mut builder = parquet_variant::VariantBuilder::new();
2872            let obj = builder.new_object();
2873            obj.finish();
2874            let (_, value) = builder.finish();
2875            value
2876        };
2877
2878        // Simple fallback values - avoiding complex nested metadata
2879        let value_array = BinaryViewArray::from(vec![
2880            Some(empty_object_value.as_slice()), // Row 0: fully shredded
2881            Some(empty_object_value.as_slice()), // Row 1: fallback (simplified)
2882            Some(empty_object_value.as_slice()), // Row 2: fallback (simplified)
2883        ]);
2884
2885        // Create the deeply nested shredded structure: a.b.x
2886
2887        // Level 3: x field (deepest level)
2888        let x_typed_value = Int32Array::from(vec![Some(100), None, None]);
2889        let x_field_shredded =
2890            ShreddedVariantFieldArray::perfectly_shredded(Arc::new(x_typed_value) as ArrayRef);
2891
2892        // Level 2: b field containing x field + value field
2893        let b_value_data = {
2894            let mut builder = parquet_variant::VariantBuilder::new();
2895            let obj = builder.new_object();
2896            obj.finish();
2897            let (_, value) = builder.finish();
2898            value
2899        };
2900        let b_value_array = BinaryViewArray::from(vec![
2901            None,                          // Row 0: x is shredded
2902            Some(b_value_data.as_slice()), // Row 1: fallback for b.x="bar"
2903            Some(b_value_data.as_slice()), // Row 2: fallback for b.y=200
2904        ]);
2905
2906        let b_inner_fields = Fields::from(vec![Field::new(
2907            "x",
2908            x_field_shredded.data_type().clone(),
2909            true,
2910        )]);
2911        let b_inner_typed_value = Arc::new(
2912            StructArray::try_new(b_inner_fields, vec![ArrayRef::from(x_field_shredded)], None)
2913                .unwrap(),
2914        ) as ArrayRef;
2915        let b_field_shredded = ShreddedVariantFieldArray::from_parts(
2916            Arc::new(b_value_array),
2917            Some(b_inner_typed_value),
2918            None,
2919        );
2920
2921        // Level 1: a field containing b field + value field
2922        let a_value_data = {
2923            let mut builder = parquet_variant::VariantBuilder::new();
2924            let obj = builder.new_object();
2925            obj.finish();
2926            let (_, value) = builder.finish();
2927            value
2928        };
2929        let a_value_array = BinaryViewArray::from(vec![
2930            None,                          // Row 0: b is shredded
2931            Some(a_value_data.as_slice()), // Row 1: fallback for a.b.*
2932            Some(a_value_data.as_slice()), // Row 2: fallback for a.b.*
2933        ]);
2934
2935        let a_inner_fields = Fields::from(vec![Field::new(
2936            "b",
2937            b_field_shredded.data_type().clone(),
2938            true,
2939        )]);
2940        let a_inner_typed_value = Arc::new(
2941            StructArray::try_new(a_inner_fields, vec![ArrayRef::from(b_field_shredded)], None)
2942                .unwrap(),
2943        ) as ArrayRef;
2944        let a_field_shredded = ShreddedVariantFieldArray::from_parts(
2945            Arc::new(a_value_array),
2946            Some(a_inner_typed_value),
2947            None,
2948        );
2949
2950        // Level 0: main typed_value struct containing a field
2951        let typed_value_fields = Fields::from(vec![Field::new(
2952            "a",
2953            a_field_shredded.data_type().clone(),
2954            true,
2955        )]);
2956        let typed_value_struct = StructArray::try_new(
2957            typed_value_fields,
2958            vec![ArrayRef::from(a_field_shredded)],
2959            None,
2960        )
2961        .unwrap();
2962
2963        // Build final VariantArray
2964        ArrayRef::from(VariantArray::from_parts(
2965            Arc::new(metadata_array),
2966            Arc::new(value_array),
2967            Some(Arc::new(typed_value_struct)),
2968            None,
2969        ))
2970    }
2971
2972    #[test]
2973    fn test_field_path_non_struct_returns_missing_path_step() {
2974        // Use the existing simple test data that has Int32 as typed_value
2975        let variant_array = perfectly_shredded_int32_variant_array();
2976
2977        for safe in [true, false] {
2978            let options = GetOptions {
2979                path: VariantPath::try_from("nonexistent_field").unwrap(),
2980                as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
2981                cast_options: CastOptions {
2982                    safe,
2983                    ..Default::default()
2984                },
2985            };
2986
2987            let result_array = variant_get(&variant_array, options).unwrap();
2988            assert_eq!(result_array.len(), 3);
2989            assert!(result_array.is_null(0));
2990            assert!(result_array.is_null(1));
2991            assert!(result_array.is_null(2));
2992        }
2993    }
2994
2995    #[test]
2996    fn test_strict_cast_options_index_on_non_list_returns_null() {
2997        use arrow::compute::CastOptions;
2998        use arrow::datatypes::{DataType, Field};
2999        use parquet_variant::VariantPath;
3000        use std::sync::Arc;
3001
3002        // Use existing test data that has Int32 typed_value at the top level.
3003        let variant_array = perfectly_shredded_int32_variant_array();
3004        let options = GetOptions {
3005            path: VariantPath::from(0),
3006            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
3007            cast_options: CastOptions {
3008                safe: false,
3009                ..Default::default()
3010            },
3011        };
3012
3013        let variant_array_ref: Arc<dyn Array> = variant_array.clone();
3014        let result = variant_get(&variant_array_ref, options).unwrap();
3015
3016        assert_eq!(result.len(), 3);
3017        assert!(result.is_null(0));
3018        assert!(result.is_null(1));
3019        assert!(result.is_null(2));
3020    }
3021
3022    #[test]
3023    fn test_error_message_boolean_type_display() {
3024        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([Some("abcd")]));
3025
3026        // Request Boolean with strict casting to force an error
3027        let options = GetOptions {
3028            path: VariantPath::default(),
3029            as_type: Some(Arc::new(Field::new("result", DataType::Boolean, true))),
3030            cast_options: CastOptions {
3031                safe: false,
3032                ..Default::default()
3033            },
3034        };
3035
3036        let err = variant_get(&variant_array, options).unwrap_err();
3037        let msg = err.to_string();
3038        assert!(msg.contains("Failed to extract primitive of type Boolean"));
3039    }
3040
3041    #[test]
3042    fn test_error_message_numeric_type_display() {
3043        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([Some("abcd")]));
3044
3045        // Request Float32 with strict casting to force an error
3046        let options = GetOptions {
3047            path: VariantPath::default(),
3048            as_type: Some(Arc::new(Field::new("result", DataType::Float32, true))),
3049            cast_options: CastOptions {
3050                safe: false,
3051                ..Default::default()
3052            },
3053        };
3054
3055        let err = variant_get(&variant_array, options).unwrap_err();
3056        let msg = err.to_string();
3057        assert!(msg.contains("Failed to extract primitive of type Float32"));
3058    }
3059
3060    #[test]
3061    fn test_error_message_temporal_type_display() {
3062        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([Some(false)]));
3063
3064        // Request Timestamp with strict casting to force an error
3065        let options = GetOptions {
3066            path: VariantPath::default(),
3067            as_type: Some(Arc::new(Field::new(
3068                "result",
3069                DataType::Timestamp(TimeUnit::Nanosecond, None),
3070                true,
3071            ))),
3072            cast_options: CastOptions {
3073                safe: false,
3074                ..Default::default()
3075            },
3076        };
3077
3078        let err = variant_get(&variant_array, options).unwrap_err();
3079        let msg = err.to_string();
3080        assert!(msg.contains("Failed to extract primitive of type Timestamp(ns)"));
3081    }
3082
3083    #[test]
3084    fn test_null_buffer_union_for_shredded_paths() {
3085        // Test that null buffers are properly unioned when traversing shredded paths
3086        // This test verifies scovich's null buffer union requirement
3087
3088        // Create a depth-1 shredded variant array where:
3089        // - The top-level variant array has some nulls
3090        // - The nested typed_value also has some nulls
3091        // - The result should be the union of both null buffers
3092
3093        let variant_array = create_depth_1_shredded_test_data_working();
3094
3095        // Get the field "x" which should union nulls from:
3096        // 1. The top-level variant array nulls
3097        // 2. The "a" field's typed_value nulls
3098        // 3. The "x" field's typed_value nulls
3099        let options = GetOptions {
3100            path: VariantPath::try_from("a.x").unwrap(),
3101            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
3102            cast_options: CastOptions::default(),
3103        };
3104
3105        let result = variant_get(&variant_array, options).unwrap();
3106
3107        // Verify the result length matches input
3108        assert_eq!(result.len(), variant_array.len());
3109
3110        // The null pattern should reflect the union of all ancestor nulls
3111        // Row 0: Should have valid data (path exists and is shredded as Int32)
3112        // Row 1: Should be null (due to type mismatch - "foo" can't cast to Int32)
3113        assert!(!result.is_null(0), "Row 0 should have valid Int32 data");
3114        assert!(
3115            result.is_null(1),
3116            "Row 1 should be null due to type casting failure"
3117        );
3118
3119        // Verify the actual values
3120        let int32_result = result.as_any().downcast_ref::<Int32Array>().unwrap();
3121        assert_eq!(int32_result.value(0), 55); // The valid Int32 value
3122    }
3123
3124    #[test]
3125    fn test_struct_null_mask_union_from_children() {
3126        // Test that struct null masks properly union nulls from children field extractions
3127        // This verifies scovich's concern about incomplete null masks in struct construction
3128
3129        // Create test data where some fields will fail type casting
3130        let json_strings = vec![
3131            r#"{"a": 42, "b": "hello"}"#, // Row 0: a=42 (castable to int), b="hello" (not castable to int)
3132            r#"{"a": "world", "b": 100}"#, // Row 1: a="world" (not castable to int), b=100 (castable to int)
3133            r#"{"a": 55, "b": 77}"#,       // Row 2: a=55 (castable to int), b=77 (castable to int)
3134        ];
3135
3136        let string_array: Arc<dyn arrow::array::Array> = Arc::new(StringArray::from(json_strings));
3137        let variant_array = json_to_variant(&string_array).unwrap();
3138
3139        // Request extraction as a struct with both fields as Int32
3140        // This should create child arrays where some fields are null due to casting failures
3141        let struct_fields = Fields::from(vec![
3142            Field::new("a", DataType::Int32, true),
3143            Field::new("b", DataType::Int32, true),
3144        ]);
3145        let struct_type = DataType::Struct(struct_fields);
3146
3147        let options = GetOptions {
3148            path: VariantPath::default(), // Extract the whole object as struct
3149            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
3150            cast_options: CastOptions::default(),
3151        };
3152
3153        let variant_array_ref = ArrayRef::from(variant_array);
3154        let result = variant_get(&variant_array_ref, options).unwrap();
3155
3156        // Verify the result is a StructArray
3157        let struct_result = result.as_struct();
3158        assert_eq!(struct_result.len(), 3);
3159
3160        // Get the individual field arrays
3161        let field_a = struct_result
3162            .column(0)
3163            .as_any()
3164            .downcast_ref::<Int32Array>()
3165            .unwrap();
3166        let field_b = struct_result
3167            .column(1)
3168            .as_any()
3169            .downcast_ref::<Int32Array>()
3170            .unwrap();
3171
3172        // Verify field values and nulls
3173        // Row 0: a=42 (valid), b=null (casting failure)
3174        assert!(!field_a.is_null(0));
3175        assert_eq!(field_a.value(0), 42);
3176        assert!(field_b.is_null(0)); // "hello" can't cast to int
3177
3178        // Row 1: a=null (casting failure), b=100 (valid)
3179        assert!(field_a.is_null(1)); // "world" can't cast to int
3180        assert!(!field_b.is_null(1));
3181        assert_eq!(field_b.value(1), 100);
3182
3183        // Row 2: a=55 (valid), b=77 (valid)
3184        assert!(!field_a.is_null(2));
3185        assert_eq!(field_a.value(2), 55);
3186        assert!(!field_b.is_null(2));
3187        assert_eq!(field_b.value(2), 77);
3188
3189        // Verify the struct-level null mask properly unions child nulls
3190        // The struct should NOT be null in any row because each row has at least one valid field
3191        // (This tests that we're not incorrectly making the entire struct null when children fail)
3192        assert!(!struct_result.is_null(0)); // Has valid field 'a'
3193        assert!(!struct_result.is_null(1)); // Has valid field 'b'
3194        assert!(!struct_result.is_null(2)); // Has both valid fields
3195    }
3196
3197    #[test]
3198    fn test_field_nullability_preservation() {
3199        // Test that field nullability from GetOptions.as_type is preserved in the result
3200
3201        let json_strings = vec![
3202            r#"{"x": 42}"#,                  // Row 0: Valid int that should convert to Int32
3203            r#"{"x": "not_a_number"}"#,      // Row 1: String that can't cast to Int32
3204            r#"{"x": null}"#,                // Row 2: Explicit null value
3205            r#"{"x": "hello"}"#,             // Row 3: Another string (wrong type)
3206            r#"{"y": 100}"#,                 // Row 4: Missing "x" field (SQL NULL case)
3207            r#"{"x": 127}"#, // Row 5: Small int (could be Int8, widening cast candidate)
3208            r#"{"x": 32767}"#, // Row 6: Medium int (could be Int16, widening cast candidate)
3209            r#"{"x": 2147483647}"#, // Row 7: Max Int32 value (fits in Int32)
3210            r#"{"x": 9223372036854775807}"#, // Row 8: Large Int64 value (cannot convert to Int32)
3211        ];
3212
3213        let string_array: Arc<dyn arrow::array::Array> = Arc::new(StringArray::from(json_strings));
3214        let variant_array = json_to_variant(&string_array).unwrap();
3215
3216        // Test 1: nullable field (should allow nulls from cast failures)
3217        let nullable_field = Arc::new(Field::new("result", DataType::Int32, true));
3218        let options_nullable = GetOptions {
3219            path: VariantPath::try_from("x").unwrap(),
3220            as_type: Some(nullable_field.clone()),
3221            cast_options: CastOptions::default(),
3222        };
3223
3224        let variant_array_ref = ArrayRef::from(variant_array);
3225        let result_nullable = variant_get(&variant_array_ref, options_nullable).unwrap();
3226
3227        // Verify we get an Int32Array with nulls for cast failures
3228        let int32_result = result_nullable
3229            .as_any()
3230            .downcast_ref::<Int32Array>()
3231            .unwrap();
3232        assert_eq!(int32_result.len(), 9);
3233
3234        // Row 0: 42 converts successfully to Int32
3235        assert!(!int32_result.is_null(0));
3236        assert_eq!(int32_result.value(0), 42);
3237
3238        // Row 1: "not_a_number" fails to convert -> NULL
3239        assert!(int32_result.is_null(1));
3240
3241        // Row 2: explicit null value -> NULL
3242        assert!(int32_result.is_null(2));
3243
3244        // Row 3: "hello" (wrong type) fails to convert -> NULL
3245        assert!(int32_result.is_null(3));
3246
3247        // Row 4: missing "x" field (SQL NULL case) -> NULL
3248        assert!(int32_result.is_null(4));
3249
3250        // Row 5: 127 (small int, potential Int8 -> Int32 widening)
3251        // Current behavior: JSON parses to Int8, should convert to Int32
3252        assert!(!int32_result.is_null(5));
3253        assert_eq!(int32_result.value(5), 127);
3254
3255        // Row 6: 32767 (medium int, potential Int16 -> Int32 widening)
3256        // Current behavior: JSON parses to Int16, should convert to Int32
3257        assert!(!int32_result.is_null(6));
3258        assert_eq!(int32_result.value(6), 32767);
3259
3260        // Row 7: 2147483647 (max Int32, fits exactly)
3261        // Current behavior: Should convert successfully
3262        assert!(!int32_result.is_null(7));
3263        assert_eq!(int32_result.value(7), 2147483647);
3264
3265        // Row 8: 9223372036854775807 (large Int64, cannot fit in Int32)
3266        // Current behavior: Should fail conversion -> NULL
3267        assert!(int32_result.is_null(8));
3268
3269        // Test 2: non-nullable field (behavior should be the same with safe casting)
3270        let non_nullable_field = Arc::new(Field::new("result", DataType::Int32, false));
3271        let options_non_nullable = GetOptions {
3272            path: VariantPath::try_from("x").unwrap(),
3273            as_type: Some(non_nullable_field.clone()),
3274            cast_options: CastOptions::default(), // safe=true by default
3275        };
3276
3277        // Create variant array again since we moved it
3278        let variant_array_2 = json_to_variant(&string_array).unwrap();
3279        let variant_array_ref_2 = ArrayRef::from(variant_array_2);
3280        let result_non_nullable = variant_get(&variant_array_ref_2, options_non_nullable).unwrap();
3281        let int32_result_2 = result_non_nullable
3282            .as_any()
3283            .downcast_ref::<Int32Array>()
3284            .unwrap();
3285
3286        // Even with a non-nullable field, safe casting should still produce nulls for failures
3287        assert_eq!(int32_result_2.len(), 9);
3288
3289        // Row 0: 42 converts successfully to Int32
3290        assert!(!int32_result_2.is_null(0));
3291        assert_eq!(int32_result_2.value(0), 42);
3292
3293        // Rows 1-4: All should be null due to safe casting behavior
3294        // (non-nullable field specification doesn't override safe casting behavior)
3295        assert!(int32_result_2.is_null(1)); // "not_a_number"
3296        assert!(int32_result_2.is_null(2)); // explicit null
3297        assert!(int32_result_2.is_null(3)); // "hello"
3298        assert!(int32_result_2.is_null(4)); // missing field
3299
3300        // Rows 5-7: These should also convert successfully (numeric widening/fitting)
3301        assert!(!int32_result_2.is_null(5)); // 127 (Int8 -> Int32)
3302        assert_eq!(int32_result_2.value(5), 127);
3303        assert!(!int32_result_2.is_null(6)); // 32767 (Int16 -> Int32)
3304        assert_eq!(int32_result_2.value(6), 32767);
3305        assert!(!int32_result_2.is_null(7)); // 2147483647 (fits in Int32)
3306        assert_eq!(int32_result_2.value(7), 2147483647);
3307
3308        // Row 8: Large Int64 should fail conversion -> NULL
3309        assert!(int32_result_2.is_null(8)); // 9223372036854775807 (too large for Int32)
3310    }
3311
3312    #[test]
3313    fn test_struct_extraction_subset_superset_schema_perfectly_shredded() {
3314        // Create variant with diverse null patterns and empty objects
3315        let variant_array = create_comprehensive_shredded_variant();
3316
3317        // Request struct with fields "a", "b", "d" (skip existing "c", add missing "d")
3318        let struct_fields = Fields::from(vec![
3319            Field::new("a", DataType::Int32, true),
3320            Field::new("b", DataType::Int32, true),
3321            Field::new("d", DataType::Int32, true),
3322        ]);
3323        let struct_type = DataType::Struct(struct_fields);
3324
3325        let options = GetOptions {
3326            path: VariantPath::default(),
3327            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
3328            cast_options: CastOptions::default(),
3329        };
3330
3331        let result = variant_get(&variant_array, options).unwrap();
3332
3333        // Verify the result is a StructArray with 3 fields and 5 rows
3334        let struct_result = result.as_any().downcast_ref::<StructArray>().unwrap();
3335        assert_eq!(struct_result.len(), 5);
3336        assert_eq!(struct_result.num_columns(), 3);
3337
3338        let field_a = struct_result
3339            .column(0)
3340            .as_any()
3341            .downcast_ref::<Int32Array>()
3342            .unwrap();
3343        let field_b = struct_result
3344            .column(1)
3345            .as_any()
3346            .downcast_ref::<Int32Array>()
3347            .unwrap();
3348        let field_d = struct_result
3349            .column(2)
3350            .as_any()
3351            .downcast_ref::<Int32Array>()
3352            .unwrap();
3353
3354        // Row 0: Normal values {"a": 1, "b": 2, "c": 3} → {a: 1, b: 2, d: NULL}
3355        assert!(!struct_result.is_null(0));
3356        assert_eq!(field_a.value(0), 1);
3357        assert_eq!(field_b.value(0), 2);
3358        assert!(field_d.is_null(0)); // Missing field "d"
3359
3360        // Row 1: Top-level NULL → struct-level NULL
3361        assert!(struct_result.is_null(1));
3362
3363        // Row 2: Field "a" missing → {a: NULL, b: 2, d: NULL}
3364        assert!(!struct_result.is_null(2));
3365        assert!(field_a.is_null(2)); // Missing field "a"
3366        assert_eq!(field_b.value(2), 2);
3367        assert!(field_d.is_null(2)); // Missing field "d"
3368
3369        // Row 3: Field "b" missing → {a: 1, b: NULL, d: NULL}
3370        assert!(!struct_result.is_null(3));
3371        assert_eq!(field_a.value(3), 1);
3372        assert!(field_b.is_null(3)); // Missing field "b"
3373        assert!(field_d.is_null(3)); // Missing field "d"
3374
3375        // Row 4: Empty object {} → {a: NULL, b: NULL, d: NULL}
3376        assert!(!struct_result.is_null(4));
3377        assert!(field_a.is_null(4)); // Empty object
3378        assert!(field_b.is_null(4)); // Empty object
3379        assert!(field_d.is_null(4)); // Missing field "d"
3380    }
3381
3382    #[test]
3383    fn test_nested_struct_extraction_perfectly_shredded() {
3384        // Create nested variant with diverse null patterns
3385        let variant_array = create_comprehensive_nested_shredded_variant();
3386        println!("variant_array: {variant_array:?}");
3387
3388        // Request 3-level nested struct type {"outer": {"inner": INT}}
3389        let inner_field = Field::new("inner", DataType::Int32, true);
3390        let inner_type = DataType::Struct(Fields::from(vec![inner_field]));
3391        let outer_field = Field::new("outer", inner_type, true);
3392        let result_type = DataType::Struct(Fields::from(vec![outer_field]));
3393
3394        let options = GetOptions {
3395            path: VariantPath::default(),
3396            as_type: Some(Arc::new(Field::new("result", result_type, true))),
3397            cast_options: CastOptions::default(),
3398        };
3399
3400        let result = variant_get(&variant_array, options).unwrap();
3401        println!("result: {result:?}");
3402
3403        // Verify the result is a StructArray with "outer" field and 4 rows
3404        let outer_struct = result.as_any().downcast_ref::<StructArray>().unwrap();
3405        assert_eq!(outer_struct.len(), 4);
3406        assert_eq!(outer_struct.num_columns(), 1);
3407
3408        // Get the "inner" struct column
3409        let inner_struct = outer_struct
3410            .column(0)
3411            .as_any()
3412            .downcast_ref::<StructArray>()
3413            .unwrap();
3414        assert_eq!(inner_struct.num_columns(), 1);
3415
3416        // Get the "leaf" field (Int32 values)
3417        let leaf_field = inner_struct
3418            .column(0)
3419            .as_any()
3420            .downcast_ref::<Int32Array>()
3421            .unwrap();
3422
3423        // Row 0: Normal nested {"outer": {"inner": {"leaf": 42}}}
3424        assert!(!outer_struct.is_null(0));
3425        assert!(!inner_struct.is_null(0));
3426        assert_eq!(leaf_field.value(0), 42);
3427
3428        // Row 1: "inner" field missing → {outer: {inner: NULL}}
3429        assert!(!outer_struct.is_null(1));
3430        assert!(!inner_struct.is_null(1)); // outer exists, inner exists but leaf is NULL
3431        assert!(leaf_field.is_null(1)); // leaf field is NULL
3432
3433        // Row 2: "outer" field missing → {outer: NULL}
3434        assert!(!outer_struct.is_null(2));
3435        assert!(inner_struct.is_null(2)); // outer field is NULL
3436
3437        // Row 3: Top-level NULL → struct-level NULL
3438        assert!(outer_struct.is_null(3));
3439    }
3440
3441    #[test]
3442    fn test_path_based_null_masks_one_step() {
3443        // Create nested variant with diverse null patterns
3444        let variant_array = create_comprehensive_nested_shredded_variant();
3445
3446        // Extract "outer" field using path-based variant_get
3447        let path = VariantPath::try_from("outer").unwrap();
3448        let inner_field = Field::new("inner", DataType::Int32, true);
3449        let result_type = DataType::Struct(Fields::from(vec![inner_field]));
3450
3451        let options = GetOptions {
3452            path,
3453            as_type: Some(Arc::new(Field::new("result", result_type, true))),
3454            cast_options: CastOptions::default(),
3455        };
3456
3457        let result = variant_get(&variant_array, options).unwrap();
3458
3459        // Verify the result is a StructArray with "inner" field and 4 rows
3460        let outer_result = result.as_any().downcast_ref::<StructArray>().unwrap();
3461        assert_eq!(outer_result.len(), 4);
3462        assert_eq!(outer_result.num_columns(), 1);
3463
3464        // Get the "inner" field (Int32 values)
3465        let inner_field = outer_result
3466            .column(0)
3467            .as_any()
3468            .downcast_ref::<Int32Array>()
3469            .unwrap();
3470
3471        // Row 0: Normal nested {"outer": {"inner": 42}} → {"inner": 42}
3472        assert!(!outer_result.is_null(0));
3473        assert_eq!(inner_field.value(0), 42);
3474
3475        // Row 1: Inner field null {"outer": {"inner": null}} → {"inner": null}
3476        assert!(!outer_result.is_null(1));
3477        assert!(inner_field.is_null(1));
3478
3479        // Row 2: Outer field null {"outer": null} → null (entire struct is null)
3480        assert!(outer_result.is_null(2));
3481
3482        // Row 3: Top-level null → null (entire struct is null)
3483        assert!(outer_result.is_null(3));
3484    }
3485
3486    #[test]
3487    fn test_path_based_null_masks_two_steps() {
3488        // Create nested variant with diverse null patterns
3489        let variant_array = create_comprehensive_nested_shredded_variant();
3490
3491        // Extract "outer.inner" field using path-based variant_get
3492        let path = VariantPath::try_from("outer").unwrap().join("inner");
3493
3494        let options = GetOptions {
3495            path,
3496            as_type: Some(Arc::new(Field::new("result", DataType::Int32, true))),
3497            cast_options: CastOptions::default(),
3498        };
3499
3500        let result = variant_get(&variant_array, options).unwrap();
3501
3502        // Verify the result is an Int32Array with 4 rows
3503        let int_result = result.as_any().downcast_ref::<Int32Array>().unwrap();
3504        assert_eq!(int_result.len(), 4);
3505
3506        // Row 0: Normal nested {"outer": {"inner": 42}} → 42
3507        assert!(!int_result.is_null(0));
3508        assert_eq!(int_result.value(0), 42);
3509
3510        // Row 1: Inner field null {"outer": {"inner": null}} → null
3511        assert!(int_result.is_null(1));
3512
3513        // Row 2: Outer field null {"outer": null} → null (path traversal fails)
3514        assert!(int_result.is_null(2));
3515
3516        // Row 3: Top-level null → null (path traversal fails)
3517        assert!(int_result.is_null(3));
3518    }
3519
3520    #[test]
3521    fn test_struct_extraction_mixed_and_unshredded() {
3522        // Create a partially shredded variant (x shredded, y not)
3523        let variant_array = create_mixed_and_unshredded_variant();
3524
3525        // Request struct with both shredded and unshredded fields
3526        let struct_fields = Fields::from(vec![
3527            Field::new("x", DataType::Int32, true),
3528            Field::new("y", DataType::Int32, true),
3529        ]);
3530        let struct_type = DataType::Struct(struct_fields);
3531
3532        let options = GetOptions {
3533            path: VariantPath::default(),
3534            as_type: Some(Arc::new(Field::new("result", struct_type, true))),
3535            cast_options: CastOptions::default(),
3536        };
3537
3538        let result = variant_get(&variant_array, options).unwrap();
3539
3540        // Verify the mixed shredding works (should succeed with current implementation)
3541        let struct_result = result.as_any().downcast_ref::<StructArray>().unwrap();
3542        assert_eq!(struct_result.len(), 4);
3543        assert_eq!(struct_result.num_columns(), 2);
3544
3545        let field_x = struct_result
3546            .column(0)
3547            .as_any()
3548            .downcast_ref::<Int32Array>()
3549            .unwrap();
3550        let field_y = struct_result
3551            .column(1)
3552            .as_any()
3553            .downcast_ref::<Int32Array>()
3554            .unwrap();
3555
3556        // Row 0: {"x": 1, "y": 42} - x from shredded, y from value field
3557        assert_eq!(field_x.value(0), 1);
3558        assert_eq!(field_y.value(0), 42);
3559
3560        // Row 1: {"x": 2} - x from shredded, y missing (perfect shredding)
3561        assert_eq!(field_x.value(1), 2);
3562        assert!(field_y.is_null(1));
3563
3564        // Row 2: {"x": 3, "y": null} - x from shredded, y explicitly null in value
3565        assert_eq!(field_x.value(2), 3);
3566        assert!(field_y.is_null(2));
3567
3568        // Row 3: top-level null - entire struct row should be null
3569        assert!(struct_result.is_null(3));
3570    }
3571
3572    #[test]
3573    fn test_struct_row_builder_handles_unshredded_nested_structs() {
3574        // Create completely unshredded JSON variant (no typed_value at all)
3575        let json_strings = vec![
3576            r#"{"outer": {"inner": 42}}"#,
3577            r#"{"outer": {"inner": 100}}"#,
3578        ];
3579        let string_array: Arc<dyn Array> = Arc::new(StringArray::from(json_strings));
3580        let variant_array = json_to_variant(&string_array).unwrap();
3581
3582        // Request nested struct
3583        let inner_fields = Fields::from(vec![Field::new("inner", DataType::Int32, true)]);
3584        let inner_struct_type = DataType::Struct(inner_fields);
3585        let outer_fields = Fields::from(vec![Field::new("outer", inner_struct_type, true)]);
3586        let outer_struct_type = DataType::Struct(outer_fields);
3587
3588        let options = GetOptions {
3589            path: VariantPath::default(),
3590            as_type: Some(Arc::new(Field::new("result", outer_struct_type, true))),
3591            cast_options: CastOptions::default(),
3592        };
3593
3594        let variant_array_ref = ArrayRef::from(variant_array);
3595        let result = variant_get(&variant_array_ref, options).unwrap();
3596
3597        let outer_struct = result.as_struct();
3598        assert_eq!(outer_struct.len(), 2);
3599        assert_eq!(outer_struct.num_columns(), 1);
3600
3601        let inner_struct = outer_struct.column(0).as_struct();
3602        assert_eq!(inner_struct.num_columns(), 1);
3603
3604        let inner_values = inner_struct
3605            .column(0)
3606            .as_any()
3607            .downcast_ref::<Int32Array>()
3608            .unwrap();
3609        assert_eq!(inner_values.value(0), 42);
3610        assert_eq!(inner_values.value(1), 100);
3611    }
3612
3613    #[test]
3614    fn test_unshredded_struct_safe_cast_and_field_mismatches() {
3615        let json_strings = vec![r#"{"a": 1, "b": 2, "extra": 3}"#, "123", "{}"];
3616        let string_array: Arc<dyn Array> = Arc::new(StringArray::from(json_strings));
3617        let variant_array_ref = ArrayRef::from(json_to_variant(&string_array).unwrap());
3618
3619        let struct_fields = Fields::from(vec![
3620            Field::new("a", DataType::Int32, true),
3621            Field::new("b", DataType::Int32, true),
3622        ]);
3623        let options = GetOptions {
3624            path: VariantPath::default(),
3625            as_type: Some(Arc::new(Field::new(
3626                "result",
3627                DataType::Struct(struct_fields),
3628                true,
3629            ))),
3630            cast_options: CastOptions::default(),
3631        };
3632
3633        let result = variant_get(&variant_array_ref, options).unwrap();
3634        let struct_result = result.as_struct();
3635        let field_a = struct_result
3636            .column(0)
3637            .as_primitive::<arrow::datatypes::Int32Type>();
3638        let field_b = struct_result
3639            .column(1)
3640            .as_primitive::<arrow::datatypes::Int32Type>();
3641
3642        // Row 0 is an object, so the struct row is valid with extracted fields. Object fields
3643        // that aren't present in the requested struct are ignored.
3644        assert!(!struct_result.is_null(0));
3645        assert_eq!(field_a.value(0), 1);
3646        assert_eq!(field_b.value(0), 2);
3647
3648        // Row 1 is a scalar, so safe struct cast should produce a NULL struct row.
3649        assert!(struct_result.is_null(1));
3650        assert!(field_a.is_null(1));
3651        assert!(field_b.is_null(1));
3652
3653        // Row 2 is an empty object, so the struct row is valid with missing fields as NULL.
3654        assert!(!struct_result.is_null(2));
3655        assert!(field_a.is_null(2));
3656        assert!(field_b.is_null(2));
3657    }
3658
3659    #[test]
3660    fn test_unshredded_struct_missing_non_nullable_field_errors() {
3661        let string_array: Arc<dyn Array> = Arc::new(StringArray::from(vec![r#"{"a": 1}"#]));
3662        let variant_array_ref = ArrayRef::from(json_to_variant(&string_array).unwrap());
3663
3664        let struct_fields = Fields::from(vec![
3665            Field::new("a", DataType::Int32, false),
3666            Field::new("missing", DataType::Int32, false),
3667        ]);
3668        let options = GetOptions {
3669            path: VariantPath::default(),
3670            as_type: Some(Arc::new(Field::new(
3671                "result",
3672                DataType::Struct(struct_fields),
3673                true,
3674            ))),
3675            cast_options: CastOptions::default(),
3676        };
3677
3678        let err = variant_get(&variant_array_ref, options).unwrap_err();
3679        assert!(
3680            err.to_string()
3681                .contains("unmasked nulls for non-nullable StructArray field \"missing\""),
3682            "unexpected error: {err}"
3683        );
3684    }
3685
3686    #[test]
3687    fn test_unshredded_struct_strict_cast_non_object_errors() {
3688        let json_strings = vec![r#"{"a": 1, "b": 2}"#, "123"];
3689        let string_array: Arc<dyn Array> = Arc::new(StringArray::from(json_strings));
3690        let variant_array_ref = ArrayRef::from(json_to_variant(&string_array).unwrap());
3691
3692        let struct_fields = Fields::from(vec![
3693            Field::new("a", DataType::Int32, true),
3694            Field::new("b", DataType::Int32, true),
3695        ]);
3696        let options = GetOptions {
3697            path: VariantPath::default(),
3698            as_type: Some(Arc::new(Field::new(
3699                "result",
3700                DataType::Struct(struct_fields),
3701                true,
3702            ))),
3703            cast_options: CastOptions {
3704                safe: false,
3705                ..Default::default()
3706            },
3707        };
3708
3709        let err = variant_get(&variant_array_ref, options).unwrap_err();
3710        assert!(
3711            err.to_string()
3712                .contains("Failed to extract struct from variant")
3713        );
3714    }
3715
3716    /// Create comprehensive shredded variant with diverse null patterns and empty objects
3717    /// Rows: normal values, top-level null, missing field a, missing field b, empty object
3718    fn create_comprehensive_shredded_variant() -> ArrayRef {
3719        let (metadata, _) = {
3720            let mut builder = parquet_variant::VariantBuilder::new();
3721            let obj = builder.new_object();
3722            obj.finish();
3723            builder.finish()
3724        };
3725
3726        // Create null buffer for top-level nulls
3727        let nulls = NullBuffer::from(vec![
3728            true,  // row 0: normal values
3729            false, // row 1: top-level null
3730            true,  // row 2: missing field a
3731            true,  // row 3: missing field b
3732            true,  // row 4: empty object
3733        ]);
3734
3735        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 5));
3736
3737        // Create shredded fields with different null patterns
3738        // Field "a": present in rows 0,3 (missing in rows 1,2,4)
3739        let a_field_typed_value = Int32Array::from(vec![Some(1), None, None, Some(1), None]);
3740        let a_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
3741            a_field_typed_value,
3742        ) as ArrayRef);
3743
3744        // Field "b": present in rows 0,2 (missing in rows 1,3,4)
3745        let b_field_typed_value = Int32Array::from(vec![Some(2), None, Some(2), None, None]);
3746        let b_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
3747            b_field_typed_value,
3748        ) as ArrayRef);
3749
3750        // Field "c": present in row 0 only (missing in all other rows)
3751        let c_field_typed_value = Int32Array::from(vec![Some(3), None, None, None, None]);
3752        let c_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
3753            c_field_typed_value,
3754        ) as ArrayRef);
3755
3756        // Create main typed_value struct
3757        let typed_value_fields = Fields::from(vec![
3758            Field::new("a", a_field_shredded.data_type().clone(), true),
3759            Field::new("b", b_field_shredded.data_type().clone(), true),
3760            Field::new("c", c_field_shredded.data_type().clone(), true),
3761        ]);
3762        let typed_value_struct = StructArray::try_new(
3763            typed_value_fields,
3764            vec![
3765                ArrayRef::from(a_field_shredded),
3766                ArrayRef::from(b_field_shredded),
3767                ArrayRef::from(c_field_shredded),
3768            ],
3769            None,
3770        )
3771        .unwrap();
3772
3773        // Build final VariantArray with top-level nulls
3774        ArrayRef::from(VariantArray::perfectly_shredded(
3775            Arc::new(metadata_array),
3776            Arc::new(typed_value_struct),
3777            Some(nulls),
3778        ))
3779    }
3780
3781    /// Create comprehensive nested shredded variant with diverse null patterns
3782    /// Represents 3-level structure: variant -> outer -> inner (INT value)
3783    /// The shredding schema is: {"metadata": BINARY, "typed_value": {"outer": {"typed_value": {"inner": {"typed_value": INT}}}}}
3784    /// Rows: normal nested value, inner field null, outer field null, top-level null
3785    fn create_comprehensive_nested_shredded_variant() -> ArrayRef {
3786        // Create the inner level: contains typed_value with Int32 values
3787        // Row 0: has value 42, Row 1: inner null, Row 2: outer null, Row 3: top-level null
3788        let inner_typed_value = Int32Array::from(vec![Some(42), None, None, None]); // dummy value for row 2
3789        let inner =
3790            ShreddedVariantFieldArray::perfectly_shredded(Arc::new(inner_typed_value) as ArrayRef);
3791
3792        let outer_typed_value_nulls = NullBuffer::from(vec![
3793            true,  // row 0: inner struct exists with typed_value=42
3794            false, // row 1: inner field NULL
3795            false, // row 2: outer field NULL
3796            false, // row 3: top-level NULL
3797        ]);
3798        let outer_typed_value = StructArrayAssembler::new()
3799            .with_field("inner", ArrayRef::from(inner), false)
3800            .with_nulls(outer_typed_value_nulls)
3801            .build()
3802            .unwrap();
3803
3804        let outer =
3805            ShreddedVariantFieldArray::perfectly_shredded(Arc::new(outer_typed_value) as ArrayRef);
3806
3807        let typed_value_nulls = NullBuffer::from(vec![
3808            true,  // row 0: inner struct exists with typed_value=42
3809            true,  // row 1: inner field NULL
3810            false, // row 2: outer field NULL
3811            false, // row 3: top-level NULL
3812        ]);
3813        let typed_value = StructArrayAssembler::new()
3814            .with_field("outer", ArrayRef::from(outer), false)
3815            .with_nulls(typed_value_nulls)
3816            .build()
3817            .unwrap();
3818
3819        // Build final VariantArray with top-level nulls
3820        let metadata_array =
3821            BinaryViewArray::from_iter_values(std::iter::repeat_n(EMPTY_VARIANT_METADATA_BYTES, 4));
3822        let nulls = NullBuffer::from(vec![
3823            true,  // row 0: inner struct exists with typed_value=42
3824            true,  // row 1: inner field NULL
3825            true,  // row 2: outer field NULL
3826            false, // row 3: top-level NULL
3827        ]);
3828        ArrayRef::from(VariantArray::perfectly_shredded(
3829            Arc::new(metadata_array),
3830            Arc::new(typed_value),
3831            Some(nulls),
3832        ))
3833    }
3834
3835    /// Create variant with mixed shredding (spec-compliant) including null scenarios
3836    /// Field "x" is globally shredded, field "y" is never shredded
3837    fn create_mixed_and_unshredded_variant() -> ArrayRef {
3838        // Create spec-compliant mixed shredding:
3839        // - Field "x" is globally shredded (has typed_value column)
3840        // - Field "y" is never shredded (only appears in value field when present)
3841
3842        let (metadata, y_field_value) = {
3843            let mut builder = parquet_variant::VariantBuilder::new();
3844            let mut obj = builder.new_object();
3845            obj.insert("y", Variant::from(42));
3846            obj.finish();
3847            builder.finish()
3848        };
3849
3850        let metadata_array = BinaryViewArray::from_iter_values(std::iter::repeat_n(&metadata, 4));
3851
3852        // Value field contains objects with unshredded fields only (never contains "x")
3853        // Row 0: {"y": "foo"} - x is shredded out, y remains in value
3854        // Row 1: {} - both x and y are absent (perfect shredding for x, y missing)
3855        // Row 2: {"y": null} - x is shredded out, y explicitly null
3856        // Row 3: top-level null (encoded in VariantArray's null mask, but fields contain valid data)
3857
3858        let empty_object_value = {
3859            let mut builder = parquet_variant::VariantBuilder::new();
3860            builder.new_object().finish();
3861            let (_, value) = builder.finish();
3862            value
3863        };
3864
3865        let y_null_value = {
3866            let mut builder = parquet_variant::VariantBuilder::new();
3867            builder.new_object().with_field("y", Variant::Null).finish();
3868            let (_, value) = builder.finish();
3869            value
3870        };
3871
3872        let value_array = BinaryViewArray::from(vec![
3873            Some(y_field_value.as_slice()),      // Row 0: {"y": 42}
3874            Some(empty_object_value.as_slice()), // Row 1: {}
3875            Some(y_null_value.as_slice()),       // Row 2: {"y": null}
3876            Some(empty_object_value.as_slice()), // Row 3: top-level null (but value field contains valid data)
3877        ]);
3878
3879        // Create shredded field "x" (globally shredded - never appears in value field)
3880        // For top-level null row, the field still needs valid content (not null)
3881        let x_field_typed_value = Int32Array::from(vec![Some(1), Some(2), Some(3), Some(0)]);
3882        let x_field_shredded = ShreddedVariantFieldArray::perfectly_shredded(Arc::new(
3883            x_field_typed_value,
3884        ) as ArrayRef);
3885
3886        // Create main typed_value struct (only contains shredded fields)
3887        let typed_value_struct = StructArrayAssembler::new()
3888            .with_field("x", ArrayRef::from(x_field_shredded), false)
3889            .build()
3890            .unwrap();
3891
3892        // Build VariantArray with both value and typed_value (PartiallyShredded)
3893        // Top-level null is encoded in the main StructArray's null mask
3894        let variant_nulls = NullBuffer::from(vec![true, true, true, false]); // Row 3 is top-level null
3895        ArrayRef::from(VariantArray::from_parts(
3896            Arc::new(metadata_array),
3897            Arc::new(value_array),
3898            Some(Arc::new(typed_value_struct)),
3899            Some(variant_nulls),
3900        ))
3901    }
3902
3903    #[test]
3904    fn get_decimal32_rescaled_to_scale2() {
3905        // Build unshredded variant values with different scales
3906        let mut builder = crate::VariantArrayBuilder::new(5);
3907        builder.append_variant(VariantDecimal4::try_new(1234, 2).unwrap().into()); // 12.34
3908        builder.append_variant(VariantDecimal4::try_new(1234, 3).unwrap().into()); // 1.234
3909        builder.append_variant(VariantDecimal4::try_new(1234, 0).unwrap().into()); // 1234
3910        builder.append_null();
3911        builder.append_variant(
3912            VariantDecimal8::try_new((VariantDecimal4::MAX_UNSCALED_VALUE as i64) + 1, 3)
3913                .unwrap()
3914                .into(),
3915        ); // should fit into Decimal32
3916        let variant_array: ArrayRef = ArrayRef::from(builder.build());
3917
3918        let field = Field::new("result", DataType::Decimal32(9, 2), true);
3919        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3920        let result = variant_get(&variant_array, options).unwrap();
3921        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3922
3923        assert_eq!(result.precision(), 9);
3924        assert_eq!(result.scale(), 2);
3925        assert_eq!(result.value(0), 1234);
3926        assert_eq!(result.value(1), 123);
3927        assert_eq!(result.value(2), 123400);
3928        assert!(result.is_null(3));
3929        assert_eq!(
3930            result.value(4),
3931            VariantDecimal4::MAX_UNSCALED_VALUE / 10 + 1
3932        ); // should not be null as the final result fits into Decimal32
3933    }
3934
3935    #[test]
3936    fn get_decimal32_scale_down_rounding() {
3937        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
3938            Some(VariantDecimal4::try_new(1235, 0).unwrap()),
3939            Some(VariantDecimal4::try_new(1245, 0).unwrap()),
3940            Some(VariantDecimal4::try_new(-1235, 0).unwrap()),
3941            Some(VariantDecimal4::try_new(-1245, 0).unwrap()),
3942            Some(VariantDecimal4::try_new(1235, 2).unwrap()), // 12.35 rounded down to 10 for scale -1
3943            Some(VariantDecimal4::try_new(1235, 3).unwrap()), // 1.235 rounded down to 0 for scale -1
3944            Some(VariantDecimal4::try_new(5235, 3).unwrap()), // 5.235 rounded up to 10 for scale -1
3945        ]));
3946
3947        let field = Field::new("result", DataType::Decimal32(9, -1), true);
3948        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3949        let result = variant_get(&variant_array, options).unwrap();
3950        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3951
3952        assert_eq!(result.precision(), 9);
3953        assert_eq!(result.scale(), -1);
3954        assert_eq!(result.value(0), 124);
3955        assert_eq!(result.value(1), 125);
3956        assert_eq!(result.value(2), -124);
3957        assert_eq!(result.value(3), -125);
3958        assert_eq!(result.value(4), 1);
3959        assert!(result.is_valid(5));
3960        assert_eq!(result.value(5), 0);
3961        assert_eq!(result.value(6), 1);
3962    }
3963
3964    #[test]
3965    fn get_decimal32_large_scale_reduction() {
3966        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
3967            Some(VariantDecimal4::try_new(-VariantDecimal4::MAX_UNSCALED_VALUE, 0).unwrap()),
3968            Some(VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0).unwrap()),
3969        ]));
3970
3971        let field = Field::new("result", DataType::Decimal32(9, -9), true);
3972        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3973        let result = variant_get(&variant_array, options).unwrap();
3974        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3975
3976        assert_eq!(result.precision(), 9);
3977        assert_eq!(result.scale(), -9);
3978        assert_eq!(result.value(0), -1);
3979        assert_eq!(result.value(1), 1);
3980
3981        let field = Field::new("result", DataType::Decimal32(9, -10), true);
3982        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
3983        let result = variant_get(&variant_array, options).unwrap();
3984        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
3985
3986        assert_eq!(result.precision(), 9);
3987        assert_eq!(result.scale(), -10);
3988        assert!(result.is_valid(0));
3989        assert_eq!(result.value(0), 0);
3990        assert!(result.is_valid(1));
3991        assert_eq!(result.value(1), 0);
3992    }
3993
3994    #[test]
3995    fn get_decimal32_precision_overflow_safe() {
3996        // Exceed Decimal32 after scaling and rounding
3997        let variant_array = ArrayRef::from(VariantArray::from_iter([
3998            Some(Variant::from(
3999                VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0).unwrap(),
4000            )),
4001            // integer value round up overflows
4002            Some(Variant::from(
4003                VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 9).unwrap(),
4004            )),
4005            // Float inputs that exceed precision directly or after rounding to 1.00.
4006            Some(Variant::Float(1.1)),
4007            Some(Variant::Double(1.1)),
4008            Some(Variant::Float(0.999)),
4009            Some(Variant::Double(0.999)),
4010        ]));
4011
4012        let field = Field::new("result", DataType::Decimal32(2, 2), true);
4013        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4014        let result = variant_get(&variant_array, options).unwrap();
4015        let result = result.as_any().downcast_ref::<Decimal32Array>().unwrap();
4016
4017        assert_eq!(result.null_count(), 6); // Even 1.00 exceeds precision 2.
4018    }
4019
4020    #[test]
4021    fn get_decimal32_precision_overflow_unsafe_errors() {
4022        for (value, kind) in [
4023            (
4024                VariantDecimal4::try_new(VariantDecimal4::MAX_UNSCALED_VALUE, 0)
4025                    .unwrap()
4026                    .into(),
4027                "Decimal4",
4028            ),
4029            (Variant::Float(1.1), "Float"),
4030            (Variant::Double(1.1), "Double"),
4031            (Variant::Float(0.999), "Float"),
4032            (Variant::Double(0.999), "Double"),
4033        ] {
4034            let variant_array = ArrayRef::from(VariantArray::from_iter([Some(value)]));
4035            let field = Field::new("result", DataType::Decimal32(2, 2), true);
4036            let options = GetOptions::new()
4037                .with_as_type(Some(FieldRef::from(field)))
4038                .with_cast_options(CastOptions {
4039                    safe: false,
4040                    ..Default::default()
4041                });
4042            let err = variant_get(&variant_array, options).unwrap_err();
4043            assert!(err.to_string().contains(&format!(
4044                "Failed to cast to Decimal32(precision=2, scale=2) from variant {kind}"
4045            )));
4046        }
4047    }
4048
4049    #[test]
4050    fn get_decimal64_rescaled_to_scale2() {
4051        let mut builder = crate::VariantArrayBuilder::new(5);
4052        builder.append_variant(VariantDecimal8::try_new(1234, 2).unwrap().into()); // 12.34
4053        builder.append_variant(VariantDecimal8::try_new(1234, 3).unwrap().into()); // 1.234
4054        builder.append_variant(VariantDecimal8::try_new(1234, 0).unwrap().into()); // 1234
4055        builder.append_null();
4056        builder.append_variant(
4057            VariantDecimal16::try_new((VariantDecimal8::MAX_UNSCALED_VALUE as i128) + 1, 3)
4058                .unwrap()
4059                .into(),
4060        ); // should fit into Decimal64
4061        let variant_array: ArrayRef = ArrayRef::from(builder.build());
4062
4063        let field = Field::new("result", DataType::Decimal64(18, 2), true);
4064        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4065        let result = variant_get(&variant_array, options).unwrap();
4066        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
4067
4068        assert_eq!(result.precision(), 18);
4069        assert_eq!(result.scale(), 2);
4070        assert_eq!(result.value(0), 1234);
4071        assert_eq!(result.value(1), 123);
4072        assert_eq!(result.value(2), 123400);
4073        assert!(result.is_null(3));
4074        assert_eq!(
4075            result.value(4),
4076            VariantDecimal8::MAX_UNSCALED_VALUE / 10 + 1
4077        ); // should not be null as the final result fits into Decimal64
4078    }
4079
4080    #[test]
4081    fn get_decimal64_scale_down_rounding() {
4082        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4083            Some(VariantDecimal8::try_new(1235, 0).unwrap()),
4084            Some(VariantDecimal8::try_new(1245, 0).unwrap()),
4085            Some(VariantDecimal8::try_new(-1235, 0).unwrap()),
4086            Some(VariantDecimal8::try_new(-1245, 0).unwrap()),
4087            Some(VariantDecimal8::try_new(1235, 2).unwrap()), // 12.35 rounded down to 10 for scale -1
4088            Some(VariantDecimal8::try_new(1235, 3).unwrap()), // 1.235 rounded down to 0 for scale -1
4089            Some(VariantDecimal8::try_new(5235, 3).unwrap()), // 5.235 rounded up to 10 for scale -1
4090        ]));
4091
4092        let field = Field::new("result", DataType::Decimal64(18, -1), true);
4093        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4094        let result = variant_get(&variant_array, options).unwrap();
4095        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
4096
4097        assert_eq!(result.precision(), 18);
4098        assert_eq!(result.scale(), -1);
4099        assert_eq!(result.value(0), 124);
4100        assert_eq!(result.value(1), 125);
4101        assert_eq!(result.value(2), -124);
4102        assert_eq!(result.value(3), -125);
4103        assert_eq!(result.value(4), 1);
4104        assert!(result.is_valid(5));
4105        assert_eq!(result.value(5), 0);
4106        assert_eq!(result.value(6), 1);
4107    }
4108
4109    #[test]
4110    fn get_decimal64_large_scale_reduction() {
4111        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4112            Some(VariantDecimal8::try_new(-VariantDecimal8::MAX_UNSCALED_VALUE, 0).unwrap()),
4113            Some(VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0).unwrap()),
4114        ]));
4115
4116        let field = Field::new("result", DataType::Decimal64(18, -18), true);
4117        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4118        let result = variant_get(&variant_array, options).unwrap();
4119        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
4120
4121        assert_eq!(result.precision(), 18);
4122        assert_eq!(result.scale(), -18);
4123        assert_eq!(result.value(0), -1);
4124        assert_eq!(result.value(1), 1);
4125
4126        let field = Field::new("result", DataType::Decimal64(18, -19), true);
4127        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4128        let result = variant_get(&variant_array, options).unwrap();
4129        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
4130
4131        assert_eq!(result.precision(), 18);
4132        assert_eq!(result.scale(), -19);
4133        assert!(result.is_valid(0));
4134        assert_eq!(result.value(0), 0);
4135        assert!(result.is_valid(1));
4136        assert_eq!(result.value(1), 0);
4137    }
4138
4139    #[test]
4140    fn get_decimal64_precision_overflow_safe() {
4141        // Exceed Decimal64 after scaling and rounding
4142        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4143            Some(VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0).unwrap()),
4144            // integer value round up overflows
4145            Some(VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 18).unwrap()),
4146        ]));
4147
4148        let field = Field::new("result", DataType::Decimal64(2, 2), true);
4149        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4150        let result = variant_get(&variant_array, options).unwrap();
4151        let result = result.as_any().downcast_ref::<Decimal64Array>().unwrap();
4152
4153        assert!(result.is_null(0));
4154        assert!(result.is_null(1));
4155    }
4156
4157    #[test]
4158    fn get_decimal64_precision_overflow_unsafe_errors() {
4159        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([Some(
4160            VariantDecimal8::try_new(VariantDecimal8::MAX_UNSCALED_VALUE, 0).unwrap(),
4161        )]));
4162
4163        let field = Field::new("result", DataType::Decimal64(18, 2), true);
4164        let cast_options = CastOptions {
4165            safe: false,
4166            ..Default::default()
4167        };
4168        let options = GetOptions::new()
4169            .with_as_type(Some(FieldRef::from(field)))
4170            .with_cast_options(cast_options);
4171        let err = variant_get(&variant_array, options).unwrap_err();
4172
4173        assert!(
4174            err.to_string().contains(
4175                "Failed to cast to Decimal64(precision=18, scale=2) from variant Decimal8"
4176            )
4177        );
4178    }
4179
4180    #[test]
4181    fn get_decimal128_rescaled_to_scale2() {
4182        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4183            Some(VariantDecimal16::try_new(1234, 2).unwrap()),
4184            Some(VariantDecimal16::try_new(1234, 3).unwrap()),
4185            Some(VariantDecimal16::try_new(1234, 0).unwrap()),
4186            None,
4187        ]));
4188
4189        let field = Field::new("result", DataType::Decimal128(38, 2), true);
4190        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4191        let result = variant_get(&variant_array, options).unwrap();
4192        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
4193
4194        assert_eq!(result.precision(), 38);
4195        assert_eq!(result.scale(), 2);
4196        assert_eq!(result.value(0), 1234);
4197        assert_eq!(result.value(1), 123);
4198        assert_eq!(result.value(2), 123400);
4199        assert!(result.is_null(3));
4200    }
4201
4202    #[test]
4203    fn get_decimal128_scale_down_rounding() {
4204        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4205            Some(VariantDecimal16::try_new(1235, 0).unwrap()),
4206            Some(VariantDecimal16::try_new(1245, 0).unwrap()),
4207            Some(VariantDecimal16::try_new(-1235, 0).unwrap()),
4208            Some(VariantDecimal16::try_new(-1245, 0).unwrap()),
4209            Some(VariantDecimal16::try_new(1235, 2).unwrap()), // 12.35 rounded down to 10 for scale -1
4210            Some(VariantDecimal16::try_new(1235, 3).unwrap()), // 1.235 rounded down to 0 for scale -1
4211            Some(VariantDecimal16::try_new(5235, 3).unwrap()), // 5.235 rounded up to 10 for scale -1
4212        ]));
4213
4214        let field = Field::new("result", DataType::Decimal128(38, -1), true);
4215        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4216        let result = variant_get(&variant_array, options).unwrap();
4217        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
4218
4219        assert_eq!(result.precision(), 38);
4220        assert_eq!(result.scale(), -1);
4221        assert_eq!(result.value(0), 124);
4222        assert_eq!(result.value(1), 125);
4223        assert_eq!(result.value(2), -124);
4224        assert_eq!(result.value(3), -125);
4225        assert_eq!(result.value(4), 1);
4226        assert!(result.is_valid(5));
4227        assert_eq!(result.value(5), 0);
4228        assert_eq!(result.value(6), 1);
4229    }
4230
4231    #[test]
4232    fn get_decimal128_precision_overflow_safe() {
4233        // Exceed Decimal128 after scaling and rounding
4234        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4235            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0).unwrap()),
4236            // integer value round up overflows
4237            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 38).unwrap()),
4238        ]));
4239
4240        let field = Field::new("result", DataType::Decimal128(2, 2), true);
4241        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4242        let result = variant_get(&variant_array, options).unwrap();
4243        let result = result.as_any().downcast_ref::<Decimal128Array>().unwrap();
4244
4245        assert!(result.is_null(0));
4246        assert!(result.is_null(1)); // should overflow because 1.00 does not fit into precision (2)
4247    }
4248
4249    #[test]
4250    fn get_decimal128_precision_overflow_unsafe_errors() {
4251        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([Some(
4252            VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0).unwrap(),
4253        )]));
4254
4255        let field = Field::new("result", DataType::Decimal128(38, 2), true);
4256        let cast_options = CastOptions {
4257            safe: false,
4258            ..Default::default()
4259        };
4260        let options = GetOptions::new()
4261            .with_as_type(Some(FieldRef::from(field)))
4262            .with_cast_options(cast_options);
4263        let err = variant_get(&variant_array, options).unwrap_err();
4264
4265        assert!(err.to_string().contains(
4266            "Failed to cast to Decimal128(precision=38, scale=2) from variant Decimal16"
4267        ));
4268    }
4269
4270    #[test]
4271    fn get_decimal256_rescaled_to_scale2() {
4272        // Build unshredded variant values with different scales using Decimal16 source
4273        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4274            Some(VariantDecimal16::try_new(1234, 2).unwrap()), // 12.34
4275            Some(VariantDecimal16::try_new(1234, 3).unwrap()), // 1.234
4276            Some(VariantDecimal16::try_new(1234, 0).unwrap()), // 1234
4277            None,
4278        ]));
4279
4280        let field = Field::new("result", DataType::Decimal256(76, 2), true);
4281        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4282        let result = variant_get(&variant_array, options).unwrap();
4283        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
4284
4285        assert_eq!(result.precision(), 76);
4286        assert_eq!(result.scale(), 2);
4287        assert_eq!(result.value(0), i256::from_i128(1234));
4288        assert_eq!(result.value(1), i256::from_i128(123));
4289        assert_eq!(result.value(2), i256::from_i128(123400));
4290        assert!(result.is_null(3));
4291    }
4292
4293    #[test]
4294    fn get_decimal256_scale_down_rounding() {
4295        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4296            Some(VariantDecimal16::try_new(1235, 0).unwrap()),
4297            Some(VariantDecimal16::try_new(1245, 0).unwrap()),
4298            Some(VariantDecimal16::try_new(-1235, 0).unwrap()),
4299            Some(VariantDecimal16::try_new(-1245, 0).unwrap()),
4300            Some(VariantDecimal16::try_new(1235, 2).unwrap()), // 12.35 rounded down to 10 for scale -1
4301            Some(VariantDecimal16::try_new(1235, 3).unwrap()), // 1.235 rounded down to 0 for scale -1
4302            Some(VariantDecimal16::try_new(5235, 3).unwrap()), // 5.235 rounded up to 10 for scale -1
4303        ]));
4304
4305        let field = Field::new("result", DataType::Decimal256(76, -1), true);
4306        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4307        let result = variant_get(&variant_array, options).unwrap();
4308        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
4309
4310        assert_eq!(result.precision(), 76);
4311        assert_eq!(result.scale(), -1);
4312        assert_eq!(result.value(0), i256::from_i128(124));
4313        assert_eq!(result.value(1), i256::from_i128(125));
4314        assert_eq!(result.value(2), i256::from_i128(-124));
4315        assert_eq!(result.value(3), i256::from_i128(-125));
4316        assert_eq!(result.value(4), i256::from_i128(1));
4317        assert!(result.is_valid(5));
4318        assert_eq!(result.value(5), i256::from_i128(0));
4319        assert_eq!(result.value(6), i256::from_i128(1));
4320    }
4321
4322    #[test]
4323    fn get_decimal256_precision_overflow_safe() {
4324        // Exceed Decimal128 max precision (38) after scaling
4325        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4326            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 1).unwrap()),
4327            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0).unwrap()),
4328        ]));
4329
4330        let field = Field::new("result", DataType::Decimal256(76, 39), true);
4331        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4332        let result = variant_get(&variant_array, options).unwrap();
4333        let result = result.as_any().downcast_ref::<Decimal256Array>().unwrap();
4334
4335        // Input is Decimal16 with integer = 10^38-1 and scale = 1, target scale = 39
4336        // So expected integer is (10^38-1) * 10^(39-1) = (10^38-1) * 10^38
4337        let base = i256::from_i128(10);
4338        let factor = base.checked_pow(38).unwrap();
4339        let expected = i256::from_i128(VariantDecimal16::MAX_UNSCALED_VALUE)
4340            .checked_mul(factor)
4341            .unwrap();
4342        assert_eq!(result.value(0), expected);
4343        assert!(result.is_null(1));
4344    }
4345
4346    #[test]
4347    fn get_decimal256_precision_overflow_unsafe_errors() {
4348        // Exceed Decimal128 max precision (38) after scaling
4349        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4350            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 1).unwrap()),
4351            Some(VariantDecimal16::try_new(VariantDecimal16::MAX_UNSCALED_VALUE, 0).unwrap()),
4352        ]));
4353
4354        let field = Field::new("result", DataType::Decimal256(76, 39), true);
4355        let cast_options = CastOptions {
4356            safe: false,
4357            ..Default::default()
4358        };
4359        let options = GetOptions::new()
4360            .with_as_type(Some(FieldRef::from(field)))
4361            .with_cast_options(cast_options);
4362        let err = variant_get(&variant_array, options).unwrap_err();
4363
4364        assert!(err.to_string().contains(
4365            "Failed to cast to Decimal256(precision=76, scale=39) from variant Decimal16"
4366        ));
4367    }
4368
4369    #[test]
4370    fn get_non_supported_temporal_types_error() {
4371        let values = vec![None, Some(Variant::Null), Some(Variant::BooleanFalse)];
4372        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter(values));
4373
4374        let test_cases = vec![
4375            FieldRef::from(Field::new(
4376                "result",
4377                DataType::Duration(TimeUnit::Microsecond),
4378                true,
4379            )),
4380            FieldRef::from(Field::new(
4381                "result",
4382                DataType::Interval(IntervalUnit::YearMonth),
4383                true,
4384            )),
4385        ];
4386
4387        for field in test_cases {
4388            let options = GetOptions::new().with_as_type(Some(field));
4389            let err = variant_get(&variant_array, options).unwrap_err();
4390            assert!(
4391                err.to_string()
4392                    .contains("Casting Variant to duration/interval types is not supported")
4393            );
4394        }
4395    }
4396
4397    #[test]
4398    fn get_variant_as_dictionary() {
4399        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4400            Some("apple"),
4401            Some("banana"),
4402            None,
4403            Some("apple"),
4404        ]));
4405        let data_type = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
4406        let options = GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
4407            "dict",
4408            data_type.clone(),
4409            true,
4410        ))));
4411
4412        let result = variant_get(&variant_array, options).unwrap();
4413        assert_eq!(result.data_type(), &data_type);
4414
4415        let decoded = cast(result.as_ref(), &DataType::Utf8).unwrap();
4416        let expected = StringArray::from(vec![Some("apple"), Some("banana"), None, Some("apple")]);
4417        assert_eq!(decoded.as_ref(), &expected);
4418    }
4419
4420    #[test]
4421    fn get_variant_as_numeric_dictionary() {
4422        let variant_array: ArrayRef =
4423            ArrayRef::from(VariantArray::from_iter([Some(42), Some(7), None, Some(42)]));
4424        let data_type = DataType::Dictionary(Box::new(DataType::Int16), Box::new(DataType::Int32));
4425        let options = GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
4426            "dict",
4427            data_type.clone(),
4428            true,
4429        ))));
4430
4431        let result = variant_get(&variant_array, options).unwrap();
4432        assert_eq!(result.data_type(), &data_type);
4433
4434        let decoded = cast(result.as_ref(), &DataType::Int32).unwrap();
4435        let expected = Int32Array::from(vec![Some(42), Some(7), None, Some(42)]);
4436        assert_eq!(decoded.as_ref(), &expected);
4437    }
4438
4439    #[test]
4440    fn get_variant_as_run_end_encoded() {
4441        let variant_array: ArrayRef = ArrayRef::from(VariantArray::from_iter([
4442            Some("apple"),
4443            Some("apple"),
4444            None,
4445            Some("banana"),
4446            Some("banana"),
4447        ]));
4448        let run_ends = Arc::new(Field::new(
4449            Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
4450            DataType::Int32,
4451            false,
4452        ));
4453        let values = Arc::new(Field::new(
4454            Field::REE_VALUES_FIELD_DEFAULT_NAME,
4455            DataType::Utf8,
4456            true,
4457        ));
4458        let data_type = DataType::RunEndEncoded(run_ends, values);
4459        let options = GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
4460            "ree",
4461            data_type.clone(),
4462            true,
4463        ))));
4464
4465        let result = variant_get(&variant_array, options).unwrap();
4466        assert_eq!(result.data_type(), &data_type);
4467
4468        let decoded = cast(result.as_ref(), &DataType::Utf8).unwrap();
4469        let expected = StringArray::from(vec![
4470            Some("apple"),
4471            Some("apple"),
4472            None,
4473            Some("banana"),
4474            Some("banana"),
4475        ]);
4476        assert_eq!(decoded.as_ref(), &expected);
4477    }
4478
4479    /// Map data type with `MapBuilder`'s default field names, so results can be
4480    /// compared against arrays built by `MapBuilder`.
4481    fn map_data_type(value_type: DataType) -> DataType {
4482        DataType::Map(
4483            Arc::new(Field::new(
4484                "entries",
4485                DataType::Struct(Fields::from(vec![
4486                    Field::new("keys", DataType::Utf8, false),
4487                    Field::new("values", value_type, true),
4488                ])),
4489                false,
4490            )),
4491            false,
4492        )
4493    }
4494
4495    fn map_get_options(data_type: &DataType) -> GetOptions<'static> {
4496        GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
4497            "map",
4498            data_type.clone(),
4499            true,
4500        ))))
4501    }
4502
4503    #[test]
4504    fn get_variant_as_map() {
4505        let input: ArrayRef = Arc::new(StringArray::from(vec![
4506            Some(r#"{"a": 1, "b": 2}"#),
4507            Some(r#"{"c": 3}"#),
4508            None,
4509            Some("{}"),
4510            Some(r#"{"d": null}"#),
4511        ]));
4512        let variant_array = ArrayRef::from(json_to_variant(&input).unwrap());
4513
4514        let data_type = map_data_type(DataType::Int64);
4515        let result = variant_get(&variant_array, map_get_options(&data_type)).unwrap();
4516        assert_eq!(result.data_type(), &data_type);
4517
4518        let mut expected = MapBuilder::new(None, StringBuilder::new(), Int64Builder::new());
4519        expected.keys().append_value("a");
4520        expected.values().append_value(1);
4521        expected.keys().append_value("b");
4522        expected.values().append_value(2);
4523        expected.append(true).unwrap();
4524        expected.keys().append_value("c");
4525        expected.values().append_value(3);
4526        expected.append(true).unwrap();
4527        expected.append(false).unwrap(); // null row
4528        expected.append(true).unwrap(); // empty object -> empty map
4529        expected.keys().append_value("d");
4530        expected.values().append_null(); // variant null -> null map value
4531        expected.append(true).unwrap();
4532        let expected = expected.finish();
4533        assert_eq!(result.as_ref(), &expected);
4534    }
4535
4536    #[test]
4537    fn get_variant_as_map_of_lists() {
4538        let input: ArrayRef = Arc::new(StringArray::from(vec![
4539            Some(r#"{"a": [1, 2], "b": []}"#),
4540            Some(r#"{"c": [3]}"#),
4541        ]));
4542        let variant_array = ArrayRef::from(json_to_variant(&input).unwrap());
4543
4544        let data_type = map_data_type(DataType::List(Arc::new(Field::new(
4545            "item",
4546            DataType::Int64,
4547            true,
4548        ))));
4549        let result = variant_get(&variant_array, map_get_options(&data_type)).unwrap();
4550        assert_eq!(result.data_type(), &data_type);
4551
4552        let mut expected = MapBuilder::new(
4553            None,
4554            StringBuilder::new(),
4555            ListBuilder::new(Int64Builder::new()),
4556        );
4557        expected.keys().append_value("a");
4558        expected.values().append_value([Some(1), Some(2)]);
4559        expected.keys().append_value("b");
4560        expected.values().append_value([]);
4561        expected.append(true).unwrap();
4562        expected.keys().append_value("c");
4563        expected.values().append_value([Some(3)]);
4564        expected.append(true).unwrap();
4565        let expected = expected.finish();
4566        assert_eq!(result.as_ref(), &expected);
4567    }
4568
4569    #[test]
4570    fn get_variant_as_map_non_object_rows() {
4571        let input: ArrayRef = Arc::new(StringArray::from(vec![
4572            Some(r#"{"a": 1}"#),
4573            Some("42"), // not an object
4574        ]));
4575        let variant_array = ArrayRef::from(json_to_variant(&input).unwrap());
4576        let data_type = map_data_type(DataType::Int64);
4577
4578        // With safe casting (the default), non-object rows become null
4579        let result = variant_get(&variant_array, map_get_options(&data_type)).unwrap();
4580        let mut expected = MapBuilder::new(None, StringBuilder::new(), Int64Builder::new());
4581        expected.keys().append_value("a");
4582        expected.values().append_value(1);
4583        expected.append(true).unwrap();
4584        expected.append(false).unwrap();
4585        let expected = expected.finish();
4586        assert_eq!(result.as_ref(), &expected);
4587
4588        // With strict casting, non-object rows are an error
4589        let options = map_get_options(&data_type).with_cast_options(CastOptions {
4590            safe: false,
4591            format_options: FormatOptions::default(),
4592        });
4593        let err = variant_get(&variant_array, options).unwrap_err();
4594        assert!(
4595            err.to_string().contains("Failed to extract object"),
4596            "unexpected error: {err}"
4597        );
4598    }
4599
4600    #[test]
4601    fn get_variant_as_map_invalid_entries() {
4602        let input: ArrayRef = Arc::new(StringArray::from(vec![Some(r#"{"a": 1}"#)]));
4603        let variant_array = ArrayRef::from(json_to_variant(&input).unwrap());
4604
4605        // Entries type is not a struct
4606        let data_type = DataType::Map(
4607            Arc::new(Field::new("entries", DataType::Int32, false)),
4608            false,
4609        );
4610        let err = variant_get(&variant_array, map_get_options(&data_type)).unwrap_err();
4611        assert!(
4612            err.to_string().contains("Map entries must be Struct"),
4613            "unexpected error: {err}"
4614        );
4615
4616        // Entries struct does not have exactly two fields
4617        let data_type = DataType::Map(
4618            Arc::new(Field::new(
4619                "entries",
4620                DataType::Struct(Fields::from(vec![Field::new(
4621                    "keys",
4622                    DataType::Utf8,
4623                    false,
4624                )])),
4625                false,
4626            )),
4627            false,
4628        );
4629        let err = variant_get(&variant_array, map_get_options(&data_type)).unwrap_err();
4630        assert!(
4631            err.to_string()
4632                .contains("Map entries must have exactly two fields"),
4633            "unexpected error: {err}"
4634        );
4635    }
4636
4637    fn invalid_time_variant_array() -> ArrayRef {
4638        // 86401000000 is invalid for Time64Microsecond (max is 86400000000)
4639        ArrayRef::from(VariantArray::from_iter([Some(86401000000_i64); 3]))
4640    }
4641
4642    #[test]
4643    fn test_variant_get_error_when_cast_failure_and_safe_false() {
4644        let variant_array = invalid_time_variant_array();
4645
4646        let field = Field::new("result", DataType::Time64(TimeUnit::Microsecond), true);
4647        let cast_options = CastOptions {
4648            safe: false, // Will error on cast failure
4649            ..Default::default()
4650        };
4651        let options = GetOptions::new()
4652            .with_as_type(Some(FieldRef::from(field)))
4653            .with_cast_options(cast_options);
4654        let err = variant_get(&variant_array, options).unwrap_err();
4655        assert!(
4656            err.to_string().contains(
4657                "Cast error: Failed to extract primitive of type Time64(µs) from variant Int64(86401000000) at path VariantPath([])"
4658            ),
4659            "actual: {err}",
4660        );
4661    }
4662
4663    #[test]
4664    fn test_variant_get_return_null_when_cast_failure_and_safe_true() {
4665        let variant_array = invalid_time_variant_array();
4666
4667        let field = Field::new("result", DataType::Time64(TimeUnit::Microsecond), true);
4668        let cast_options = CastOptions {
4669            safe: true, // Will return null on cast failure
4670            ..Default::default()
4671        };
4672        let options = GetOptions::new()
4673            .with_as_type(Some(FieldRef::from(field)))
4674            .with_cast_options(cast_options);
4675        let result = variant_get(&variant_array, options).unwrap();
4676        assert_eq!(3, result.len());
4677
4678        for i in 0..3 {
4679            assert!(result.is_null(i));
4680        }
4681    }
4682
4683    #[test]
4684    fn test_perfect_shredding_returns_same_arc_ptr() {
4685        let variant_array = perfectly_shredded_int32_variant_array();
4686
4687        let variant_array_ref = VariantArray::try_new(&variant_array).unwrap();
4688        let typed_value_arc = variant_array_ref.typed_value_column().unwrap().clone();
4689
4690        let field = Field::new("result", DataType::Int32, true);
4691        let options = GetOptions::new().with_as_type(Some(FieldRef::from(field)));
4692        let result = variant_get(&variant_array, options).unwrap();
4693
4694        assert!(Arc::ptr_eq(&typed_value_arc, &result));
4695    }
4696
4697    #[test]
4698    fn test_perfect_shredding_three_typed_value_columns() {
4699        // Column 1: perfectly shredded primitive with all nulls
4700        let all_nulls_values: Arc<Int32Array> = Arc::new(Int32Array::from(vec![
4701            Option::<i32>::None,
4702            Option::<i32>::None,
4703            Option::<i32>::None,
4704        ]));
4705        let all_nulls_erased: ArrayRef = all_nulls_values.clone();
4706        let all_nulls_field =
4707            ShreddedVariantFieldArray::perfectly_shredded(all_nulls_erased.clone());
4708        let all_nulls_type = all_nulls_field.data_type().clone();
4709        let all_nulls_struct: ArrayRef = ArrayRef::from(all_nulls_field);
4710
4711        // Column 2: perfectly shredded primitive with some nulls
4712        let some_nulls_values: Arc<Int32Array> =
4713            Arc::new(Int32Array::from(vec![Some(10), None, Some(30)]));
4714        let some_nulls_erased: ArrayRef = some_nulls_values.clone();
4715        let some_nulls_field =
4716            ShreddedVariantFieldArray::perfectly_shredded(some_nulls_erased.clone());
4717        let some_nulls_type = some_nulls_field.data_type().clone();
4718        let some_nulls_struct: ArrayRef = ArrayRef::from(some_nulls_field);
4719
4720        // Column 3: perfectly shredded nested struct
4721        let inner_values: Arc<Int32Array> =
4722            Arc::new(Int32Array::from(vec![Some(111), None, Some(333)]));
4723        let inner_erased: ArrayRef = inner_values.clone();
4724        let inner_field = ShreddedVariantFieldArray::perfectly_shredded(inner_erased.clone());
4725        let inner_field_type = inner_field.data_type().clone();
4726        let inner_struct_array: ArrayRef = ArrayRef::from(inner_field);
4727
4728        let nested_struct = Arc::new(
4729            StructArray::try_new(
4730                Fields::from(vec![Field::new("inner", inner_field_type, true)]),
4731                vec![inner_struct_array],
4732                None,
4733            )
4734            .unwrap(),
4735        );
4736        let nested_struct_erased: ArrayRef = nested_struct.clone();
4737        let struct_field =
4738            ShreddedVariantFieldArray::perfectly_shredded(nested_struct_erased.clone());
4739        let struct_field_type = struct_field.data_type().clone();
4740        let struct_field_struct: ArrayRef = ArrayRef::from(struct_field);
4741
4742        // Assemble the top-level typed_value struct with the three columns above
4743        let typed_value_struct = StructArray::try_new(
4744            Fields::from(vec![
4745                Field::new("all_nulls", all_nulls_type, true),
4746                Field::new("some_nulls", some_nulls_type, true),
4747                Field::new("struct_field", struct_field_type, true),
4748            ]),
4749            vec![all_nulls_struct, some_nulls_struct, struct_field_struct],
4750            None,
4751        )
4752        .unwrap();
4753
4754        let metadata = BinaryViewArray::from_iter_values(std::iter::repeat_n(
4755            EMPTY_VARIANT_METADATA_BYTES,
4756            all_nulls_values.len(),
4757        ));
4758        let variant_array: ArrayRef = VariantArray::perfectly_shredded(
4759            Arc::new(metadata),
4760            Arc::new(typed_value_struct),
4761            None,
4762        )
4763        .into();
4764
4765        // Case 1: all-null primitive column should reuse the typed_value Arc directly
4766        let all_nulls_field_ref = FieldRef::from(Field::new("result", DataType::Int32, true));
4767        let all_nulls_result = variant_get(
4768            &variant_array,
4769            GetOptions::new_with_path(VariantPath::try_from("all_nulls").unwrap())
4770                .with_as_type(Some(all_nulls_field_ref)),
4771        )
4772        .unwrap();
4773        assert!(Arc::ptr_eq(&all_nulls_result, &all_nulls_erased));
4774
4775        // Case 2: primitive column with some nulls should also reuse its typed_value Arc
4776        let some_nulls_field_ref = FieldRef::from(Field::new("result", DataType::Int32, true));
4777        let some_nulls_result = variant_get(
4778            &variant_array,
4779            GetOptions::new_with_path(VariantPath::try_from("some_nulls").unwrap())
4780                .with_as_type(Some(some_nulls_field_ref)),
4781        )
4782        .unwrap();
4783        assert!(Arc::ptr_eq(&some_nulls_result, &some_nulls_erased));
4784
4785        // Case 3: struct column should return a StructArray composed from the nested field
4786        let struct_child_fields = Fields::from(vec![Field::new("inner", DataType::Int32, true)]);
4787        let struct_field_ref = FieldRef::from(Field::new(
4788            "result",
4789            DataType::Struct(struct_child_fields.clone()),
4790            true,
4791        ));
4792        let struct_result = variant_get(
4793            &variant_array,
4794            GetOptions::new_with_path(VariantPath::try_from("struct_field").unwrap())
4795                .with_as_type(Some(struct_field_ref)),
4796        )
4797        .unwrap();
4798        let struct_array = struct_result
4799            .as_any()
4800            .downcast_ref::<StructArray>()
4801            .unwrap();
4802        assert_eq!(struct_array.len(), 3);
4803        assert_eq!(struct_array.null_count(), 0);
4804
4805        let inner_values_result = struct_array
4806            .column(0)
4807            .as_any()
4808            .downcast_ref::<Int32Array>()
4809            .unwrap();
4810        assert_eq!(inner_values_result.len(), 3);
4811        assert_eq!(inner_values_result.value(0), 111);
4812        assert!(inner_values_result.is_null(1));
4813        assert_eq!(inner_values_result.value(2), 333);
4814    }
4815
4816    #[test]
4817    fn test_variant_get_list_like_safe_cast() {
4818        let string_array: ArrayRef = Arc::new(StringArray::from(vec![
4819            r#"{"outer":{"list":[1, "two", 3]}}"#,
4820            r#"{"outer":{"list":"not a list"}}"#,
4821        ]));
4822        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
4823
4824        let element_array: ArrayRef = Arc::new(Int64Array::from(vec![Some(1), None, Some(3)]));
4825        let field = Arc::new(Field::new("item", Int64, true));
4826
4827        let expectations = vec![
4828            (
4829                DataType::List(field.clone()),
4830                Arc::new(ListArray::new(
4831                    field.clone(),
4832                    OffsetBuffer::new(ScalarBuffer::from(vec![0, 3, 3])),
4833                    element_array.clone(),
4834                    Some(NullBuffer::from(vec![true, false])),
4835                )) as ArrayRef,
4836            ),
4837            (
4838                DataType::LargeList(field.clone()),
4839                Arc::new(LargeListArray::new(
4840                    field.clone(),
4841                    OffsetBuffer::new(ScalarBuffer::from(vec![0, 3, 3])),
4842                    element_array.clone(),
4843                    Some(NullBuffer::from(vec![true, false])),
4844                )) as ArrayRef,
4845            ),
4846            (
4847                DataType::ListView(field.clone()),
4848                Arc::new(ListViewArray::new(
4849                    field.clone(),
4850                    ScalarBuffer::from(vec![0, 3]),
4851                    ScalarBuffer::from(vec![3, 0]),
4852                    element_array.clone(),
4853                    Some(NullBuffer::from(vec![true, false])),
4854                )) as ArrayRef,
4855            ),
4856            (
4857                DataType::LargeListView(field.clone()),
4858                Arc::new(LargeListViewArray::new(
4859                    field.clone(),
4860                    ScalarBuffer::from(vec![0, 3]),
4861                    ScalarBuffer::from(vec![3, 0]),
4862                    element_array,
4863                    Some(NullBuffer::from(vec![true, false])),
4864                )) as ArrayRef,
4865            ),
4866            (
4867                DataType::FixedSizeList(field.clone(), 3),
4868                Arc::new(FixedSizeListArray::new(
4869                    field,
4870                    3,
4871                    Arc::new(Int64Array::from(vec![
4872                        Some(1),
4873                        None,
4874                        Some(3),
4875                        None,
4876                        None,
4877                        None,
4878                    ])),
4879                    Some(NullBuffer::from(vec![true, false])),
4880                )) as ArrayRef,
4881            ),
4882        ];
4883
4884        for (request_type, expected) in expectations {
4885            let options =
4886                GetOptions::new_with_path(VariantPath::try_from("outer").unwrap().join("list"))
4887                    .with_as_type(Some(FieldRef::from(Field::new(
4888                        "result",
4889                        request_type.clone(),
4890                        true,
4891                    ))));
4892
4893            let result = variant_get(&variant_array, options).unwrap();
4894            assert_eq!(result.data_type(), expected.data_type());
4895            assert_eq!(&result, &expected);
4896        }
4897
4898        for (idx, expected) in [
4899            (0, vec![Some(1), None]),
4900            (1, vec![None, None]),
4901            (2, vec![Some(3), None]),
4902        ] {
4903            let index_options = GetOptions::new_with_path(
4904                VariantPath::try_from("outer")
4905                    .unwrap()
4906                    .join("list")
4907                    .join(idx),
4908            )
4909            .with_as_type(Some(FieldRef::from(Field::new(
4910                "result",
4911                DataType::Int64,
4912                true,
4913            ))));
4914            let index_result = variant_get(&variant_array, index_options).unwrap();
4915            let index_expected: ArrayRef = Arc::new(Int64Array::from(expected));
4916            assert_eq!(&index_result, &index_expected);
4917        }
4918    }
4919
4920    #[test]
4921    fn test_variant_get_nested_list() {
4922        use arrow::datatypes::Int64Type;
4923
4924        let string_array: ArrayRef = Arc::new(StringArray::from(vec![
4925            "[[1, 2], [3]]",
4926            r#"[[4], "not a list", [5, 6]]"#,
4927        ]));
4928        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
4929
4930        let inner_field = Arc::new(Field::new("item", Int64, true));
4931        let outer_field = Arc::new(Field::new(
4932            "item",
4933            DataType::List(inner_field.clone()),
4934            true,
4935        ));
4936        let request_type = DataType::List(outer_field.clone());
4937
4938        let options = GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
4939            "result",
4940            request_type,
4941            true,
4942        ))));
4943        let result = variant_get(&variant_array, options).unwrap();
4944        let outer = result.as_list::<i32>();
4945
4946        // Row 0: [[1, 2], [3]]
4947        let row0 = outer.value(0);
4948        let row0 = row0.as_list::<i32>();
4949        assert_eq!(row0.len(), 2);
4950        let elem0 = row0.value(0);
4951        assert_eq!(elem0.as_primitive::<Int64Type>().values(), &[1, 2]);
4952        let elem1 = row0.value(1);
4953        assert_eq!(elem1.as_primitive::<Int64Type>().values(), &[3]);
4954
4955        // Row 1: [[4], null, [5, 6]] — "not a list" becomes null inner list
4956        let row1 = outer.value(1);
4957        let row1 = row1.as_list::<i32>();
4958        assert_eq!(row1.len(), 3);
4959        let elem0 = row1.value(0);
4960        assert_eq!(elem0.as_primitive::<Int64Type>().values(), &[4]);
4961        assert!(row1.is_null(1));
4962        let elem2 = row1.value(2);
4963        assert_eq!(elem2.as_primitive::<Int64Type>().values(), &[5, 6]);
4964    }
4965
4966    #[test]
4967    fn test_variant_get_list_like_unsafe_cast_errors_on_element_mismatch() {
4968        let string_array: ArrayRef =
4969            Arc::new(StringArray::from(vec![r#"[1, "two", 3]"#, "[4, 5]"]));
4970        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
4971        let cast_options = CastOptions {
4972            safe: false,
4973            ..Default::default()
4974        };
4975
4976        let item_field = Arc::new(Field::new("item", DataType::Int64, true));
4977        let request_types = vec![
4978            DataType::List(item_field.clone()),
4979            DataType::LargeList(item_field.clone()),
4980            DataType::ListView(item_field.clone()),
4981            DataType::LargeListView(item_field),
4982        ];
4983
4984        for request_type in request_types {
4985            let options = GetOptions::new()
4986                .with_as_type(Some(FieldRef::from(Field::new(
4987                    "result",
4988                    request_type.clone(),
4989                    true,
4990                ))))
4991                .with_cast_options(cast_options.clone());
4992
4993            let err = variant_get(&variant_array, options).unwrap_err();
4994            assert!(
4995                err.to_string()
4996                    .contains("Failed to extract primitive of type Int64")
4997            );
4998        }
4999    }
5000
5001    #[test]
5002    fn test_variant_get_list_like_unsafe_cast_preserves_null_elements() {
5003        let string_array: ArrayRef = Arc::new(StringArray::from(vec!["[1, null, 3]"]));
5004        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
5005        let cast_options = CastOptions {
5006            safe: false,
5007            ..Default::default()
5008        };
5009        let options = GetOptions::new()
5010            .with_as_type(Some(FieldRef::from(Field::new(
5011                "result",
5012                DataType::List(Arc::new(Field::new("item", DataType::Int64, true))),
5013                true,
5014            ))))
5015            .with_cast_options(cast_options);
5016
5017        let result = variant_get(&variant_array, options).unwrap();
5018        let list_array = result.as_any().downcast_ref::<ListArray>().unwrap();
5019        let values = list_array
5020            .values()
5021            .as_any()
5022            .downcast_ref::<Int64Array>()
5023            .unwrap();
5024
5025        assert_eq!(values.len(), 3);
5026        assert_eq!(values.value(0), 1);
5027        assert!(values.is_null(1));
5028        assert_eq!(values.value(2), 3);
5029    }
5030
5031    #[test]
5032    fn test_variant_get_list_like_null_element_in_non_nullable_item() {
5033        let item_field = Arc::new(Field::new("item", Int64, false));
5034        let data_types = [
5035            (DataType::List(item_field.clone()), "ListArray"),
5036            (DataType::LargeList(item_field.clone()), "LargeListArray"),
5037            (DataType::ListView(item_field.clone()), "ListViewArray"),
5038            (DataType::LargeListView(item_field), "LargeListViewArray"),
5039        ];
5040        // A null element, and an element that a safe cast turns into null
5041        for json in ["[1, null, 3]", r#"[1, "two", 3]"#] {
5042            let string_array: ArrayRef = Arc::new(StringArray::from(vec![json]));
5043            let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
5044            for (data_type, array_name) in &data_types {
5045                let options = GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
5046                    "result",
5047                    data_type.clone(),
5048                    true,
5049                ))));
5050                let err = variant_get(&variant_array, options).unwrap_err();
5051                assert_eq!(
5052                    err.to_string(),
5053                    format!(
5054                        "Invalid argument error: Non-nullable field of {array_name} \"item\" cannot contain nulls"
5055                    )
5056                );
5057            }
5058        }
5059    }
5060
5061    #[test]
5062    fn test_variant_get_list_like_unsafe_cast_errors_on_non_list() {
5063        let string_array: ArrayRef = Arc::new(StringArray::from(vec!["[1, 2]", "\"not a list\""]));
5064        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
5065        let cast_options = CastOptions {
5066            safe: false,
5067            ..Default::default()
5068        };
5069        let item_field = Arc::new(Field::new("item", Int64, true));
5070        let data_types = vec![
5071            DataType::List(item_field.clone()),
5072            DataType::LargeList(item_field.clone()),
5073            DataType::ListView(item_field.clone()),
5074            DataType::LargeListView(item_field.clone()),
5075            DataType::FixedSizeList(item_field, 2),
5076        ];
5077
5078        for data_type in data_types {
5079            let options = GetOptions::new()
5080                .with_as_type(Some(FieldRef::from(Field::new("result", data_type, true))))
5081                .with_cast_options(cast_options.clone());
5082
5083            let err = variant_get(&variant_array, options).unwrap_err();
5084            assert!(
5085                err.to_string()
5086                    .contains("Failed to extract list from variant"),
5087            );
5088        }
5089    }
5090
5091    #[test]
5092    fn test_variant_get_fixed_size_list_wrong_size() {
5093        let string_array: ArrayRef = Arc::new(StringArray::from(vec!["[1, 2, 3]"]));
5094        let variant_array = ArrayRef::from(json_to_variant(&string_array).unwrap());
5095        let item_field = Arc::new(Field::new("item", Int64, true));
5096
5097        // With `safe` set to true, size mismatch should return Null.
5098        let options = GetOptions::new()
5099            .with_as_type(Some(FieldRef::from(Field::new(
5100                "result",
5101                DataType::FixedSizeList(item_field.clone(), 2),
5102                true,
5103            ))))
5104            .with_cast_options(CastOptions {
5105                safe: true,
5106                ..Default::default()
5107            });
5108        let result = variant_get(&variant_array, options).unwrap();
5109        let fixed_size_list = result
5110            .as_any()
5111            .downcast_ref::<FixedSizeListArray>()
5112            .expect("Expected FixedSizeListArray");
5113        assert_eq!(fixed_size_list.len(), 1);
5114        assert!(fixed_size_list.is_null(0));
5115
5116        // With `safe` set to false, error should be raised on wrong sized fixed list.
5117        let options = GetOptions::new()
5118            .with_as_type(Some(FieldRef::from(Field::new(
5119                "result",
5120                DataType::FixedSizeList(item_field.clone(), 2),
5121                true,
5122            ))))
5123            .with_cast_options(CastOptions {
5124                safe: false,
5125                ..Default::default()
5126            });
5127        let err = variant_get(&variant_array, options).unwrap_err();
5128        assert!(
5129            err.to_string()
5130                .contains("Expected fixed size list of size 2, got size 3"),
5131            "got: {err}",
5132        );
5133    }
5134
5135    macro_rules! perfectly_shredded_preserves_top_level_nulls_test {
5136        ($name:ident, $result_type:expr, $typed_value:expr, $expected_array:expr) => {
5137            perfectly_shredded_preserves_top_level_nulls_test!(
5138                $name,
5139                $result_type,
5140                $typed_value,
5141                Some(NullBuffer::from(vec![true, false, true])),
5142                $expected_array
5143            );
5144        };
5145        ($name:ident, $result_type:expr, $typed_value:expr, $parent_nulls:expr, $expected_array:expr) => {
5146            #[test]
5147            fn $name() {
5148                let metadata = Arc::new(BinaryViewArray::from_iter_values(std::iter::repeat_n(
5149                    EMPTY_VARIANT_METADATA_BYTES,
5150                    3,
5151                )));
5152                let typed_value: ArrayRef = Arc::new($typed_value);
5153                let variant_array: ArrayRef =
5154                    VariantArray::perfectly_shredded(metadata, typed_value, $parent_nulls).into();
5155
5156                let result = variant_get(
5157                    &variant_array,
5158                    GetOptions::new().with_as_type(Some(FieldRef::from(Field::new(
5159                        "result",
5160                        $result_type,
5161                        true,
5162                    )))),
5163                )
5164                .unwrap();
5165
5166                let expected_array: ArrayRef = Arc::new($expected_array);
5167                assert_eq!(&result, &expected_array);
5168            }
5169        };
5170    }
5171
5172    perfectly_shredded_preserves_top_level_nulls_test!(
5173        test_variant_get_perfectly_shredded_integer_preserves_top_level_nulls,
5174        DataType::Int32,
5175        Int32Array::from(vec![Some(0_i32), Some(1_i32), Some(2_i32)]),
5176        Int32Array::from(vec![Some(0_i32), None, Some(2_i32)])
5177    );
5178
5179    perfectly_shredded_preserves_top_level_nulls_test!(
5180        test_variant_get_perfectly_shredded_integer_unions_child_and_top_level_nulls,
5181        DataType::Int32,
5182        Int32Array::from(vec![None, Some(1_i32), Some(2_i32)]),
5183        Some(NullBuffer::from(vec![true, false, true])),
5184        Int32Array::from(vec![None, None, Some(2_i32)])
5185    );
5186
5187    perfectly_shredded_preserves_top_level_nulls_test!(
5188        test_variant_get_perfectly_shredded_null_preserves_top_level_nulls,
5189        DataType::Null,
5190        NullArray::new(3),
5191        NullArray::new(3)
5192    );
5193
5194    perfectly_shredded_preserves_top_level_nulls_test!(
5195        test_variant_get_perfectly_shredded_binary_view_preserves_top_level_nulls,
5196        DataType::BinaryView,
5197        BinaryViewArray::from(vec![
5198            Some(b"Apache" as &[u8]),
5199            Some(b"masked-null" as &[u8]),
5200            Some(b"Parquet-variant" as &[u8]),
5201        ]),
5202        BinaryViewArray::from(vec![
5203            Some(b"Apache" as &[u8]),
5204            None,
5205            Some(b"Parquet-variant" as &[u8]),
5206        ])
5207    );
5208
5209    perfectly_shredded_preserves_top_level_nulls_test!(
5210        test_variant_get_perfectly_shredded_binary_preserves_top_level_nulls,
5211        DataType::Binary,
5212        BinaryArray::from(vec![
5213            Some(b"Apache" as &[u8]),
5214            Some(b"masked-null" as &[u8]),
5215            Some(b"Parquet-variant" as &[u8]),
5216        ]),
5217        BinaryArray::from(vec![
5218            Some(b"Apache" as &[u8]),
5219            None,
5220            Some(b"Parquet-variant" as &[u8]),
5221        ])
5222    );
5223
5224    perfectly_shredded_preserves_top_level_nulls_test!(
5225        test_variant_get_perfectly_shredded_decimal4_preserves_top_level_nulls,
5226        DataType::Decimal32(5, 2),
5227        Decimal32Array::from(vec![Some(12345), Some(23400), Some(-12342)])
5228            .with_precision_and_scale(5, 2)
5229            .unwrap(),
5230        Decimal32Array::from(vec![Some(12345), None, Some(-12342)])
5231            .with_precision_and_scale(5, 2)
5232            .unwrap()
5233    );
5234
5235    perfectly_shredded_preserves_top_level_nulls_test!(
5236        test_variant_get_perfectly_shredded_decimal8_preserves_top_level_nulls,
5237        DataType::Decimal64(10, 1),
5238        Decimal64Array::from(vec![Some(1234567809), Some(1456787000), Some(-1234561203)])
5239            .with_precision_and_scale(10, 1)
5240            .unwrap(),
5241        Decimal64Array::from(vec![Some(1234567809), None, Some(-1234561203)])
5242            .with_precision_and_scale(10, 1)
5243            .unwrap()
5244    );
5245
5246    perfectly_shredded_preserves_top_level_nulls_test!(
5247        test_variant_get_perfectly_shredded_decimal16_preserves_top_level_nulls,
5248        DataType::Decimal128(20, 3),
5249        Decimal128Array::from(vec![
5250            Some(i128::from_str("12345678901234567899").unwrap()),
5251            Some(i128::from_str("23445677483748324300").unwrap()),
5252            Some(i128::from_str("-12345678901234567899").unwrap()),
5253        ])
5254        .with_precision_and_scale(20, 3)
5255        .unwrap(),
5256        Decimal128Array::from(vec![
5257            Some(i128::from_str("12345678901234567899").unwrap()),
5258            None,
5259            Some(i128::from_str("-12345678901234567899").unwrap()),
5260        ])
5261        .with_precision_and_scale(20, 3)
5262        .unwrap()
5263    );
5264
5265    fn union_get_options(fields: &UnionFields, mode: UnionMode) -> GetOptions<'static> {
5266        let field = Field::new("union", DataType::Union(fields.clone(), mode), true);
5267        GetOptions::new().with_as_type(Some(FieldRef::from(field)))
5268    }
5269
5270    fn int_str_bool_union_fields() -> UnionFields {
5271        UnionFields::try_new(
5272            vec![0, 1, 2],
5273            vec![
5274                Field::new("int", DataType::Int64, true),
5275                Field::new("str", DataType::Utf8, true),
5276                Field::new("bool", DataType::Boolean, true),
5277            ],
5278        )
5279        .unwrap()
5280    }
5281
5282    /// int8, string, bool, array-level null, `Variant::Null`, double (no matching field), int64
5283    fn mixed_variant_array() -> ArrayRef {
5284        let mut builder = VariantArrayBuilder::new(7);
5285        builder.append_variant(Variant::Int8(1));
5286        builder.append_variant(Variant::from("hello"));
5287        builder.append_variant(Variant::from(true));
5288        builder.append_null();
5289        builder.append_variant(Variant::Null);
5290        builder.append_variant(Variant::Double(2.5));
5291        builder.append_variant(Variant::Int64(5_000_000_000));
5292        ArrayRef::from(builder.build())
5293    }
5294
5295    #[test]
5296    fn get_variant_as_dense_union() {
5297        let fields = int_str_bool_union_fields();
5298        let array = mixed_variant_array();
5299        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5300
5301        // nulls, `Variant::Null`, and the unmatched Double all land as nulls in the first child
5302        let expected: ArrayRef = Arc::new(
5303            UnionArray::try_new(
5304                fields,
5305                ScalarBuffer::from(vec![0i8, 1, 2, 0, 0, 0, 0]),
5306                Some(ScalarBuffer::from(vec![0i32, 0, 0, 1, 2, 3, 4])),
5307                vec![
5308                    Arc::new(Int64Array::from(vec![
5309                        Some(1),
5310                        None,
5311                        None,
5312                        None,
5313                        Some(5_000_000_000),
5314                    ])),
5315                    Arc::new(StringArray::from(vec!["hello"])),
5316                    Arc::new(BooleanArray::from(vec![true])),
5317                ],
5318            )
5319            .unwrap(),
5320        );
5321        assert_eq!(&result, &expected);
5322    }
5323
5324    #[test]
5325    fn get_variant_as_sparse_union() {
5326        let fields = int_str_bool_union_fields();
5327        let array = mixed_variant_array();
5328        let result = variant_get(&array, union_get_options(&fields, UnionMode::Sparse)).unwrap();
5329
5330        let expected: ArrayRef = Arc::new(
5331            UnionArray::try_new(
5332                fields,
5333                ScalarBuffer::from(vec![0i8, 1, 2, 0, 0, 0, 0]),
5334                None,
5335                vec![
5336                    Arc::new(Int64Array::from(vec![
5337                        Some(1),
5338                        None,
5339                        None,
5340                        None,
5341                        None,
5342                        None,
5343                        Some(5_000_000_000),
5344                    ])),
5345                    Arc::new(StringArray::from(vec![
5346                        None,
5347                        Some("hello"),
5348                        None,
5349                        None,
5350                        None,
5351                        None,
5352                        None,
5353                    ])),
5354                    Arc::new(BooleanArray::from(vec![
5355                        None,
5356                        None,
5357                        Some(true),
5358                        None,
5359                        None,
5360                        None,
5361                        None,
5362                    ])),
5363                ],
5364            )
5365            .unwrap(),
5366        );
5367        assert_eq!(&result, &expected);
5368    }
5369
5370    #[test]
5371    fn get_variant_as_union_prefers_most_exact_field() {
5372        // Int8 picks the later-declared Int32 over Int64: exactness wins over declaration order
5373        let fields = UnionFields::try_new(
5374            vec![0, 1],
5375            vec![
5376                Field::new("big", DataType::Int64, true),
5377                Field::new("small", DataType::Int32, true),
5378            ],
5379        )
5380        .unwrap();
5381        let mut builder = VariantArrayBuilder::new(3);
5382        builder.append_variant(Variant::Int8(1));
5383        builder.append_variant(Variant::Int32(2));
5384        builder.append_variant(Variant::Int64(3));
5385        let array = ArrayRef::from(builder.build());
5386
5387        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5388
5389        let expected: ArrayRef = Arc::new(
5390            UnionArray::try_new(
5391                fields,
5392                ScalarBuffer::from(vec![1i8, 1, 0]),
5393                Some(ScalarBuffer::from(vec![0i32, 1, 0])),
5394                vec![
5395                    Arc::new(Int64Array::from(vec![3])),
5396                    Arc::new(Int32Array::from(vec![1, 2])),
5397                ],
5398            )
5399            .unwrap(),
5400        );
5401        assert_eq!(&result, &expected);
5402    }
5403
5404    #[test]
5405    fn get_variant_as_union_with_encoded_children() {
5406        let encoded_types = [
5407            DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
5408            DataType::RunEndEncoded(
5409                Arc::new(Field::new(
5410                    Field::REE_RUN_ENDS_FIELD_DEFAULT_NAME,
5411                    DataType::Int32,
5412                    false,
5413                )),
5414                Arc::new(Field::new(
5415                    Field::REE_VALUES_FIELD_DEFAULT_NAME,
5416                    DataType::Utf8,
5417                    true,
5418                )),
5419            ),
5420        ];
5421
5422        for data_type in encoded_types {
5423            let fields = UnionFields::try_new(
5424                vec![0],
5425                vec![Field::new("encoded", data_type.clone(), true)],
5426            )
5427            .unwrap();
5428            let array = ArrayRef::from(VariantArray::from_iter([Some("apple"), Some("banana")]));
5429            let options =
5430                union_get_options(&fields, UnionMode::Dense).with_cast_options(CastOptions {
5431                    safe: false,
5432                    ..Default::default()
5433                });
5434
5435            let result = variant_get(&array, options).unwrap();
5436            let union = result.as_any().downcast_ref::<UnionArray>().unwrap();
5437            assert_eq!(union.type_ids(), &[0i8, 0]);
5438            assert_eq!(union.child(0).data_type(), &data_type);
5439
5440            let decoded = cast(union.child(0).as_ref(), &DataType::Utf8).unwrap();
5441            let expected = StringArray::from(vec!["apple", "banana"]);
5442            assert_eq!(decoded.as_ref(), &expected);
5443        }
5444    }
5445
5446    #[test]
5447    fn get_variant_as_union_with_fixed_size_list_child() {
5448        let item = Arc::new(Field::new("item", DataType::Int64, true));
5449        let fields = UnionFields::try_new(
5450            vec![0],
5451            vec![Field::new("fixed", DataType::FixedSizeList(item, 2), true)],
5452        )
5453        .unwrap();
5454        let json = StringArray::from(vec!["[1, 2]"]);
5455        let array = ArrayRef::from(json_to_variant(&(Arc::new(json) as ArrayRef)).unwrap());
5456
5457        for safe in [true, false] {
5458            let options =
5459                union_get_options(&fields, UnionMode::Dense).with_cast_options(CastOptions {
5460                    safe,
5461                    ..Default::default()
5462                });
5463            let result = variant_get(&array, options).unwrap();
5464            let union = result.as_any().downcast_ref::<UnionArray>().unwrap();
5465            assert_eq!(union.type_ids(), &[0i8]);
5466            let list = union
5467                .child(0)
5468                .as_any()
5469                .downcast_ref::<FixedSizeListArray>()
5470                .unwrap();
5471            assert_eq!(
5472                list.value(0)
5473                    .as_primitive::<arrow::datatypes::Int64Type>()
5474                    .values(),
5475                &[1, 2]
5476            );
5477        }
5478    }
5479
5480    #[test]
5481    fn get_variant_as_union_skips_decimal_that_cannot_fit() {
5482        let fields = UnionFields::try_new(
5483            vec![0, 1],
5484            vec![
5485                Field::new("too_narrow", DataType::Decimal32(3, 2), true),
5486                Field::new("fits", DataType::Decimal32(5, 2), true),
5487            ],
5488        )
5489        .unwrap();
5490        let array = ArrayRef::from(VariantArray::from_iter([Some(
5491            VariantDecimal4::try_new(12_345, 2).unwrap(),
5492        )]));
5493
5494        for safe in [true, false] {
5495            let options =
5496                union_get_options(&fields, UnionMode::Dense).with_cast_options(CastOptions {
5497                    safe,
5498                    ..Default::default()
5499                });
5500            let result = variant_get(&array, options).unwrap();
5501            let union = result.as_any().downcast_ref::<UnionArray>().unwrap();
5502            assert_eq!(union.type_ids(), &[1i8]);
5503            let decimal = union
5504                .child(1)
5505                .as_any()
5506                .downcast_ref::<Decimal32Array>()
5507                .unwrap();
5508            assert_eq!(decimal.value(0), 12_345);
5509        }
5510    }
5511
5512    #[test]
5513    fn get_variant_as_union_with_null_field() {
5514        // nulls and unmatched values land in the Null-typed field instead of the first one
5515        let fields = UnionFields::try_new(
5516            vec![0, 1],
5517            vec![
5518                Field::new("int", DataType::Int64, true),
5519                Field::new("null", DataType::Null, true),
5520            ],
5521        )
5522        .unwrap();
5523        let mut builder = VariantArrayBuilder::new(4);
5524        builder.append_variant(Variant::Int8(1));
5525        builder.append_null();
5526        builder.append_variant(Variant::Null);
5527        builder.append_variant(Variant::from("no matching field"));
5528        let array = ArrayRef::from(builder.build());
5529
5530        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5531
5532        let expected: ArrayRef = Arc::new(
5533            UnionArray::try_new(
5534                fields,
5535                ScalarBuffer::from(vec![0i8, 1, 1, 1]),
5536                Some(ScalarBuffer::from(vec![0i32, 0, 1, 2])),
5537                vec![
5538                    Arc::new(Int64Array::from(vec![1])),
5539                    Arc::new(NullArray::new(3)),
5540                ],
5541            )
5542            .unwrap(),
5543        );
5544        assert_eq!(&result, &expected);
5545    }
5546
5547    #[test]
5548    fn get_variant_as_union_of_nested_types() {
5549        let fields = UnionFields::try_new(
5550            vec![0, 1, 2],
5551            vec![
5552                Field::new(
5553                    "struct",
5554                    DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int64, true)])),
5555                    true,
5556                ),
5557                Field::new(
5558                    "list",
5559                    DataType::List(Arc::new(Field::new("item", DataType::Int64, true))),
5560                    true,
5561                ),
5562                Field::new("str", DataType::Utf8, true),
5563            ],
5564        )
5565        .unwrap();
5566        let json = StringArray::from(vec![r#"{"a": 1}"#, "[1, 2, 3]", "\"s\""]);
5567        let array = ArrayRef::from(json_to_variant(&(Arc::new(json) as ArrayRef)).unwrap());
5568
5569        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5570
5571        let mut list_builder = ListBuilder::new(Int64Builder::new());
5572        list_builder.append_value([Some(1), Some(2), Some(3)]);
5573        let expected: ArrayRef = Arc::new(
5574            UnionArray::try_new(
5575                fields,
5576                ScalarBuffer::from(vec![0i8, 1, 2]),
5577                Some(ScalarBuffer::from(vec![0i32, 0, 0])),
5578                vec![
5579                    Arc::new(StructArray::from(vec![(
5580                        Arc::new(Field::new("a", DataType::Int64, true)),
5581                        Arc::new(Int64Array::from(vec![1])) as ArrayRef,
5582                    )])),
5583                    Arc::new(list_builder.finish()),
5584                    Arc::new(StringArray::from(vec!["s"])),
5585                ],
5586            )
5587            .unwrap(),
5588        );
5589        assert_eq!(&result, &expected);
5590    }
5591
5592    #[test]
5593    fn get_variant_as_union_with_map_field() {
5594        // With no Struct field in the union, an object routes to the Map child.
5595        let fields = UnionFields::try_new(
5596            vec![0, 1],
5597            vec![
5598                Field::new("map", map_data_type(DataType::Int64), true),
5599                Field::new("str", DataType::Utf8, true),
5600            ],
5601        )
5602        .unwrap();
5603        let json = StringArray::from(vec![r#"{"a": 1, "b": 2}"#, "\"hi\""]);
5604        let array = ArrayRef::from(json_to_variant(&(Arc::new(json) as ArrayRef)).unwrap());
5605
5606        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5607
5608        let mut map_builder = MapBuilder::new(None, StringBuilder::new(), Int64Builder::new());
5609        map_builder.keys().append_value("a");
5610        map_builder.values().append_value(1);
5611        map_builder.keys().append_value("b");
5612        map_builder.values().append_value(2);
5613        map_builder.append(true).unwrap();
5614        let expected: ArrayRef = Arc::new(
5615            UnionArray::try_new(
5616                fields,
5617                ScalarBuffer::from(vec![0i8, 1]),
5618                Some(ScalarBuffer::from(vec![0i32, 0])),
5619                vec![
5620                    Arc::new(map_builder.finish()),
5621                    Arc::new(StringArray::from(vec!["hi"])),
5622                ],
5623            )
5624            .unwrap(),
5625        );
5626        assert_eq!(&result, &expected);
5627    }
5628
5629    #[test]
5630    fn get_variant_as_union_prefers_struct_over_map() {
5631        // Both a Struct and a Map field can hold an object; the object routes to Struct because
5632        // it represents the object more exactly (rank 0 vs 1).
5633        let fields = UnionFields::try_new(
5634            vec![0, 1],
5635            vec![
5636                Field::new("map", map_data_type(DataType::Int64), true),
5637                Field::new(
5638                    "struct",
5639                    DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int64, true)])),
5640                    true,
5641                ),
5642            ],
5643        )
5644        .unwrap();
5645        let json = StringArray::from(vec![r#"{"a": 1}"#]);
5646        let array = ArrayRef::from(json_to_variant(&(Arc::new(json) as ArrayRef)).unwrap());
5647
5648        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5649        let union = result.as_any().downcast_ref::<UnionArray>().unwrap();
5650        // type_id 1 == the struct child
5651        assert_eq!(union.type_ids(), &[1i8]);
5652    }
5653
5654    #[test]
5655    fn get_variant_as_union_no_matching_field() {
5656        // Like other requested fields, union child nullability does not override safe casting.
5657        let fields =
5658            UnionFields::try_new(vec![0], vec![Field::new("str", DataType::Utf8, false)]).unwrap();
5659        let mut builder = VariantArrayBuilder::new(2);
5660        builder.append_variant(Variant::from("kept"));
5661        builder.append_variant(Variant::Int8(1));
5662        let array = ArrayRef::from(builder.build());
5663
5664        // Safe mode: the Int8 row becomes a null in the first (only) child.
5665        let result = variant_get(&array, union_get_options(&fields, UnionMode::Dense)).unwrap();
5666        let expected: ArrayRef = Arc::new(
5667            UnionArray::try_new(
5668                fields.clone(),
5669                ScalarBuffer::from(vec![0i8, 0]),
5670                Some(ScalarBuffer::from(vec![0i32, 1])),
5671                vec![Arc::new(StringArray::from(vec![Some("kept"), None]))],
5672            )
5673            .unwrap(),
5674        );
5675        assert_eq!(&result, &expected);
5676
5677        // Strict mode: the same row is a cast error.
5678        let options = union_get_options(&fields, UnionMode::Dense).with_cast_options(CastOptions {
5679            safe: false,
5680            ..Default::default()
5681        });
5682        let err = variant_get(&array, options).unwrap_err();
5683        assert!(
5684            err.to_string().contains("no field can represent it"),
5685            "unexpected error: {err}"
5686        );
5687    }
5688
5689    #[test]
5690    fn get_variant_as_union_empty_fields_errors() {
5691        let array = ArrayRef::from(VariantArray::from_iter([Some(1_i8)]));
5692
5693        let err = variant_get(
5694            &array,
5695            union_get_options(&UnionFields::empty(), UnionMode::Dense),
5696        )
5697        .unwrap_err();
5698        assert!(
5699            err.to_string().contains("at least one union field"),
5700            "unexpected error: {err}"
5701        );
5702    }
5703}