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