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