Skip to main content

vortex_arrow/
convert.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Canonical (non-plugin) conversions of Arrow arrays into Vortex arrays.
5//!
6//! Each `from_arrow_*` function converts one Arrow array shape; [`from_arrow_dyn`] dispatches on
7//! the Arrow [`DataType`]. These functions have no awareness of Arrow extension types — the
8//! plugin-aware entry points are the [`ArrowSession`](crate::ArrowSession) methods, which fall
9//! back to these conversions for non-extension data. The deprecated [`FromArrowArray`] impls are
10//! thin shims over these functions and will eventually be removed.
11#![allow(deprecated)]
12
13use std::ops::Range;
14use std::sync::Arc;
15
16use arrow_array::AnyDictionaryArray;
17use arrow_array::Array as ArrowArray;
18use arrow_array::ArrowPrimitiveType;
19use arrow_array::BooleanArray as ArrowBooleanArray;
20use arrow_array::DictionaryArray;
21use arrow_array::FixedSizeListArray as ArrowFixedSizeListArray;
22use arrow_array::GenericByteArray;
23use arrow_array::GenericByteViewArray;
24use arrow_array::GenericListArray;
25use arrow_array::GenericListViewArray;
26use arrow_array::MapArray as ArrowMapArray;
27use arrow_array::NullArray as ArrowNullArray;
28use arrow_array::OffsetSizeTrait;
29use arrow_array::PrimitiveArray as ArrowPrimitiveArray;
30use arrow_array::RecordBatch;
31use arrow_array::StructArray as ArrowStructArray;
32use arrow_array::cast::AsArray;
33use arrow_array::cast::as_null_array;
34use arrow_array::make_array;
35use arrow_array::types::ArrowDictionaryKeyType;
36use arrow_array::types::ByteArrayType;
37use arrow_array::types::ByteViewType;
38use arrow_array::types::Date32Type;
39use arrow_array::types::Date64Type;
40use arrow_array::types::Decimal32Type;
41use arrow_array::types::Decimal64Type;
42use arrow_array::types::Decimal128Type;
43use arrow_array::types::Decimal256Type;
44use arrow_array::types::Float16Type;
45use arrow_array::types::Float32Type;
46use arrow_array::types::Float64Type;
47use arrow_array::types::Int8Type;
48use arrow_array::types::Int16Type;
49use arrow_array::types::Int32Type;
50use arrow_array::types::Int64Type;
51use arrow_array::types::Time32MillisecondType;
52use arrow_array::types::Time32SecondType;
53use arrow_array::types::Time64MicrosecondType;
54use arrow_array::types::Time64NanosecondType;
55use arrow_array::types::TimestampMicrosecondType;
56use arrow_array::types::TimestampMillisecondType;
57use arrow_array::types::TimestampNanosecondType;
58use arrow_array::types::TimestampSecondType;
59use arrow_array::types::UInt8Type;
60use arrow_array::types::UInt16Type;
61use arrow_array::types::UInt32Type;
62use arrow_array::types::UInt64Type;
63use arrow_buffer::ArrowNativeType;
64use arrow_buffer::BooleanBuffer;
65use arrow_buffer::Buffer as ArrowBuffer;
66use arrow_buffer::ScalarBuffer;
67use arrow_buffer::buffer::NullBuffer;
68use arrow_buffer::buffer::OffsetBuffer;
69use arrow_schema::DataType;
70use arrow_schema::TimeUnit as ArrowTimeUnit;
71use vortex_array::ArrayRef;
72use vortex_array::IntoArray;
73use vortex_array::arrays::BoolArray;
74use vortex_array::arrays::DecimalArray;
75use vortex_array::arrays::DictArray;
76use vortex_array::arrays::FixedSizeListArray;
77use vortex_array::arrays::ListArray;
78use vortex_array::arrays::ListViewArray;
79use vortex_array::arrays::MapArray;
80use vortex_array::arrays::NullArray;
81use vortex_array::arrays::PrimitiveArray;
82use vortex_array::arrays::Struct;
83use vortex_array::arrays::StructArray;
84use vortex_array::arrays::TemporalArray;
85use vortex_array::arrays::VarBinArray;
86use vortex_array::arrays::VarBinViewArray;
87use vortex_array::arrays::struct_::StructArrayExt;
88use vortex_array::dtype::DType;
89use vortex_array::dtype::DecimalDType;
90use vortex_array::dtype::FieldNames;
91use vortex_array::dtype::IntegerPType;
92use vortex_array::dtype::MapDType;
93use vortex_array::dtype::NativePType;
94use vortex_array::dtype::Nullability;
95use vortex_array::dtype::PType;
96use vortex_array::dtype::i256;
97use vortex_array::extension::datetime::TimeUnit;
98use vortex_array::validity::Validity;
99use vortex_buffer::Alignment;
100use vortex_buffer::BitBuffer;
101use vortex_buffer::Buffer;
102use vortex_buffer::ByteBuffer;
103use vortex_error::VortexResult;
104use vortex_error::vortex_bail;
105use vortex_error::vortex_ensure;
106use vortex_error::vortex_ensure_eq;
107use vortex_error::vortex_err;
108use vortex_error::vortex_panic;
109
110use crate::FromArrowArray;
111use crate::dtype::from_arrow_time_unit;
112
113/// Zero-copy conversion of Arrow buffers into non-nullable Vortex arrays.
114///
115/// This mirrors [`IntoArray`] for Arrow buffer types; a separate trait is required because both
116/// [`IntoArray`] and the Arrow buffer types are foreign to this crate.
117pub trait IntoVortexArray {
118    /// Convert this Arrow buffer into a non-nullable Vortex array without copying.
119    fn into_array(self) -> ArrayRef;
120}
121
122impl IntoVortexArray for ArrowBuffer {
123    fn into_array(self) -> ArrayRef {
124        PrimitiveArray::from_byte_buffer(
125            ByteBuffer::from_arrow_buffer(self, Alignment::of::<u8>()),
126            PType::U8,
127            Validity::NonNullable,
128        )
129        .into_array()
130    }
131}
132
133impl IntoVortexArray for BooleanBuffer {
134    fn into_array(self) -> ArrayRef {
135        BoolArray::new(self.into(), Validity::NonNullable).into_array()
136    }
137}
138
139impl<T> IntoVortexArray for ScalarBuffer<T>
140where
141    T: ArrowNativeType + NativePType,
142{
143    fn into_array(self) -> ArrayRef {
144        PrimitiveArray::new(
145            Buffer::<T>::from_arrow_scalar_buffer(self),
146            Validity::NonNullable,
147        )
148        .into_array()
149    }
150}
151
152impl<O> IntoVortexArray for OffsetBuffer<O>
153where
154    O: IntegerPType + OffsetSizeTrait,
155{
156    fn into_array(self) -> ArrayRef {
157        let primitive = PrimitiveArray::new(
158            Buffer::from_arrow_scalar_buffer(self.into_inner()),
159            Validity::NonNullable,
160        );
161
162        primitive.into_array()
163    }
164}
165
166/// Zero-copy conversion of an Arrow numeric primitive array into a Vortex array.
167///
168/// Use [`from_arrow_temporal`] for Arrow temporal arrays, which carry a logical dtype beyond
169/// their physical primitive storage.
170pub fn from_arrow_primitive<T: ArrowPrimitiveType>(
171    value: &ArrowPrimitiveArray<T>,
172    nullable: bool,
173) -> VortexResult<ArrayRef>
174where
175    T::Native: NativePType,
176{
177    let buffer = Buffer::from_arrow_scalar_buffer(value.values().clone());
178    let validity = nulls(value.nulls(), nullable)?;
179    Ok(PrimitiveArray::new(buffer, validity).into_array())
180}
181
182macro_rules! impl_from_arrow_primitive {
183    ($T:path) => {
184        impl FromArrowArray<&ArrowPrimitiveArray<$T>> for ArrayRef {
185            fn from_arrow(value: &ArrowPrimitiveArray<$T>, nullable: bool) -> VortexResult<Self> {
186                from_arrow_primitive(value, nullable)
187            }
188        }
189    };
190}
191
192impl_from_arrow_primitive!(Int8Type);
193impl_from_arrow_primitive!(Int16Type);
194impl_from_arrow_primitive!(Int32Type);
195impl_from_arrow_primitive!(Int64Type);
196impl_from_arrow_primitive!(UInt8Type);
197impl_from_arrow_primitive!(UInt16Type);
198impl_from_arrow_primitive!(UInt32Type);
199impl_from_arrow_primitive!(UInt64Type);
200impl_from_arrow_primitive!(Float16Type);
201impl_from_arrow_primitive!(Float32Type);
202impl_from_arrow_primitive!(Float64Type);
203
204/// Zero-copy conversion of an Arrow `Decimal32` array into a Vortex decimal array.
205pub fn from_arrow_decimal32(
206    array: &ArrowPrimitiveArray<Decimal32Type>,
207    nullable: bool,
208) -> VortexResult<ArrayRef> {
209    let decimal_type = DecimalDType::new(array.precision(), array.scale());
210    let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone());
211    let validity = nulls(array.nulls(), nullable)?;
212    Ok(DecimalArray::new(buffer, decimal_type, validity).into_array())
213}
214
215impl FromArrowArray<&ArrowPrimitiveArray<Decimal32Type>> for ArrayRef {
216    fn from_arrow(
217        array: &ArrowPrimitiveArray<Decimal32Type>,
218        nullable: bool,
219    ) -> VortexResult<Self> {
220        from_arrow_decimal32(array, nullable)
221    }
222}
223
224/// Zero-copy conversion of an Arrow `Decimal64` array into a Vortex decimal array.
225pub fn from_arrow_decimal64(
226    array: &ArrowPrimitiveArray<Decimal64Type>,
227    nullable: bool,
228) -> VortexResult<ArrayRef> {
229    let decimal_type = DecimalDType::new(array.precision(), array.scale());
230    let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone());
231    let validity = nulls(array.nulls(), nullable)?;
232    Ok(DecimalArray::new(buffer, decimal_type, validity).into_array())
233}
234
235impl FromArrowArray<&ArrowPrimitiveArray<Decimal64Type>> for ArrayRef {
236    fn from_arrow(
237        array: &ArrowPrimitiveArray<Decimal64Type>,
238        nullable: bool,
239    ) -> VortexResult<Self> {
240        from_arrow_decimal64(array, nullable)
241    }
242}
243
244/// Zero-copy conversion of an Arrow `Decimal128` array into a Vortex decimal array.
245pub fn from_arrow_decimal128(
246    array: &ArrowPrimitiveArray<Decimal128Type>,
247    nullable: bool,
248) -> VortexResult<ArrayRef> {
249    let decimal_type = DecimalDType::new(array.precision(), array.scale());
250    let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone());
251    let validity = nulls(array.nulls(), nullable)?;
252    Ok(DecimalArray::new(buffer, decimal_type, validity).into_array())
253}
254
255impl FromArrowArray<&ArrowPrimitiveArray<Decimal128Type>> for ArrayRef {
256    fn from_arrow(
257        array: &ArrowPrimitiveArray<Decimal128Type>,
258        nullable: bool,
259    ) -> VortexResult<Self> {
260        from_arrow_decimal128(array, nullable)
261    }
262}
263
264/// Zero-copy conversion of an Arrow `Decimal256` array into a Vortex decimal array.
265pub fn from_arrow_decimal256(
266    array: &ArrowPrimitiveArray<Decimal256Type>,
267    nullable: bool,
268) -> VortexResult<ArrayRef> {
269    let decimal_type = DecimalDType::new(array.precision(), array.scale());
270    let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone());
271    // SAFETY: Our i256 implementation has the same bit-pattern representation of the
272    //  arrow_buffer::i256 type. It is safe to treat values held inside the buffer as values
273    //  of either type.
274    let buffer = unsafe { std::mem::transmute::<Buffer<arrow_buffer::i256>, Buffer<i256>>(buffer) };
275    let validity = nulls(array.nulls(), nullable)?;
276    Ok(DecimalArray::new(buffer, decimal_type, validity).into_array())
277}
278
279impl FromArrowArray<&ArrowPrimitiveArray<Decimal256Type>> for ArrayRef {
280    fn from_arrow(
281        array: &ArrowPrimitiveArray<Decimal256Type>,
282        nullable: bool,
283    ) -> VortexResult<Self> {
284        from_arrow_decimal256(array, nullable)
285    }
286}
287
288macro_rules! impl_from_arrow_temporal {
289    ($T:path) => {
290        impl FromArrowArray<&ArrowPrimitiveArray<$T>> for ArrayRef {
291            fn from_arrow(
292                value: &ArrowPrimitiveArray<$T>,
293                nullable: bool,
294            ) -> vortex_error::VortexResult<Self> {
295                from_arrow_temporal(value, nullable)
296            }
297        }
298    };
299}
300
301// timestamp
302impl_from_arrow_temporal!(TimestampSecondType);
303impl_from_arrow_temporal!(TimestampMillisecondType);
304impl_from_arrow_temporal!(TimestampMicrosecondType);
305impl_from_arrow_temporal!(TimestampNanosecondType);
306
307// time
308impl_from_arrow_temporal!(Time32SecondType);
309impl_from_arrow_temporal!(Time32MillisecondType);
310impl_from_arrow_temporal!(Time64MicrosecondType);
311impl_from_arrow_temporal!(Time64NanosecondType);
312
313// date
314impl_from_arrow_temporal!(Date32Type);
315impl_from_arrow_temporal!(Date64Type);
316
317/// Conversion of an Arrow temporal array (timestamp/date/time) into a Vortex temporal
318/// extension array.
319pub fn from_arrow_temporal<T: ArrowPrimitiveType>(
320    value: &ArrowPrimitiveArray<T>,
321    nullable: bool,
322) -> VortexResult<ArrayRef>
323where
324    T::Native: NativePType,
325{
326    let arr = PrimitiveArray::new(
327        Buffer::from_arrow_scalar_buffer(value.values().clone()),
328        nulls(value.nulls(), nullable)?,
329    )
330    .into_array();
331
332    Ok(match value.data_type() {
333        DataType::Timestamp(time_unit, tz) => {
334            TemporalArray::new_timestamp(arr, from_arrow_time_unit(*time_unit), tz.clone()).into()
335        }
336        DataType::Time32(time_unit) => {
337            TemporalArray::new_time(arr, from_arrow_time_unit(*time_unit)).into()
338        }
339        DataType::Time64(time_unit) => {
340            TemporalArray::new_time(arr, from_arrow_time_unit(*time_unit)).into()
341        }
342        DataType::Date32 => TemporalArray::new_date(arr, TimeUnit::Days).into(),
343        DataType::Date64 => TemporalArray::new_date(arr, TimeUnit::Milliseconds).into(),
344        DataType::Duration(_) | DataType::Interval(_) => {
345            vortex_bail!("Arrow {} arrays are not supported", value.data_type())
346        }
347        _ => vortex_panic!("Invalid temporal type: {}", value.data_type()),
348    })
349}
350
351/// Rebase a (possibly sliced) Arrow offset buffer to start at zero.
352///
353/// A sliced Arrow string, binary or list array keeps its parent's whole child,
354/// so the returned range selects just the child data its rows reference.
355pub(crate) fn trim_offsets<O: OffsetSizeTrait>(
356    offsets: &OffsetBuffer<O>,
357) -> (OffsetBuffer<O>, Range<usize>) {
358    let first = offsets[0];
359    let referenced = first.as_usize()..offsets[offsets.len() - 1].as_usize();
360    if first.as_usize() == 0 {
361        return (offsets.clone(), referenced);
362    }
363    let rebased = OffsetBuffer::new(offsets.iter().map(|&offset| offset - first).collect());
364    (rebased, referenced)
365}
366
367/// Conversion of an Arrow (large) string/binary array into a Vortex `VarBin` array, sharing its
368/// bytes.
369pub fn from_arrow_bytes<T: ByteArrayType>(
370    value: &GenericByteArray<T>,
371    nullable: bool,
372) -> VortexResult<ArrayRef>
373where
374    <T as ByteArrayType>::Offset: IntegerPType,
375{
376    let dtype = match T::DATA_TYPE {
377        DataType::Binary | DataType::LargeBinary => DType::Binary(nullable.into()),
378        DataType::Utf8 | DataType::LargeUtf8 => DType::Utf8(nullable.into()),
379        dt => vortex_panic!("Invalid data type for ByteArray: {dt}"),
380    };
381    let (offsets, referenced) = trim_offsets(value.offsets());
382    let bytes = value
383        .values()
384        .slice_with_length(referenced.start, referenced.len());
385    // SAFETY: Arrow arrays are already validated (valid UTF-8, valid offsets, correct validity).
386    Ok(unsafe {
387        VarBinArray::new_unchecked(
388            offsets.into_array(),
389            ByteBuffer::from_arrow_buffer(bytes, Alignment::of::<u8>()),
390            dtype,
391            nulls(value.nulls(), nullable)?,
392        )
393    }
394    .into_array())
395}
396
397impl<T: ByteArrayType> FromArrowArray<&GenericByteArray<T>> for ArrayRef
398where
399    <T as ByteArrayType>::Offset: IntegerPType,
400{
401    fn from_arrow(value: &GenericByteArray<T>, nullable: bool) -> VortexResult<Self> {
402        from_arrow_bytes(value, nullable)
403    }
404}
405
406/// Zero-copy conversion of an Arrow string/binary view array into a Vortex `VarBinView` array.
407pub fn from_arrow_byte_view<T: ByteViewType>(
408    value: &GenericByteViewArray<T>,
409    nullable: bool,
410) -> VortexResult<ArrayRef> {
411    let dtype = match T::DATA_TYPE {
412        DataType::BinaryView => DType::Binary(nullable.into()),
413        DataType::Utf8View => DType::Utf8(nullable.into()),
414        dt => vortex_panic!("Invalid data type for ByteViewArray: {dt}"),
415    };
416
417    let views_buffer = Buffer::from_byte_buffer(
418        Buffer::from_arrow_scalar_buffer(value.views().clone()).into_byte_buffer(),
419    );
420
421    // SAFETY: arrow-rs ByteViewArray already checks the same invariants, we inherit those
422    //  guarantees by zero-copy constructing from one.
423    Ok(unsafe {
424        VarBinViewArray::new_unchecked(
425            views_buffer,
426            Arc::from(
427                value
428                    .data_buffers()
429                    .iter()
430                    .map(|b| ByteBuffer::from_arrow_buffer(b.clone(), Alignment::of::<u8>()))
431                    .collect::<Vec<_>>(),
432            ),
433            dtype,
434            nulls(value.nulls(), nullable)?,
435        )
436        .into_array()
437    })
438}
439
440impl<T: ByteViewType> FromArrowArray<&GenericByteViewArray<T>> for ArrayRef {
441    fn from_arrow(value: &GenericByteViewArray<T>, nullable: bool) -> VortexResult<Self> {
442        from_arrow_byte_view(value, nullable)
443    }
444}
445
446/// Zero-copy conversion of an Arrow boolean array into a Vortex `Bool` array.
447pub fn from_arrow_boolean(value: &ArrowBooleanArray, nullable: bool) -> VortexResult<ArrayRef> {
448    Ok(BoolArray::new(
449        value.values().clone().into(),
450        nulls(value.nulls(), nullable)?,
451    )
452    .into_array())
453}
454
455impl FromArrowArray<&ArrowBooleanArray> for ArrayRef {
456    fn from_arrow(value: &ArrowBooleanArray, nullable: bool) -> VortexResult<Self> {
457        from_arrow_boolean(value, nullable)
458    }
459}
460
461/// Strip out the nulls from this array and return a new array without nulls.
462pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> VortexResult<arrow_data::ArrayData> {
463    if data.null_count() == 0 {
464        // No nulls to remove, return the array as is
465        return Ok(data);
466    }
467
468    let children = match data.data_type() {
469        DataType::Struct(fields) => Some(
470            fields
471                .iter()
472                .zip(data.child_data().iter())
473                .map(|(field, child_data)| {
474                    if field.is_nullable() {
475                        Ok(child_data.clone())
476                    } else {
477                        remove_nulls(child_data.clone())
478                    }
479                })
480                .collect::<VortexResult<Vec<_>>>()?,
481        ),
482        DataType::List(f)
483        | DataType::LargeList(f)
484        | DataType::ListView(f)
485        | DataType::LargeListView(f)
486        | DataType::FixedSizeList(f, _)
487            if !f.is_nullable() =>
488        {
489            // All list types only have one child
490            vortex_ensure_eq!(
491                data.child_data().len(),
492                1,
493                "List types should have one child"
494            );
495            Some(vec![remove_nulls(data.child_data()[0].clone())?])
496        }
497        _ => None,
498    };
499
500    let mut builder = data.into_builder().nulls(None);
501    if let Some(children) = children {
502        builder = builder.child_data(children);
503    }
504    builder
505        .build()
506        .map_err(|e| vortex_err!("Failed to reconstruct Arrow array without nulls: {e}"))
507}
508
509/// Conversion of an Arrow struct array into a Vortex `Struct` array, converting each column.
510pub fn from_arrow_struct(value: &ArrowStructArray, nullable: bool) -> VortexResult<ArrayRef> {
511    Ok(StructArray::try_new(
512        value.column_names().iter().copied().collect(),
513        value
514            .columns()
515            .iter()
516            .zip(value.fields())
517            .map(|(c, field)| {
518                // Arrow pushes down nulls, even into non-nullable fields. So we strip them
519                // out here because Vortex is a little more strict.
520                if c.null_count() > 0 && !field.is_nullable() {
521                    let stripped = make_array(remove_nulls(c.into_data())?);
522                    from_arrow_dyn(stripped.as_ref(), false)
523                } else {
524                    from_arrow_dyn(c.as_ref(), field.is_nullable())
525                }
526            })
527            .collect::<VortexResult<Vec<_>>>()?,
528        value.len(),
529        nulls(value.nulls(), nullable)?,
530    )?
531    .into_array())
532}
533
534impl FromArrowArray<&ArrowStructArray> for ArrayRef {
535    fn from_arrow(value: &ArrowStructArray, nullable: bool) -> VortexResult<Self> {
536        from_arrow_struct(value, nullable)
537    }
538}
539
540/// Conversion of an Arrow (large) list array into a Vortex `List` array.
541pub fn from_arrow_list<O: IntegerPType + OffsetSizeTrait>(
542    value: &GenericListArray<O>,
543    nullable: bool,
544) -> VortexResult<ArrayRef> {
545    // Extract the validity of the underlying element array.
546    let elements_are_nullable = match value.data_type() {
547        DataType::List(field) => field.is_nullable(),
548        DataType::LargeList(field) => field.is_nullable(),
549        dt => vortex_panic!("Invalid data type for ListArray: {dt}"),
550    };
551
552    let (offsets, referenced) = trim_offsets(value.offsets());
553    let elements = from_arrow_dyn(
554        value
555            .values()
556            .slice(referenced.start, referenced.len())
557            .as_ref(),
558        elements_are_nullable,
559    )?;
560    let nulls = nulls(value.nulls(), nullable)?;
561
562    // `offsets` are always non-nullable.
563    Ok(ListArray::try_new(elements, offsets.into_array(), nulls)?.into_array())
564}
565
566impl<O: IntegerPType + OffsetSizeTrait> FromArrowArray<&GenericListArray<O>> for ArrayRef {
567    fn from_arrow(value: &GenericListArray<O>, nullable: bool) -> VortexResult<Self> {
568        from_arrow_list(value, nullable)
569    }
570}
571
572/// Conversion of an Arrow (large) list-view array into a Vortex `ListView` array.
573pub fn from_arrow_list_view<O: OffsetSizeTrait + NativePType>(
574    array: &GenericListViewArray<O>,
575    nullable: bool,
576) -> VortexResult<ArrayRef> {
577    // Extract the validity of the underlying element array.
578    let elements_are_nullable = match array.data_type() {
579        DataType::ListView(field) => field.is_nullable(),
580        DataType::LargeListView(field) => field.is_nullable(),
581        dt => vortex_panic!("Invalid data type for ListViewArray: {dt}"),
582    };
583
584    let elements = from_arrow_dyn(array.values().as_ref(), elements_are_nullable)?;
585
586    // `offsets` and `sizes` are always non-nullable.
587    let offsets = array.offsets().clone().into_array();
588    let sizes = array.sizes().clone().into_array();
589    let nulls = nulls(array.nulls(), nullable)?;
590
591    Ok(ListViewArray::try_new(elements, offsets, sizes, nulls)?.into_array())
592}
593
594impl<O: OffsetSizeTrait + NativePType> FromArrowArray<&GenericListViewArray<O>> for ArrayRef {
595    fn from_arrow(array: &GenericListViewArray<O>, nullable: bool) -> VortexResult<Self> {
596        from_arrow_list_view(array, nullable)
597    }
598}
599
600/// Conversion of an Arrow fixed-size list array into a Vortex `FixedSizeList` array.
601pub fn from_arrow_fixed_size_list(
602    array: &ArrowFixedSizeListArray,
603    nullable: bool,
604) -> VortexResult<ArrayRef> {
605    let DataType::FixedSizeList(field, list_size) = array.data_type() else {
606        vortex_panic!("Invalid data type for ListArray: {}", array.data_type());
607    };
608
609    Ok(FixedSizeListArray::try_new(
610        from_arrow_dyn(array.values().as_ref(), field.is_nullable())?,
611        *list_size as u32,
612        nulls(array.nulls(), nullable)?,
613        array.len(),
614    )?
615    .into_array())
616}
617
618impl FromArrowArray<&ArrowFixedSizeListArray> for ArrayRef {
619    fn from_arrow(array: &ArrowFixedSizeListArray, nullable: bool) -> VortexResult<Self> {
620        from_arrow_fixed_size_list(array, nullable)
621    }
622}
623
624pub(crate) fn map_from_arrow_parts(
625    entries: ArrayRef,
626    offsets: &OffsetBuffer<i32>,
627    arrow_nulls: Option<&NullBuffer>,
628    keys_sorted: bool,
629    nullable: bool,
630) -> VortexResult<ArrayRef> {
631    let DType::Struct(struct_dtype, Nullability::NonNullable) = entries.dtype() else {
632        vortex_bail!(
633            "Arrow map entries must import as non-nullable struct, got {}",
634            entries.dtype()
635        );
636    };
637    vortex_ensure_eq!(
638        struct_dtype.nfields(),
639        2,
640        "Arrow map entries struct must contain exactly two fields"
641    );
642
643    let key_dtype = struct_dtype
644        .field_by_index(0)
645        .ok_or_else(|| vortex_err!("Arrow map entries struct missing key field"))?;
646    vortex_ensure!(
647        !key_dtype.is_nullable(),
648        "Arrow map key field must be non-nullable"
649    );
650    let value_dtype = struct_dtype
651        .field_by_index(1)
652        .ok_or_else(|| vortex_err!("Arrow map entries struct missing value field"))?;
653    let map_dtype = MapDType::try_new(key_dtype, value_dtype, keys_sorted)?;
654    let entries_struct = entries.as_::<Struct>();
655    let entries = StructArray::try_new(
656        FieldNames::from(["key", "value"]),
657        vec![
658            entries_struct.unmasked_field(0).clone(),
659            entries_struct.unmasked_field(1).clone(),
660        ],
661        entries.len(),
662        Validity::NonNullable,
663    )?
664    .into_array();
665
666    let len = offsets.len() - 1;
667    let sizes = Buffer::<i32>::from_iter(offsets.windows(2).map(|window| window[1] - window[0]))
668        .into_array();
669    let offsets = offsets.inner().slice(0, len).into_array();
670    let validity = nulls(arrow_nulls, nullable)?;
671    let entries = ListViewArray::try_new(entries, offsets, sizes, validity)?;
672    // SAFETY: Arrow Map offsets are sorted list offsets. Vortex sizes are adjacent offset
673    // differences, so every view's end equals the next view's start. Sliced Arrow arrays may
674    // leave leading or trailing entries unreferenced, which zero-copy-to-list permits.
675    let entries = unsafe { entries.with_zero_copy_to_list(true) };
676
677    Ok(MapArray::try_new(map_dtype, entries)?.into_array())
678}
679
680/// Conversion of an Arrow map array into a Vortex `Map` array.
681pub fn from_arrow_map(array: &ArrowMapArray, nullable: bool) -> VortexResult<ArrayRef> {
682    let DataType::Map(_, keys_sorted) = array.data_type() else {
683        vortex_panic!("Invalid data type for MapArray: {}", array.data_type());
684    };
685    let entries = from_arrow_struct(array.entries(), false)?;
686    map_from_arrow_parts(
687        entries,
688        array.offsets(),
689        array.nulls(),
690        *keys_sorted,
691        nullable,
692    )
693}
694
695impl FromArrowArray<&ArrowMapArray> for ArrayRef {
696    fn from_arrow(array: &ArrowMapArray, nullable: bool) -> VortexResult<Self> {
697        from_arrow_map(array, nullable)
698    }
699}
700
701/// Conversion of an Arrow null array into a Vortex `Null` array.
702pub fn from_arrow_null(value: &ArrowNullArray, nullable: bool) -> VortexResult<ArrayRef> {
703    vortex_ensure!(
704        nullable,
705        "Cannot convert an Arrow NullArray into a non-nullable Vortex array"
706    );
707    Ok(NullArray::new(value.len()).into_array())
708}
709
710impl FromArrowArray<&ArrowNullArray> for ArrayRef {
711    fn from_arrow(value: &ArrowNullArray, nullable: bool) -> VortexResult<Self> {
712        from_arrow_null(value, nullable)
713    }
714}
715
716/// Conversion of an Arrow dictionary array into a Vortex `Dict` array.
717pub fn from_arrow_dictionary<K: ArrowDictionaryKeyType>(
718    array: &DictionaryArray<K>,
719    nullable: bool,
720) -> VortexResult<DictArray> {
721    let keys = AnyDictionaryArray::keys(array);
722    let keys = from_arrow_dyn(keys, keys.is_nullable())?;
723    let values = from_arrow_dyn(array.values().as_ref(), nullable)?;
724    // SAFETY: we assume that Arrow has checked the invariants on construction.
725    Ok(unsafe { DictArray::new_unchecked(keys, values) })
726}
727
728impl<K: ArrowDictionaryKeyType> FromArrowArray<&DictionaryArray<K>> for DictArray {
729    fn from_arrow(array: &DictionaryArray<K>, nullable: bool) -> VortexResult<Self> {
730        from_arrow_dictionary(array, nullable)
731    }
732}
733
734/// Convert an Arrow [`NullBuffer`] into a Vortex [`Validity`] for an array of the given
735/// nullability.
736///
737/// # Errors
738///
739/// Returns an error if `nullable` is false but the null buffer contains nulls.
740pub fn nulls(nulls: Option<&NullBuffer>, nullable: bool) -> VortexResult<Validity> {
741    if nullable {
742        Ok(nulls
743            .map(|nulls| {
744                if nulls.null_count() == nulls.len() {
745                    Validity::AllInvalid
746                } else {
747                    Validity::from(BitBuffer::from(nulls.inner().clone()))
748                }
749            })
750            .unwrap_or(Validity::AllValid))
751    } else {
752        let null_count = nulls.map(NullBuffer::null_count).unwrap_or(0);
753        vortex_ensure_eq!(
754            null_count,
755            0,
756            "Cannot convert an Arrow array containing nulls into a non-nullable Vortex array"
757        );
758        Ok(Validity::NonNullable)
759    }
760}
761
762/// Canonical conversion of any supported Arrow array into a Vortex array, dispatching on the
763/// Arrow [`DataType`].
764pub fn from_arrow_dyn(array: &dyn ArrowArray, nullable: bool) -> VortexResult<ArrayRef> {
765    match array.data_type() {
766        DataType::Boolean => from_arrow_boolean(array.as_boolean(), nullable),
767        DataType::UInt8 => from_arrow_primitive(array.as_primitive::<UInt8Type>(), nullable),
768        DataType::UInt16 => from_arrow_primitive(array.as_primitive::<UInt16Type>(), nullable),
769        DataType::UInt32 => from_arrow_primitive(array.as_primitive::<UInt32Type>(), nullable),
770        DataType::UInt64 => from_arrow_primitive(array.as_primitive::<UInt64Type>(), nullable),
771        DataType::Int8 => from_arrow_primitive(array.as_primitive::<Int8Type>(), nullable),
772        DataType::Int16 => from_arrow_primitive(array.as_primitive::<Int16Type>(), nullable),
773        DataType::Int32 => from_arrow_primitive(array.as_primitive::<Int32Type>(), nullable),
774        DataType::Int64 => from_arrow_primitive(array.as_primitive::<Int64Type>(), nullable),
775        DataType::Float16 => from_arrow_primitive(array.as_primitive::<Float16Type>(), nullable),
776        DataType::Float32 => from_arrow_primitive(array.as_primitive::<Float32Type>(), nullable),
777        DataType::Float64 => from_arrow_primitive(array.as_primitive::<Float64Type>(), nullable),
778        DataType::Utf8 => from_arrow_bytes(array.as_string::<i32>(), nullable),
779        DataType::LargeUtf8 => from_arrow_bytes(array.as_string::<i64>(), nullable),
780        DataType::Binary => from_arrow_bytes(array.as_binary::<i32>(), nullable),
781        DataType::LargeBinary => from_arrow_bytes(array.as_binary::<i64>(), nullable),
782        DataType::BinaryView => from_arrow_byte_view(array.as_binary_view(), nullable),
783        DataType::Utf8View => from_arrow_byte_view(array.as_string_view(), nullable),
784        DataType::Struct(_) => from_arrow_struct(array.as_struct(), nullable),
785        DataType::List(_) => from_arrow_list(array.as_list::<i32>(), nullable),
786        DataType::LargeList(_) => from_arrow_list(array.as_list::<i64>(), nullable),
787        DataType::ListView(_) => from_arrow_list_view(array.as_list_view::<i32>(), nullable),
788        DataType::LargeListView(_) => from_arrow_list_view(array.as_list_view::<i64>(), nullable),
789        DataType::FixedSizeList(..) => {
790            from_arrow_fixed_size_list(array.as_fixed_size_list(), nullable)
791        }
792        DataType::Map(..) => from_arrow_map(array.as_map(), nullable),
793        DataType::Null => from_arrow_null(as_null_array(array), nullable),
794        DataType::Timestamp(u, _) => match u {
795            ArrowTimeUnit::Second => {
796                from_arrow_temporal(array.as_primitive::<TimestampSecondType>(), nullable)
797            }
798            ArrowTimeUnit::Millisecond => {
799                from_arrow_temporal(array.as_primitive::<TimestampMillisecondType>(), nullable)
800            }
801            ArrowTimeUnit::Microsecond => {
802                from_arrow_temporal(array.as_primitive::<TimestampMicrosecondType>(), nullable)
803            }
804            ArrowTimeUnit::Nanosecond => {
805                from_arrow_temporal(array.as_primitive::<TimestampNanosecondType>(), nullable)
806            }
807        },
808        DataType::Date32 => from_arrow_temporal(array.as_primitive::<Date32Type>(), nullable),
809        DataType::Date64 => from_arrow_temporal(array.as_primitive::<Date64Type>(), nullable),
810        DataType::Time32(u) => match u {
811            ArrowTimeUnit::Second => {
812                from_arrow_temporal(array.as_primitive::<Time32SecondType>(), nullable)
813            }
814            ArrowTimeUnit::Millisecond => {
815                from_arrow_temporal(array.as_primitive::<Time32MillisecondType>(), nullable)
816            }
817            ArrowTimeUnit::Microsecond | ArrowTimeUnit::Nanosecond => unreachable!(),
818        },
819        DataType::Time64(u) => match u {
820            ArrowTimeUnit::Microsecond => {
821                from_arrow_temporal(array.as_primitive::<Time64MicrosecondType>(), nullable)
822            }
823            ArrowTimeUnit::Nanosecond => {
824                from_arrow_temporal(array.as_primitive::<Time64NanosecondType>(), nullable)
825            }
826            ArrowTimeUnit::Second | ArrowTimeUnit::Millisecond => unreachable!(),
827        },
828        DataType::Decimal32(..) => {
829            from_arrow_decimal32(array.as_primitive::<Decimal32Type>(), nullable)
830        }
831        DataType::Decimal64(..) => {
832            from_arrow_decimal64(array.as_primitive::<Decimal64Type>(), nullable)
833        }
834        DataType::Decimal128(..) => {
835            from_arrow_decimal128(array.as_primitive::<Decimal128Type>(), nullable)
836        }
837        DataType::Decimal256(..) => {
838            from_arrow_decimal256(array.as_primitive::<Decimal256Type>(), nullable)
839        }
840        DataType::Dictionary(key_type, _) => match key_type.as_ref() {
841            DataType::Int8 => Ok(from_arrow_dictionary(
842                array.as_dictionary::<Int8Type>(),
843                nullable,
844            )?
845            .into_array()),
846            DataType::Int16 => Ok(from_arrow_dictionary(
847                array.as_dictionary::<Int16Type>(),
848                nullable,
849            )?
850            .into_array()),
851            DataType::Int32 => Ok(from_arrow_dictionary(
852                array.as_dictionary::<Int32Type>(),
853                nullable,
854            )?
855            .into_array()),
856            DataType::Int64 => Ok(from_arrow_dictionary(
857                array.as_dictionary::<Int64Type>(),
858                nullable,
859            )?
860            .into_array()),
861            DataType::UInt8 => Ok(from_arrow_dictionary(
862                array.as_dictionary::<UInt8Type>(),
863                nullable,
864            )?
865            .into_array()),
866            DataType::UInt16 => Ok(from_arrow_dictionary(
867                array.as_dictionary::<UInt16Type>(),
868                nullable,
869            )?
870            .into_array()),
871            DataType::UInt32 => Ok(from_arrow_dictionary(
872                array.as_dictionary::<UInt32Type>(),
873                nullable,
874            )?
875            .into_array()),
876            DataType::UInt64 => Ok(from_arrow_dictionary(
877                array.as_dictionary::<UInt64Type>(),
878                nullable,
879            )?
880            .into_array()),
881            key_dt => vortex_bail!("Unsupported dictionary key type: {key_dt}"),
882        },
883        dt => vortex_bail!("Array encoding not implemented for Arrow data type {dt}"),
884    }
885}
886
887impl FromArrowArray<&dyn ArrowArray> for ArrayRef {
888    fn from_arrow(array: &dyn ArrowArray, nullable: bool) -> VortexResult<Self> {
889        from_arrow_dyn(array, nullable)
890    }
891}
892
893/// Canonical conversion of an Arrow [`RecordBatch`] into a Vortex struct array.
894pub fn from_arrow_batch(batch: &RecordBatch, nullable: bool) -> VortexResult<ArrayRef> {
895    from_arrow_struct(&arrow_array::StructArray::from(batch.clone()), nullable)
896}
897
898impl FromArrowArray<RecordBatch> for ArrayRef {
899    fn from_arrow(array: RecordBatch, nullable: bool) -> VortexResult<Self> {
900        from_arrow_batch(&array, nullable)
901    }
902}
903
904impl FromArrowArray<&RecordBatch> for ArrayRef {
905    fn from_arrow(array: &RecordBatch, nullable: bool) -> VortexResult<Self> {
906        from_arrow_batch(array, nullable)
907    }
908}
909
910#[cfg(test)]
911mod tests {
912    use std::ops::Range;
913    use std::sync::Arc;
914
915    use arrow_array::Array as ArrowArray;
916    use arrow_array::BinaryArray;
917    use arrow_array::BooleanArray;
918    use arrow_array::Date32Array;
919    use arrow_array::Date64Array;
920    use arrow_array::FixedSizeListArray as ArrowFixedSizeListArray;
921    use arrow_array::Float32Array;
922    use arrow_array::Float64Array;
923    use arrow_array::GenericListViewArray;
924    use arrow_array::Int8Array;
925    use arrow_array::Int16Array;
926    use arrow_array::Int32Array;
927    use arrow_array::Int64Array;
928    use arrow_array::LargeBinaryArray;
929    use arrow_array::LargeStringArray;
930    use arrow_array::NullArray;
931    use arrow_array::RecordBatch;
932    use arrow_array::StringArray;
933    use arrow_array::StructArray;
934    use arrow_array::Time32MillisecondArray;
935    use arrow_array::Time32SecondArray;
936    use arrow_array::Time64MicrosecondArray;
937    use arrow_array::Time64NanosecondArray;
938    use arrow_array::TimestampMicrosecondArray;
939    use arrow_array::TimestampMillisecondArray;
940    use arrow_array::TimestampNanosecondArray;
941    use arrow_array::TimestampSecondArray;
942    use arrow_array::UInt8Array;
943    use arrow_array::UInt16Array;
944    use arrow_array::UInt32Array;
945    use arrow_array::UInt64Array;
946    use arrow_array::builder::BinaryViewBuilder;
947    use arrow_array::builder::Decimal128Builder;
948    use arrow_array::builder::Decimal256Builder;
949    use arrow_array::builder::Int32Builder;
950    use arrow_array::builder::Int64Builder;
951    use arrow_array::builder::LargeListBuilder;
952    use arrow_array::builder::ListBuilder;
953    use arrow_array::builder::MapBuilder as ArrowMapBuilder;
954    use arrow_array::builder::StringBuilder;
955    use arrow_array::builder::StringViewBuilder;
956    use arrow_array::new_null_array;
957    use arrow_array::types::ArrowPrimitiveType;
958    use arrow_array::types::Float16Type;
959    use arrow_buffer::BooleanBuffer;
960    use arrow_buffer::Buffer as ArrowBuffer;
961    use arrow_buffer::OffsetBuffer;
962    use arrow_buffer::ScalarBuffer;
963    use arrow_schema::DataType;
964    use arrow_schema::Field;
965    use arrow_schema::Fields;
966    use arrow_schema::Schema;
967    use rstest::rstest;
968    use vortex_array::ArrayRef;
969    use vortex_array::VortexSessionExecute as _;
970    use vortex_array::array_session;
971    use vortex_array::arrays::Decimal;
972    use vortex_array::arrays::FixedSizeList;
973    use vortex_array::arrays::List;
974    use vortex_array::arrays::ListView;
975    use vortex_array::arrays::Primitive;
976    use vortex_array::arrays::Struct;
977    use vortex_array::arrays::VarBinView;
978    use vortex_array::arrays::fixed_size_list::FixedSizeListArrayExt;
979    use vortex_array::arrays::fixed_size_list::FixedSizeListArraySlotsExt;
980    use vortex_array::arrays::list::ListArraySlotsExt;
981    use vortex_array::arrays::listview::ListViewArraySlotsExt;
982    use vortex_array::arrays::struct_::StructArrayExt;
983    use vortex_array::assert_arrays_eq;
984    use vortex_array::dtype::DType;
985    use vortex_array::dtype::Nullability;
986    use vortex_array::dtype::PType;
987    use vortex_array::extension::datetime::TimeUnit;
988    use vortex_array::extension::datetime::Timestamp;
989    use vortex_error::VortexResult;
990
991    use crate::FromArrowArray as _;
992    use crate::IntoVortexArray as _;
993    use crate::convert::from_arrow_batch;
994    use crate::convert::from_arrow_dyn;
995
996    #[rstest]
997    #[case::i8(
998        Arc::new(Int8Array::from(vec![Some(1), None, Some(3), Some(4)])),
999        Arc::new(Int8Array::from(vec![1, 2, 3, 4])),
1000        PType::I8,
1001    )]
1002    #[case::i16(
1003        Arc::new(Int16Array::from(vec![Some(100), None, Some(300), Some(400)])),
1004        Arc::new(Int16Array::from(vec![100, 200, 300, 400])),
1005        PType::I16,
1006    )]
1007    #[case::i32(
1008        Arc::new(Int32Array::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1009        Arc::new(Int32Array::from(vec![1000, 2000, 3000, 4000])),
1010        PType::I32,
1011    )]
1012    #[case::i64(
1013        Arc::new(Int64Array::from(vec![Some(10000), None, Some(30000), Some(40000)])),
1014        Arc::new(Int64Array::from(vec![10000_i64, 20000, 30000, 40000])),
1015        PType::I64,
1016    )]
1017    #[case::u8(
1018        Arc::new(UInt8Array::from(vec![Some(1), None, Some(3), Some(4)])),
1019        Arc::new(UInt8Array::from(vec![1_u8, 2, 3, 4])),
1020        PType::U8,
1021    )]
1022    #[case::u16(
1023        Arc::new(UInt16Array::from(vec![Some(100), None, Some(300), Some(400)])),
1024        Arc::new(UInt16Array::from(vec![100_u16, 200, 300, 400])),
1025        PType::U16,
1026    )]
1027    #[case::u32(
1028        Arc::new(UInt32Array::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1029        Arc::new(UInt32Array::from(vec![1000_u32, 2000, 3000, 4000])),
1030        PType::U32,
1031    )]
1032    #[case::u64(
1033        Arc::new(UInt64Array::from(vec![Some(10000), None, Some(30000), Some(40000)])),
1034        Arc::new(UInt64Array::from(vec![10000_u64, 20000, 30000, 40000])),
1035        PType::U64,
1036    )]
1037    #[case::f32(
1038        Arc::new(Float32Array::from(vec![Some(1.5), None, Some(3.5), Some(4.5)])),
1039        Arc::new(Float32Array::from(vec![1.5_f32, 2.5, 3.5, 4.5])),
1040        PType::F32,
1041    )]
1042    #[case::f64(
1043        Arc::new(Float64Array::from(vec![Some(1.5), None, Some(3.5), Some(4.5)])),
1044        Arc::new(Float64Array::from(vec![1.5_f64, 2.5, 3.5, 4.5])),
1045        PType::F64,
1046    )]
1047    fn test_primitive_array_conversion(
1048        #[case] nullable: Arc<dyn ArrowArray>,
1049        #[case] non_nullable: Arc<dyn ArrowArray>,
1050        #[case] expected_ptype: PType,
1051    ) {
1052        let v_null = ArrayRef::from_arrow(nullable.as_ref(), true).unwrap();
1053        let v_non_null = ArrayRef::from_arrow(non_nullable.as_ref(), false).unwrap();
1054        assert_eq!(v_null.len(), 4);
1055        assert_eq!(v_non_null.len(), 4);
1056        assert_eq!(v_null.as_::<Primitive>().ptype(), expected_ptype);
1057        assert_eq!(v_non_null.as_::<Primitive>().ptype(), expected_ptype);
1058    }
1059
1060    #[test]
1061    fn test_float16_array_conversion() {
1062        let values = vec![
1063            Some(<Float16Type as ArrowPrimitiveType>::Native::from_f32(1.5)),
1064            None,
1065            Some(<Float16Type as ArrowPrimitiveType>::Native::from_f32(3.5)),
1066        ];
1067        let arrow_array = arrow_array::PrimitiveArray::<Float16Type>::from(values);
1068        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1069
1070        let non_null_values = vec![
1071            <Float16Type as ArrowPrimitiveType>::Native::from_f32(1.5),
1072            <Float16Type as ArrowPrimitiveType>::Native::from_f32(2.5),
1073        ];
1074        let arrow_array_non_null =
1075            arrow_array::PrimitiveArray::<Float16Type>::from(non_null_values);
1076        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1077
1078        assert_eq!(vortex_array.len(), 3);
1079        assert_eq!(vortex_array_non_null.len(), 2);
1080
1081        // Verify metadata - should be PrimitiveArray with F16 ptype
1082        let primitive_array = vortex_array.as_::<Primitive>();
1083        assert_eq!(primitive_array.ptype(), PType::F16);
1084
1085        let primitive_array_non_null = vortex_array_non_null.as_::<Primitive>();
1086        assert_eq!(primitive_array_non_null.ptype(), PType::F16);
1087    }
1088
1089    // Test decimal array conversions
1090    #[test]
1091    fn test_decimal128_array_conversion() {
1092        let mut builder = Decimal128Builder::with_capacity(4);
1093        builder.append_value(12345);
1094        builder.append_null();
1095        builder.append_value(67890);
1096        builder.append_value(11111);
1097        let decimal_array = builder.finish().with_precision_and_scale(10, 2).unwrap();
1098
1099        let vortex_array = ArrayRef::from_arrow(&decimal_array, true).unwrap();
1100        assert_eq!(vortex_array.len(), 4);
1101
1102        let mut builder_non_null = Decimal128Builder::with_capacity(3);
1103        builder_non_null.append_value(12345);
1104        builder_non_null.append_value(67890);
1105        builder_non_null.append_value(11111);
1106        let decimal_array_non_null = builder_non_null
1107            .finish()
1108            .with_precision_and_scale(10, 2)
1109            .unwrap();
1110
1111        let vortex_array_non_null = ArrayRef::from_arrow(&decimal_array_non_null, false).unwrap();
1112        assert_eq!(vortex_array_non_null.len(), 3);
1113
1114        // Verify metadata - should be DecimalArray with correct precision and scale
1115        let decimal_vortex_array = vortex_array.as_::<Decimal>();
1116        assert_eq!(decimal_vortex_array.decimal_dtype().precision(), 10);
1117        assert_eq!(decimal_vortex_array.decimal_dtype().scale(), 2);
1118
1119        let decimal_vortex_array_non_null = vortex_array_non_null.as_::<Decimal>();
1120        assert_eq!(
1121            decimal_vortex_array_non_null.decimal_dtype().precision(),
1122            10
1123        );
1124        assert_eq!(decimal_vortex_array_non_null.decimal_dtype().scale(), 2);
1125    }
1126
1127    #[test]
1128    fn test_decimal256_array_conversion() {
1129        let mut builder = Decimal256Builder::with_capacity(4);
1130        builder.append_value(arrow_buffer::i256::from_i128(12345));
1131        builder.append_null();
1132        builder.append_value(arrow_buffer::i256::from_i128(67890));
1133        builder.append_value(arrow_buffer::i256::from_i128(11111));
1134        let decimal_array = builder.finish().with_precision_and_scale(38, 10).unwrap();
1135
1136        let vortex_array = ArrayRef::from_arrow(&decimal_array, true).unwrap();
1137        assert_eq!(vortex_array.len(), 4);
1138
1139        let mut builder_non_null = Decimal256Builder::with_capacity(3);
1140        builder_non_null.append_value(arrow_buffer::i256::from_i128(12345));
1141        builder_non_null.append_value(arrow_buffer::i256::from_i128(67890));
1142        builder_non_null.append_value(arrow_buffer::i256::from_i128(11111));
1143        let decimal_array_non_null = builder_non_null
1144            .finish()
1145            .with_precision_and_scale(38, 10)
1146            .unwrap();
1147
1148        let vortex_array_non_null = ArrayRef::from_arrow(&decimal_array_non_null, false).unwrap();
1149        assert_eq!(vortex_array_non_null.len(), 3);
1150
1151        // Verify metadata - should be DecimalArray with correct precision and scale
1152        let decimal_vortex_array = vortex_array.as_::<Decimal>();
1153        assert_eq!(decimal_vortex_array.decimal_dtype().precision(), 38);
1154        assert_eq!(decimal_vortex_array.decimal_dtype().scale(), 10);
1155
1156        let decimal_vortex_array_non_null = vortex_array_non_null.as_::<Decimal>();
1157        assert_eq!(
1158            decimal_vortex_array_non_null.decimal_dtype().precision(),
1159            38
1160        );
1161        assert_eq!(decimal_vortex_array_non_null.decimal_dtype().scale(), 10);
1162    }
1163
1164    // Test temporal array conversions
1165    #[rstest]
1166    #[case::timestamp_second(
1167        Arc::new(TimestampSecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1168        Arc::new(TimestampSecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1169    )]
1170    #[case::timestamp_millisecond(
1171        Arc::new(TimestampMillisecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1172        Arc::new(TimestampMillisecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1173    )]
1174    #[case::timestamp_microsecond(
1175        Arc::new(TimestampMicrosecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1176        Arc::new(TimestampMicrosecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1177    )]
1178    #[case::timestamp_nanosecond(
1179        Arc::new(TimestampNanosecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1180        Arc::new(TimestampNanosecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1181    )]
1182    #[case::time32_second(
1183        Arc::new(Time32SecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1184        Arc::new(Time32SecondArray::from(vec![1000_i32, 2000, 3000, 4000])),
1185    )]
1186    #[case::time32_millisecond(
1187        Arc::new(Time32MillisecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1188        Arc::new(Time32MillisecondArray::from(vec![1000_i32, 2000, 3000, 4000])),
1189    )]
1190    #[case::time64_microsecond(
1191        Arc::new(Time64MicrosecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1192        Arc::new(Time64MicrosecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1193    )]
1194    #[case::time64_nanosecond(
1195        Arc::new(Time64NanosecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])),
1196        Arc::new(Time64NanosecondArray::from(vec![1000_i64, 2000, 3000, 4000])),
1197    )]
1198    #[case::date32(
1199        Arc::new(Date32Array::from(vec![Some(18000), None, Some(18002), Some(18003)])),
1200        Arc::new(Date32Array::from(vec![18000_i32, 18001, 18002, 18003])),
1201    )]
1202    #[case::date64(
1203        Arc::new(Date64Array::from(vec![Some(1555200000000), None, Some(1555286400000), Some(1555372800000)]
1204        )),
1205        Arc::new(Date64Array::from(vec![1555200000000_i64, 1555213600000, 1555286400000, 1555372800000]
1206        )),
1207    )]
1208    fn test_temporal_array_conversion(
1209        #[case] nullable: Arc<dyn ArrowArray>,
1210        #[case] non_nullable: Arc<dyn ArrowArray>,
1211    ) {
1212        let v_null = ArrayRef::from_arrow(nullable.as_ref(), true).unwrap();
1213        let v_non_null = ArrayRef::from_arrow(non_nullable.as_ref(), false).unwrap();
1214        assert_eq!(v_null.len(), 4);
1215        assert_eq!(v_non_null.len(), 4);
1216    }
1217
1218    #[test]
1219    fn test_timestamp_timezone_microsecond_array_conversion() {
1220        let arrow_array =
1221            TimestampMicrosecondArray::from(vec![Some(1000), None, Some(3000), Some(4000)])
1222                .with_timezone("UTC");
1223        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1224
1225        let arrow_array_non_null =
1226            TimestampMicrosecondArray::from(vec![1000_i64, 2000, 3000, 4000]).with_timezone("UTC");
1227        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1228
1229        assert_eq!(vortex_array.len(), 4);
1230        assert_eq!(
1231            vortex_array.dtype(),
1232            &DType::Extension(
1233                Timestamp::new_with_tz(
1234                    TimeUnit::Microseconds,
1235                    Some("UTC".into()),
1236                    Nullability::Nullable
1237                )
1238                .erased()
1239            ),
1240        );
1241        assert_eq!(vortex_array_non_null.len(), 4);
1242        assert_eq!(
1243            vortex_array_non_null.dtype(),
1244            &DType::Extension(
1245                Timestamp::new_with_tz(
1246                    TimeUnit::Microseconds,
1247                    Some("UTC".into()),
1248                    Nullability::NonNullable
1249                )
1250                .erased()
1251            )
1252        );
1253    }
1254
1255    // Test string/binary array conversions
1256    #[rstest]
1257    #[case::utf8(
1258        Arc::new(StringArray::from(vec![Some("hello"), None, Some("world"), Some("test")])),
1259        Arc::new(StringArray::from(vec!["hello", "world", "test", "vortex"])),
1260        DType::Utf8(Nullability::NonNullable),
1261    )]
1262    #[case::large_utf8(
1263        Arc::new(LargeStringArray::from(vec![Some("hello"), None, Some("world"), Some("test")])),
1264        Arc::new(LargeStringArray::from(vec!["hello", "world", "test", "vortex"])),
1265        DType::Utf8(Nullability::NonNullable),
1266    )]
1267    #[case::binary(
1268        Arc::new(BinaryArray::from(vec![
1269            Some("hello".as_bytes()), None, Some("world".as_bytes()), Some("test".as_bytes()),
1270        ])),
1271        Arc::new(BinaryArray::from(vec![
1272            "hello".as_bytes(), "world".as_bytes(), "test".as_bytes(), "vortex".as_bytes(),
1273        ])),
1274        DType::Binary(Nullability::NonNullable),
1275    )]
1276    #[case::large_binary(
1277        Arc::new(LargeBinaryArray::from(vec![
1278            Some("hello".as_bytes()), None, Some("world".as_bytes()), Some("test".as_bytes()),
1279        ])),
1280        Arc::new(LargeBinaryArray::from(vec![
1281            "hello".as_bytes(), "world".as_bytes(), "test".as_bytes(), "vortex".as_bytes(),
1282        ])),
1283        DType::Binary(Nullability::NonNullable),
1284    )]
1285    fn test_string_binary_array_conversion(
1286        #[case] nullable: Arc<dyn ArrowArray>,
1287        #[case] non_nullable: Arc<dyn ArrowArray>,
1288        #[case] expected_non_nullable_dtype: DType,
1289    ) {
1290        let v_null = ArrayRef::from_arrow(nullable.as_ref(), true).unwrap();
1291        let v_non_null = ArrayRef::from_arrow(non_nullable.as_ref(), false).unwrap();
1292        assert_eq!(v_null.len(), 4);
1293        assert_eq!(v_non_null.len(), 4);
1294        assert_eq!(v_null.dtype(), &expected_non_nullable_dtype.as_nullable());
1295        assert_eq!(v_non_null.dtype(), &expected_non_nullable_dtype);
1296    }
1297
1298    #[test]
1299    fn test_utf8_view_array_conversion() {
1300        let mut builder = StringViewBuilder::new();
1301        builder.append_value("hello");
1302        builder.append_null();
1303        builder.append_value("world");
1304        builder.append_value("test");
1305        let arrow_array = builder.finish();
1306        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1307
1308        let mut builder_non_null = StringViewBuilder::new();
1309        builder_non_null.append_value("hello");
1310        builder_non_null.append_value("world");
1311        builder_non_null.append_value("test");
1312        builder_non_null.append_value("vortex");
1313        let arrow_array_non_null = builder_non_null.finish();
1314        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1315
1316        assert_eq!(vortex_array.len(), 4);
1317        assert_eq!(vortex_array_non_null.len(), 4);
1318
1319        // Verify metadata - should be VarBinViewArray with correct buffer count and dtype
1320        let varbin_view_array = vortex_array.as_::<VarBinView>();
1321        assert_eq!(
1322            varbin_view_array.data_buffers().len(),
1323            arrow_array.data_buffers().len()
1324        );
1325        assert_eq!(varbin_view_array.dtype(), &DType::Utf8(true.into()));
1326
1327        let varbin_view_array_non_null = vortex_array_non_null.as_::<VarBinView>();
1328        assert_eq!(
1329            varbin_view_array_non_null.data_buffers().len(),
1330            arrow_array_non_null.data_buffers().len()
1331        );
1332        assert_eq!(
1333            varbin_view_array_non_null.dtype(),
1334            &DType::Utf8(false.into())
1335        );
1336    }
1337
1338    #[test]
1339    fn test_binary_view_array_conversion() {
1340        let mut builder = BinaryViewBuilder::new();
1341        builder.append_value(b"hello");
1342        builder.append_null();
1343        builder.append_value(b"world");
1344        builder.append_value(b"test");
1345        let arrow_array = builder.finish();
1346        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1347
1348        let mut builder_non_null = BinaryViewBuilder::new();
1349        builder_non_null.append_value(b"hello");
1350        builder_non_null.append_value(b"world");
1351        builder_non_null.append_value(b"test");
1352        builder_non_null.append_value(b"vortex");
1353        let arrow_array_non_null = builder_non_null.finish();
1354        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1355
1356        assert_eq!(vortex_array.len(), 4);
1357        assert_eq!(vortex_array_non_null.len(), 4);
1358
1359        // Verify metadata - should be VarBinViewArray with correct buffer count and dtype
1360        let varbin_view_array = vortex_array.as_::<VarBinView>();
1361        assert_eq!(
1362            varbin_view_array.data_buffers().len(),
1363            arrow_array.data_buffers().len()
1364        );
1365        assert_eq!(varbin_view_array.dtype(), &DType::Binary(true.into()));
1366
1367        let varbin_view_array_non_null = vortex_array_non_null.as_::<VarBinView>();
1368        assert_eq!(
1369            varbin_view_array_non_null.data_buffers().len(),
1370            arrow_array_non_null.data_buffers().len()
1371        );
1372        assert_eq!(
1373            varbin_view_array_non_null.dtype(),
1374            &DType::Binary(false.into())
1375        );
1376    }
1377
1378    // Test boolean array conversions
1379    #[test]
1380    fn test_boolean_array_conversion() {
1381        let arrow_array = BooleanArray::from(vec![Some(true), None, Some(false), Some(true)]);
1382        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1383
1384        let arrow_array_non_null = BooleanArray::from(vec![true, false, true, false]);
1385        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1386
1387        assert_eq!(vortex_array.len(), 4);
1388        assert_eq!(vortex_array_non_null.len(), 4);
1389    }
1390
1391    // Test struct array conversions
1392    #[test]
1393    fn test_struct_array_conversion() {
1394        let fields = vec![
1395            Field::new("field1", DataType::Int32, true),
1396            Field::new("field2", DataType::Utf8, false),
1397        ];
1398        let schema = Fields::from(fields);
1399
1400        let field1_data = Int32Array::from(vec![Some(1), None, Some(3)]);
1401        let field2_data = StringArray::from(vec!["a", "b", "c"]);
1402
1403        let arrow_array = StructArray::new(
1404            schema.clone(),
1405            vec![Arc::new(field1_data), Arc::new(field2_data)],
1406            None,
1407        );
1408
1409        let vortex_array = ArrayRef::from_arrow(&arrow_array, false).unwrap();
1410        assert_eq!(vortex_array.len(), 3);
1411
1412        // Verify metadata - should be StructArray with correct field names
1413        let struct_vortex_array = vortex_array.as_::<Struct>();
1414        assert_eq!(struct_vortex_array.names().len(), 2);
1415        assert_eq!(struct_vortex_array.names()[0], "field1");
1416        assert_eq!(struct_vortex_array.names()[1], "field2");
1417
1418        // Test nullable struct
1419        let nullable_array = StructArray::new(
1420            schema,
1421            vec![
1422                Arc::new(Int32Array::from(vec![Some(1), None, Some(3)])),
1423                Arc::new(StringArray::from(vec!["a", "b", "c"])),
1424            ],
1425            Some(arrow_buffer::NullBuffer::new(BooleanBuffer::from(vec![
1426                true, false, true,
1427            ]))),
1428        );
1429
1430        let vortex_nullable_array = ArrayRef::from_arrow(&nullable_array, true).unwrap();
1431        assert_eq!(vortex_nullable_array.len(), 3);
1432
1433        // Verify metadata for nullable struct
1434        let struct_vortex_nullable_array = vortex_nullable_array.as_::<Struct>();
1435        assert_eq!(struct_vortex_nullable_array.names().len(), 2);
1436        assert_eq!(struct_vortex_nullable_array.names()[0], "field1");
1437        assert_eq!(struct_vortex_nullable_array.names()[1], "field2");
1438    }
1439
1440    // Test list array conversions
1441    #[test]
1442    fn test_list_array_conversion() {
1443        let mut builder = ListBuilder::new(Int32Builder::new());
1444        builder.append_value([Some(1), None, Some(3)]);
1445        builder.append_null();
1446        builder.append_value([Some(4), Some(5)]);
1447        let arrow_array = builder.finish();
1448
1449        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1450        assert_eq!(vortex_array.len(), 3);
1451
1452        // Verify metadata - should be ListArray with correct offsets
1453        let list_vortex_array = vortex_array.as_::<List>();
1454        let offsets_array = list_vortex_array.offsets().as_::<Primitive>();
1455        assert_eq!(offsets_array.len(), 4); // n+1 offsets for n lists
1456        assert_eq!(offsets_array.ptype(), PType::I32);
1457
1458        // Test non-nullable list
1459        let mut builder_non_null = ListBuilder::new(Int32Builder::new());
1460        builder_non_null.append_value([Some(1), None, Some(3)]);
1461        builder_non_null.append_value([Some(4), Some(5)]);
1462        let arrow_array_non_null = builder_non_null.finish();
1463
1464        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1465        assert_eq!(vortex_array_non_null.len(), 2);
1466
1467        // Verify metadata for non-nullable list
1468        let list_vortex_array_non_null = vortex_array_non_null.as_::<List>();
1469        let offsets_array_non_null = list_vortex_array_non_null.offsets().as_::<Primitive>();
1470        assert_eq!(offsets_array_non_null.len(), 3); // n+1 offsets for n lists
1471        assert_eq!(offsets_array_non_null.ptype(), PType::I32);
1472    }
1473
1474    #[test]
1475    fn test_sliced_offset_arrays_import_only_their_rows() -> VortexResult<()> {
1476        let strings = |rows: Range<i64>| {
1477            StringArray::from_iter_values(rows.map(|row| format!("value-{row}")))
1478        };
1479        let lists = |rows: Range<i64>| {
1480            let mut builder = ListBuilder::new(Int64Builder::new());
1481            for row in rows {
1482                builder.append_value([Some(row), Some(row + 1)]);
1483            }
1484            builder.finish()
1485        };
1486        let mut ctx = array_session().create_execution_ctx();
1487
1488        // A slice keeps its parent's whole child; the import must not.
1489        let sliced = ArrayRef::from_arrow(&strings(0..1000).slice(500, 2), false)?;
1490        let fresh = ArrayRef::from_arrow(&strings(500..502), false)?;
1491        assert_eq!(sliced.nbytes(), fresh.nbytes());
1492        assert_arrays_eq!(sliced, fresh, &mut ctx);
1493
1494        let sliced = ArrayRef::from_arrow(&lists(0..1000).slice(500, 2), false)?;
1495        let fresh = ArrayRef::from_arrow(&lists(500..502), false)?;
1496        assert_eq!(sliced.nbytes(), fresh.nbytes());
1497        assert_arrays_eq!(sliced, fresh, &mut ctx);
1498        Ok(())
1499    }
1500
1501    #[test]
1502    fn test_large_list_array_conversion() {
1503        let mut builder = LargeListBuilder::new(Int32Builder::new());
1504        builder.append_value([Some(1), None, Some(3)]);
1505        builder.append_null();
1506        builder.append_value([Some(4), Some(5)]);
1507        let arrow_array = builder.finish();
1508
1509        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1510        assert_eq!(vortex_array.len(), 3);
1511
1512        // Verify metadata - should be ListArray with correct offsets (I64 for large lists)
1513        let list_vortex_array = vortex_array.as_::<List>();
1514        let offsets_array = list_vortex_array.offsets().as_::<Primitive>();
1515        assert_eq!(offsets_array.len(), 4); // n+1 offsets for n lists
1516        assert_eq!(offsets_array.ptype(), PType::I64); // Large lists use I64 offsets
1517
1518        // Test non-nullable large list
1519        let mut builder_non_null = LargeListBuilder::new(Int32Builder::new());
1520        builder_non_null.append_value([Some(1), None, Some(3)]);
1521        builder_non_null.append_value([Some(4), Some(5)]);
1522        let arrow_array_non_null = builder_non_null.finish();
1523
1524        let vortex_array_non_null = ArrayRef::from_arrow(&arrow_array_non_null, false).unwrap();
1525        assert_eq!(vortex_array_non_null.len(), 2);
1526
1527        // Verify metadata for non-nullable large list
1528        let list_vortex_array_non_null = vortex_array_non_null.as_::<List>();
1529        let offsets_array_non_null = list_vortex_array_non_null.offsets().as_::<Primitive>();
1530        assert_eq!(offsets_array_non_null.len(), 3); // n+1 offsets for n lists
1531        assert_eq!(offsets_array_non_null.ptype(), PType::I64); // Large lists use I64 offsets
1532    }
1533
1534    #[test]
1535    fn test_fixed_size_list_array_conversion() {
1536        // Create elements for the fixed-size lists
1537        let values = Int32Array::from(vec![
1538            Some(1),
1539            Some(2),
1540            Some(3), // First list
1541            Some(4),
1542            None,
1543            Some(6), // Second list (with null element)
1544            Some(7),
1545            Some(8),
1546            Some(9), // Third list
1547            Some(10),
1548            Some(11),
1549            Some(12), // Fourth list
1550        ]);
1551
1552        // Create a FixedSizeListArray with list_size=3
1553        let field = Arc::new(Field::new("item", DataType::Int32, true));
1554        let arrow_array =
1555            ArrowFixedSizeListArray::try_new(Arc::clone(&field), 3, Arc::new(values), None)
1556                .unwrap();
1557        let vortex_array = ArrayRef::from_arrow(&arrow_array, false).unwrap();
1558
1559        assert_eq!(vortex_array.len(), 4);
1560
1561        // Verify metadata - should be FixedSizeListArray with correct list size
1562        let fsl_vortex_array = vortex_array.as_::<FixedSizeList>();
1563        assert_eq!(fsl_vortex_array.list_size(), 3);
1564        assert_eq!(fsl_vortex_array.elements().len(), 12); // 4 lists * 3 elements
1565
1566        // Test nullable fixed-size list
1567        let values_nullable = Int32Array::from(vec![
1568            Some(1),
1569            Some(2),
1570            Some(3), // First list
1571            Some(4),
1572            None,
1573            Some(6), // Second list (will be null)
1574            Some(7),
1575            Some(8),
1576            Some(9), // Third list
1577        ]);
1578
1579        // Create nulls buffer - second list is null
1580        let null_buffer =
1581            arrow_buffer::NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
1582
1583        let arrow_array_nullable = ArrowFixedSizeListArray::try_new(
1584            field,
1585            3,
1586            Arc::new(values_nullable),
1587            Some(null_buffer),
1588        )
1589        .unwrap();
1590        let vortex_array_nullable = ArrayRef::from_arrow(&arrow_array_nullable, true).unwrap();
1591
1592        assert_eq!(vortex_array_nullable.len(), 3);
1593
1594        // Verify metadata for nullable array
1595        let fsl_vortex_array_nullable = vortex_array_nullable.as_::<FixedSizeList>();
1596        assert_eq!(fsl_vortex_array_nullable.list_size(), 3);
1597        assert_eq!(fsl_vortex_array_nullable.elements().len(), 9); // 3 lists * 3 elements
1598    }
1599
1600    #[test]
1601    fn test_list_view_array_conversion() {
1602        // Create values array for the lists
1603        let values = Int32Array::from(vec![
1604            Some(1),
1605            Some(2),
1606            Some(3), // First list [1, 2, 3]
1607            Some(4),
1608            Some(5), // Second list [4, 5]
1609            Some(6), // Third list [6]
1610            Some(7),
1611            Some(8),
1612            Some(9),
1613            Some(10), // Fourth list [7, 8, 9, 10]
1614        ]);
1615
1616        // Create offsets and sizes for ListView
1617        let offsets = ScalarBuffer::from(vec![0i32, 3, 5, 6]);
1618        let sizes = ScalarBuffer::from(vec![3i32, 2, 1, 4]);
1619
1620        let field = Arc::new(Field::new("item", DataType::Int32, true));
1621        let arrow_array = GenericListViewArray::try_new(
1622            Arc::clone(&field),
1623            offsets.clone(),
1624            sizes.clone(),
1625            Arc::new(values.clone()),
1626            None,
1627        )
1628        .unwrap();
1629
1630        let vortex_array = ArrayRef::from_arrow(&arrow_array, false).unwrap();
1631        assert_eq!(vortex_array.len(), 4);
1632
1633        // Verify metadata - should be ListViewArray with correct offsets and sizes
1634        let list_view_vortex_array = vortex_array.as_::<ListView>();
1635        let offsets_array = list_view_vortex_array.offsets().as_::<Primitive>();
1636        let sizes_array = list_view_vortex_array.sizes().as_::<Primitive>();
1637
1638        assert_eq!(offsets_array.len(), 4);
1639        assert_eq!(offsets_array.ptype(), PType::I32);
1640        assert_eq!(sizes_array.len(), 4);
1641        assert_eq!(sizes_array.ptype(), PType::I32);
1642
1643        // Test nullable ListView
1644        let null_buffer =
1645            arrow_buffer::NullBuffer::new(BooleanBuffer::from(vec![true, false, true, true]));
1646
1647        let arrow_array_nullable = GenericListViewArray::try_new(
1648            Arc::clone(&field),
1649            offsets,
1650            sizes,
1651            Arc::new(values.clone()),
1652            Some(null_buffer),
1653        )
1654        .unwrap();
1655
1656        let vortex_array_nullable = ArrayRef::from_arrow(&arrow_array_nullable, true).unwrap();
1657        assert_eq!(vortex_array_nullable.len(), 4);
1658
1659        // Test LargeListView (i64 offsets and sizes)
1660        let large_offsets = ScalarBuffer::from(vec![0i64, 3, 5, 6]);
1661        let large_sizes = ScalarBuffer::from(vec![3i64, 2, 1, 4]);
1662
1663        let large_arrow_array = GenericListViewArray::try_new(
1664            field,
1665            large_offsets,
1666            large_sizes,
1667            Arc::new(values),
1668            None,
1669        )
1670        .unwrap();
1671
1672        let large_vortex_array = ArrayRef::from_arrow(&large_arrow_array, false).unwrap();
1673        assert_eq!(large_vortex_array.len(), 4);
1674
1675        // Verify metadata for large ListView
1676        let large_list_view_vortex_array = large_vortex_array.as_::<ListView>();
1677        let large_offsets_array = large_list_view_vortex_array.offsets().as_::<Primitive>();
1678        let large_sizes_array = large_list_view_vortex_array.sizes().as_::<Primitive>();
1679
1680        assert_eq!(large_offsets_array.len(), 4);
1681        assert_eq!(large_offsets_array.ptype(), PType::I64); // Large ListView uses I64 offsets
1682        assert_eq!(large_sizes_array.len(), 4);
1683        assert_eq!(large_sizes_array.ptype(), PType::I64); // Large ListView uses I64 sizes
1684    }
1685
1686    // Test null array conversions
1687    #[test]
1688    fn test_null_array_conversion() {
1689        let arrow_array = NullArray::new(5);
1690        let vortex_array = ArrayRef::from_arrow(&arrow_array, true).unwrap();
1691        assert_eq!(vortex_array.len(), 5);
1692    }
1693
1694    // Test buffer conversions
1695    #[test]
1696    fn test_arrow_buffer_conversion() {
1697        let data = vec![1u8, 2, 3, 4, 5];
1698        let arrow_buffer = ArrowBuffer::from_vec(data);
1699        let vortex_array = arrow_buffer.into_array();
1700        assert_eq!(vortex_array.len(), 5);
1701    }
1702
1703    #[test]
1704    fn test_boolean_buffer_conversion() {
1705        let data = vec![true, false, true, false, true];
1706        let boolean_buffer = BooleanBuffer::from(data);
1707        let vortex_array = boolean_buffer.into_array();
1708        assert_eq!(vortex_array.len(), 5);
1709    }
1710
1711    #[test]
1712    fn test_scalar_buffer_conversion() {
1713        let data = vec![1i32, 2, 3, 4, 5];
1714        let scalar_buffer = ScalarBuffer::from(data);
1715        let vortex_array = scalar_buffer.into_array();
1716        assert_eq!(vortex_array.len(), 5);
1717    }
1718
1719    #[test]
1720    fn test_offset_buffer_conversion() {
1721        let data = vec![0i32, 2, 5, 8, 10];
1722        let offset_buffer = OffsetBuffer::new(ScalarBuffer::from(data));
1723        let vortex_array = offset_buffer.into_array();
1724        assert_eq!(vortex_array.len(), 5);
1725    }
1726
1727    // Test RecordBatch conversions
1728    #[test]
1729    fn test_record_batch_conversion() {
1730        let schema = Arc::new(Schema::new(vec![
1731            Field::new("field1", DataType::Int32, false),
1732            Field::new("field2", DataType::Utf8, false),
1733        ]));
1734
1735        let field1_data = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
1736        let field2_data = Arc::new(StringArray::from(vec!["a", "b", "c", "d"]));
1737
1738        let record_batch = RecordBatch::try_new(schema, vec![field1_data, field2_data]).unwrap();
1739
1740        let vortex_array = ArrayRef::from_arrow(record_batch, false).unwrap();
1741        assert_eq!(vortex_array.len(), 4);
1742
1743        // Test with reference
1744        let schema = Arc::new(Schema::new(vec![
1745            Field::new("field1", DataType::Int32, false),
1746            Field::new("field2", DataType::Utf8, false),
1747        ]));
1748
1749        let field1_data = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
1750        let field2_data = Arc::new(StringArray::from(vec!["a", "b", "c", "d"]));
1751
1752        let record_batch = RecordBatch::try_new(schema, vec![field1_data, field2_data]).unwrap();
1753
1754        let vortex_array = ArrayRef::from_arrow(&record_batch, false).unwrap();
1755        assert_eq!(vortex_array.len(), 4);
1756    }
1757
1758    // Test dynamic dispatch conversion
1759    #[test]
1760    fn test_dyn_array_conversion() {
1761        let int_array = Int32Array::from(vec![1, 2, 3, 4]);
1762        let dyn_array: &dyn ArrowArray = &int_array;
1763        let vortex_array = ArrayRef::from_arrow(dyn_array, false).unwrap();
1764        assert_eq!(vortex_array.len(), 4);
1765
1766        let string_array = StringArray::from(vec!["a", "b", "c"]);
1767        let dyn_array: &dyn ArrowArray = &string_array;
1768        let vortex_array = ArrayRef::from_arrow(dyn_array, false).unwrap();
1769        assert_eq!(vortex_array.len(), 3);
1770
1771        let bool_array = BooleanArray::from(vec![true, false, true]);
1772        let dyn_array: &dyn ArrowArray = &bool_array;
1773        let vortex_array = ArrayRef::from_arrow(dyn_array, false).unwrap();
1774        assert_eq!(vortex_array.len(), 3);
1775    }
1776
1777    // Existing tests
1778    #[test]
1779    pub fn nullable_may_contain_non_nullable() {
1780        let null_struct_array_with_non_nullable_field = new_null_array(
1781            &DataType::Struct(Fields::from(vec![Field::new(
1782                "non_nullable_inner",
1783                DataType::Int32,
1784                false,
1785            )])),
1786            1,
1787        );
1788        ArrayRef::from_arrow(null_struct_array_with_non_nullable_field.as_ref(), true).unwrap();
1789    }
1790
1791    #[test]
1792    pub fn nullable_may_contain_deeply_nested_non_nullable() {
1793        let null_struct_array_with_non_nullable_field = new_null_array(
1794            &DataType::Struct(Fields::from(vec![Field::new(
1795                "non_nullable_inner",
1796                DataType::Struct(Fields::from(vec![Field::new(
1797                    "non_nullable_deeper_inner",
1798                    DataType::Int32,
1799                    false,
1800                )])),
1801                false,
1802            )])),
1803            1,
1804        );
1805        ArrayRef::from_arrow(null_struct_array_with_non_nullable_field.as_ref(), true).unwrap();
1806    }
1807
1808    #[test]
1809    fn dyn_dispatch_imports_map() -> VortexResult<()> {
1810        let mut builder = ArrowMapBuilder::new(None, StringBuilder::new(), Int32Builder::new());
1811        builder.keys().append_value("joe");
1812        builder.values().append_value(1);
1813        builder.append(true)?;
1814        builder.append(false)?;
1815        let arrow = builder.finish();
1816
1817        let expected = DType::map(
1818            DType::Utf8(Nullability::NonNullable),
1819            DType::Primitive(PType::I32, Nullability::Nullable),
1820            false,
1821            Nullability::Nullable,
1822        )?;
1823
1824        // Dispatching on the Arrow data type must cover maps...
1825        assert_eq!(from_arrow_dyn(&arrow, true)?.dtype(), &expected);
1826
1827        // ...including maps reached by recursing into a container.
1828        let batch = RecordBatch::try_from_iter_with_nullable([(
1829            "maps",
1830            Arc::new(arrow) as Arc<dyn ArrowArray>,
1831            true,
1832        )])?;
1833        let imported = from_arrow_batch(&batch, false)?;
1834        assert_eq!(
1835            imported.as_::<Struct>().unmasked_field(0).dtype(),
1836            &expected
1837        );
1838
1839        Ok(())
1840    }
1841
1842    #[test]
1843    fn non_nullable_request_rejects_nulls() {
1844        // Requesting `nullable = false` on an Arrow array that physically contains nulls is a
1845        // contradiction and must surface as an error, not a panic.
1846        let arrow_array = Int32Array::from(vec![Some(1), None, Some(3)]);
1847        assert!(ArrayRef::from_arrow(&arrow_array, false).is_err());
1848    }
1849
1850    #[test]
1851    fn non_nullable_request_rejects_null_array() {
1852        // An Arrow NullArray is entirely null, so it cannot be converted to a non-nullable
1853        // Vortex array.
1854        let arrow_array = NullArray::new(5);
1855        assert!(ArrayRef::from_arrow(&arrow_array, false).is_err());
1856    }
1857
1858    #[test]
1859    fn non_nullable_struct_with_nulls_errors() {
1860        // A struct array carrying top-level nulls cannot be converted to a non-nullable Vortex
1861        // struct; the struct-level validity reconciliation must error rather than panic.
1862        let struct_array = new_null_array(
1863            &DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, true)])),
1864            3,
1865        );
1866        assert!(ArrayRef::from_arrow(struct_array.as_ref(), false).is_err());
1867    }
1868
1869    #[test]
1870    fn non_nullable_list_with_nulls_errors() {
1871        // Likewise for a list array with a null entry: requesting a non-nullable list must error
1872        // rather than panic.
1873        let mut builder = ListBuilder::new(Int32Builder::new());
1874        builder.append_value([Some(1), Some(2)]);
1875        builder.append_null();
1876        let list = builder.finish();
1877        assert!(ArrayRef::from_arrow(&list, false).is_err());
1878    }
1879
1880    #[test]
1881    pub fn nullable_struct_containing_non_nullable_dictionary_with_nulls_errors() {
1882        // `remove_nulls` cannot strip pushed-down nulls out of a non-nullable dictionary field,
1883        // so the values end up converted with `nullable = false` while still containing nulls.
1884        // This must surface as an error rather than panicking.
1885        let null_struct_array_with_non_nullable_field = new_null_array(
1886            &DataType::Struct(Fields::from(vec![Field::new(
1887                "non_nullable_deeper_inner",
1888                DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
1889                false,
1890            )])),
1891            1,
1892        );
1893
1894        assert!(
1895            ArrayRef::from_arrow(null_struct_array_with_non_nullable_field.as_ref(), true).is_err()
1896        );
1897    }
1898}