Skip to main content

arrow_array/array/
struct_array.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::array::print_long_array;
19use crate::{Array, ArrayRef, RecordBatch, make_array, new_null_array};
20use arrow_buffer::{BooleanBuffer, Buffer, NullBuffer};
21use arrow_data::{ArrayData, ArrayDataBuilder};
22use arrow_schema::{ArrowError, DataType, Field, FieldRef, Fields};
23use std::sync::Arc;
24use std::{any::Any, ops::Index};
25
26/// An array of [structs](https://arrow.apache.org/docs/format/Columnar.html#struct-layout)
27///
28/// Each child (called *field*) is represented by a separate array.
29///
30/// # Comparison with [RecordBatch]
31///
32/// Both [`RecordBatch`] and [`StructArray`] represent a collection of columns / arrays with the
33/// same length.
34///
35/// However, there are a couple of key differences:
36///
37/// * [`StructArray`] can be nested within other [`Array`], including itself
38/// * [`RecordBatch`] can contain top-level metadata on its associated [`Schema`][arrow_schema::Schema]
39/// * [`StructArray`] can contain top-level nulls, i.e. `null`
40/// * [`RecordBatch`] can only represent nulls in its child columns, i.e. `{"field": null}`
41///
42/// [`StructArray`] is therefore a more general data container than [`RecordBatch`], and as such
43/// code that needs to handle both will typically share an implementation in terms of
44/// [`StructArray`] and convert to/from [`RecordBatch`] as necessary.
45///
46/// [`From`] implementations are provided to facilitate this conversion, however, converting
47/// from a [`StructArray`] containing top-level nulls to a [`RecordBatch`] will panic, as there
48/// is no way to preserve them.
49///
50/// # Example: Create an array from a vector of fields
51///
52/// ```
53/// use std::sync::Arc;
54/// use arrow_array::{Array, ArrayRef, BooleanArray, Int32Array, StructArray};
55/// use arrow_schema::{DataType, Field};
56///
57/// let boolean = Arc::new(BooleanArray::from(vec![false, false, true, true]));
58/// let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31]));
59///
60/// let struct_array = StructArray::from(vec![
61///     (
62///         Arc::new(Field::new("b", DataType::Boolean, false)),
63///         boolean.clone() as ArrayRef,
64///     ),
65///     (
66///         Arc::new(Field::new("c", DataType::Int32, false)),
67///         int.clone() as ArrayRef,
68///     ),
69/// ]);
70/// assert_eq!(struct_array.column(0).as_ref(), boolean.as_ref());
71/// assert_eq!(struct_array.column(1).as_ref(), int.as_ref());
72/// assert_eq!(4, struct_array.len());
73/// assert_eq!(0, struct_array.null_count());
74/// assert_eq!(0, struct_array.offset());
75/// ```
76#[derive(Clone)]
77pub struct StructArray {
78    len: usize,
79    data_type: DataType,
80    nulls: Option<NullBuffer>,
81    fields: Vec<ArrayRef>,
82}
83
84impl StructArray {
85    /// Create a new [`StructArray`] from the provided parts, panicking on failure
86    ///
87    /// # Panics
88    ///
89    /// Panics if [`Self::try_new`] returns an error
90    pub fn new(fields: Fields, arrays: Vec<ArrayRef>, nulls: Option<NullBuffer>) -> Self {
91        Self::try_new(fields, arrays, nulls).unwrap()
92    }
93
94    /// Create a new [`StructArray`] from the provided parts, returning an error on failure
95    ///
96    /// The length will be inferred from the length of the child arrays.  Returns an error if
97    /// there are no child arrays.  Consider using [`Self::try_new_with_length`] if the length
98    /// is known to avoid this.
99    ///
100    /// # Errors
101    ///
102    /// Errors if
103    ///
104    /// * `fields.len() == 0`
105    /// * Any reason that [`Self::try_new_with_length`] would error
106    pub fn try_new(
107        fields: Fields,
108        arrays: Vec<ArrayRef>,
109        nulls: Option<NullBuffer>,
110    ) -> Result<Self, ArrowError> {
111        let len = arrays.first().map(|x| x.len()).ok_or_else(||ArrowError::InvalidArgumentError("use StructArray::try_new_with_length or StructArray::new_empty_fields to create a struct array with no fields so that the length can be set correctly".to_string()))?;
112
113        Self::try_new_with_length(fields, arrays, nulls, len)
114    }
115
116    /// Create a new [`StructArray`] from the provided parts, returning an error on failure
117    ///
118    /// # Errors
119    ///
120    /// Errors if
121    ///
122    /// * `fields.len() != arrays.len()`
123    /// * `fields[i].data_type() != arrays[i].data_type()`
124    /// * `arrays[i].len() != arrays[j].len()`
125    /// * `arrays[i].len() != nulls.len()`
126    /// * `!fields[i].is_nullable() && !nulls.contains(arrays[i].nulls())`
127    pub fn try_new_with_length(
128        fields: Fields,
129        arrays: Vec<ArrayRef>,
130        nulls: Option<NullBuffer>,
131        len: usize,
132    ) -> Result<Self, ArrowError> {
133        if fields.len() != arrays.len() {
134            return Err(ArrowError::InvalidArgumentError(format!(
135                "Incorrect number of arrays for StructArray fields, expected {} got {}",
136                fields.len(),
137                arrays.len()
138            )));
139        }
140
141        if let Some(n) = nulls.as_ref()
142            && n.len() != len
143        {
144            return Err(ArrowError::InvalidArgumentError(format!(
145                "Incorrect number of nulls for StructArray, expected {len} got {}",
146                n.len(),
147            )));
148        }
149
150        for (f, a) in fields.iter().zip(&arrays) {
151            if f.data_type() != a.data_type() {
152                return Err(ArrowError::InvalidArgumentError(format!(
153                    "Incorrect datatype for StructArray field {:?}, expected {} got {}",
154                    f.name(),
155                    f.data_type(),
156                    a.data_type()
157                )));
158            }
159
160            if a.len() != len {
161                return Err(ArrowError::InvalidArgumentError(format!(
162                    "Incorrect array length for StructArray field {:?}, expected {} got {}",
163                    f.name(),
164                    len,
165                    a.len()
166                )));
167            }
168
169            if !f.is_nullable()
170                && let Some(a) = a.logical_nulls()
171                && nulls.as_ref().is_none_or(|n| !n.contains(&a))
172                && a.null_count() > 0
173            {
174                return Err(ArrowError::InvalidArgumentError(format!(
175                    "Found unmasked nulls for non-nullable StructArray field {:?}",
176                    f.name()
177                )));
178            }
179        }
180
181        Ok(Self {
182            len,
183            data_type: DataType::Struct(fields),
184            nulls: nulls.filter(|n| n.null_count() > 0),
185            fields: arrays,
186        })
187    }
188
189    /// Create a new [`StructArray`] of length `len` where all values are null
190    pub fn new_null(fields: Fields, len: usize) -> Self {
191        let arrays = fields
192            .iter()
193            .map(|f| new_null_array(f.data_type(), len))
194            .collect();
195
196        Self {
197            len,
198            data_type: DataType::Struct(fields),
199            nulls: Some(NullBuffer::new_null(len)),
200            fields: arrays,
201        }
202    }
203
204    /// Create a new [`StructArray`] from the provided parts without validation
205    ///
206    /// The length will be inferred from the length of the child arrays.  Panics if there are no
207    /// child arrays.  Consider using [`Self::new_unchecked_with_length`] if the length is known
208    /// to avoid this.
209    ///
210    /// # Safety
211    ///
212    /// Safe if [`Self::new`] would not panic with the given arguments
213    pub unsafe fn new_unchecked(
214        fields: Fields,
215        arrays: Vec<ArrayRef>,
216        nulls: Option<NullBuffer>,
217    ) -> Self {
218        if cfg!(feature = "force_validate") {
219            return Self::new(fields, arrays, nulls);
220        }
221
222        let len = arrays.first().map(|x| x.len()).expect(
223            "cannot use StructArray::new_unchecked if there are no fields, length is unknown",
224        );
225        Self {
226            len,
227            data_type: DataType::Struct(fields),
228            nulls,
229            fields: arrays,
230        }
231    }
232
233    /// Create a new [`StructArray`] from the provided parts without validation
234    ///
235    /// # Safety
236    ///
237    /// Safe if [`Self::new`] would not panic with the given arguments
238    pub unsafe fn new_unchecked_with_length(
239        fields: Fields,
240        arrays: Vec<ArrayRef>,
241        nulls: Option<NullBuffer>,
242        len: usize,
243    ) -> Self {
244        if cfg!(feature = "force_validate") {
245            return Self::try_new_with_length(fields, arrays, nulls, len).unwrap();
246        }
247
248        Self {
249            len,
250            data_type: DataType::Struct(fields),
251            nulls,
252            fields: arrays,
253        }
254    }
255
256    /// Create a new [`StructArray`] containing no fields
257    ///
258    /// # Panics
259    ///
260    /// If `len != nulls.len()`
261    pub fn new_empty_fields(len: usize, nulls: Option<NullBuffer>) -> Self {
262        if let Some(n) = &nulls {
263            assert_eq!(len, n.len())
264        }
265        Self {
266            len,
267            data_type: DataType::Struct(Fields::empty()),
268            fields: vec![],
269            nulls,
270        }
271    }
272
273    /// Deconstruct this array into its constituent parts
274    pub fn into_parts(self) -> (Fields, Vec<ArrayRef>, Option<NullBuffer>) {
275        let DataType::Struct(f) = self.data_type else {
276            unreachable!()
277        };
278        (f, self.fields, self.nulls)
279    }
280
281    /// Returns the field at `pos`.
282    ///
283    /// # Panics
284    /// Panics if `pos` is out of bounds
285    pub fn column(&self, pos: usize) -> &ArrayRef {
286        &self.fields[pos]
287    }
288
289    /// Return the number of fields in this struct array
290    pub fn num_columns(&self) -> usize {
291        self.fields.len()
292    }
293
294    /// Returns the fields of the struct array
295    pub fn columns(&self) -> &[ArrayRef] {
296        &self.fields
297    }
298
299    /// Return field names in this struct array
300    pub fn column_names(&self) -> Vec<&str> {
301        match self.data_type() {
302            DataType::Struct(fields) => fields
303                .iter()
304                .map(|f| f.name().as_str())
305                .collect::<Vec<&str>>(),
306            _ => unreachable!("Struct array's data type is not struct!"),
307        }
308    }
309
310    /// Returns the [`Fields`] of this [`StructArray`]
311    pub fn fields(&self) -> &Fields {
312        match self.data_type() {
313            DataType::Struct(f) => f,
314            _ => unreachable!(),
315        }
316    }
317
318    /// Return child array whose field name equals to column_name
319    ///
320    /// Note: A schema can currently have duplicate field names, in which case
321    /// the first field will always be selected.
322    /// This issue will be addressed in [#9205](https://github.com/apache/arrow-rs/issues/9205)
323    pub fn column_by_name(&self, column_name: &str) -> Option<&ArrayRef> {
324        self.fields()
325            .find(column_name)
326            .map(|(pos, _)| self.column(pos))
327    }
328
329    /// Returns the [`FieldRef`] at `pos`.
330    pub fn field(&self, pos: usize) -> &FieldRef {
331        &self.fields()[pos]
332    }
333
334    /// Return the [`FieldRef`] whose name equals to `field_name`
335    ///
336    /// Note: A schema can currently have duplicate field names, in which case
337    /// the first field will always be selected.
338    /// This issue will be addressed in [#9205](https://github.com/apache/arrow-rs/issues/9205)
339    pub fn field_by_name(&self, field_name: &str) -> Option<&FieldRef> {
340        self.fields().find(field_name).map(|(_, field)| field)
341    }
342
343    /// Returns a zero-copy slice of this array with the indicated offset and length.
344    ///
345    /// # Panics
346    /// Panics if `offset + len > self.len()`
347    pub fn slice(&self, offset: usize, len: usize) -> Self {
348        assert!(
349            offset.saturating_add(len) <= self.len,
350            "the length + offset of the sliced StructArray cannot exceed the existing length"
351        );
352
353        let fields = self.fields.iter().map(|a| a.slice(offset, len)).collect();
354
355        Self {
356            len,
357            data_type: self.data_type.clone(),
358            nulls: self.nulls.as_ref().map(|n| n.slice(offset, len)),
359            fields,
360        }
361    }
362
363    /// Returns the children of this [`StructArray`] with the struct's validity
364    /// bitmap AND'd into each child's validity bitmap.
365    ///
366    /// This ensures that positions where the struct itself is null are also
367    /// null in each returned child array. Fields that were non-nullable are
368    /// marked nullable in the returned [`Fields`] when the struct has nulls.
369    ///
370    /// If the struct has no nulls, children and fields are returned as-is.
371    ///
372    /// This mirrors the semantics of C++ Arrow's `StructArray::Flatten`.
373    ///
374    /// # Example
375    ///
376    /// ```
377    /// # use std::sync::Arc;
378    /// # use arrow_array::{Array, ArrayRef, Int32Array, StructArray};
379    /// # use arrow_buffer::{BooleanBuffer, NullBuffer};
380    /// # use arrow_schema::{DataType, Field, Fields};
381    /// let child = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
382    /// let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
383    /// let sa = StructArray::new(
384    ///     Fields::from(vec![Field::new("a", DataType::Int32, false)]),
385    ///     vec![child],
386    ///     Some(struct_nulls),
387    /// );
388    /// let (fields, columns) = sa.flatten();
389    /// assert!(fields[0].is_nullable());
390    /// assert!(columns[0].is_null(1));
391    /// ```
392    pub fn flatten(&self) -> (Fields, Vec<ArrayRef>) {
393        let schema_fields = self.fields();
394
395        let Some(struct_nulls) = &self.nulls else {
396            return (schema_fields.clone(), self.fields.clone());
397        };
398
399        let new_fields: Fields = schema_fields
400            .iter()
401            .map(|f| {
402                if f.is_nullable() {
403                    Arc::clone(f)
404                } else {
405                    Arc::new(f.as_ref().clone().with_nullable(true))
406                }
407            })
408            .collect::<Vec<_>>()
409            .into();
410
411        let new_columns = self
412            .fields
413            .iter()
414            .map(|child| {
415                let merged = NullBuffer::union(Some(struct_nulls), child.nulls());
416                // SAFETY: We only make the null buffer more restrictive (adding nulls).
417                // All data buffers and child data remain unchanged.
418                let data = child.to_data().into_builder().nulls(merged);
419                make_array(unsafe { data.build_unchecked() })
420            })
421            .collect();
422
423        (new_fields, new_columns)
424    }
425}
426
427impl From<ArrayData> for StructArray {
428    fn from(data: ArrayData) -> Self {
429        let (data_type, len, nulls, offset, _buffers, child_data) = data.into_parts();
430
431        let parent_offset = offset;
432        let parent_len = len;
433
434        let fields = child_data
435            .into_iter()
436            .map(|cd| {
437                if parent_offset != 0 || parent_len != cd.len() {
438                    make_array(cd.slice(parent_offset, parent_len))
439                } else {
440                    make_array(cd)
441                }
442            })
443            .collect();
444
445        Self {
446            len,
447            data_type,
448            nulls,
449            fields,
450        }
451    }
452}
453
454impl From<StructArray> for ArrayData {
455    fn from(array: StructArray) -> Self {
456        let builder = ArrayDataBuilder::new(array.data_type)
457            .len(array.len)
458            .nulls(array.nulls)
459            .child_data(array.fields.iter().map(|x| x.to_data()).collect());
460
461        unsafe { builder.build_unchecked() }
462    }
463}
464
465impl TryFrom<Vec<(&str, ArrayRef)>> for StructArray {
466    type Error = ArrowError;
467
468    /// builds a StructArray from a vector of names and arrays.
469    fn try_from(values: Vec<(&str, ArrayRef)>) -> Result<Self, ArrowError> {
470        let (fields, arrays): (Vec<_>, _) = values
471            .into_iter()
472            .map(|(name, array)| {
473                (
474                    Field::new(name, array.data_type().clone(), array.is_nullable()),
475                    array,
476                )
477            })
478            .unzip();
479
480        StructArray::try_new(fields.into(), arrays, None)
481    }
482}
483
484/// SAFETY: Correctly implements the contract of Arrow Arrays
485unsafe impl Array for StructArray {
486    fn as_any(&self) -> &dyn Any {
487        self
488    }
489
490    fn to_data(&self) -> ArrayData {
491        self.clone().into()
492    }
493
494    fn into_data(self) -> ArrayData {
495        self.into()
496    }
497
498    fn data_type(&self) -> &DataType {
499        &self.data_type
500    }
501
502    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
503        Arc::new(self.slice(offset, length))
504    }
505
506    fn len(&self) -> usize {
507        self.len
508    }
509
510    fn is_empty(&self) -> bool {
511        self.len == 0
512    }
513
514    fn shrink_to_fit(&mut self) {
515        if let Some(nulls) = &mut self.nulls {
516            nulls.shrink_to_fit();
517        }
518        self.fields.iter_mut().for_each(|n| n.shrink_to_fit());
519    }
520
521    fn offset(&self) -> usize {
522        0
523    }
524
525    fn nulls(&self) -> Option<&NullBuffer> {
526        self.nulls.as_ref()
527    }
528
529    fn logical_null_count(&self) -> usize {
530        // More efficient that the default implementation
531        self.null_count()
532    }
533
534    fn get_buffer_memory_size(&self) -> usize {
535        let mut size = self.fields.iter().map(|a| a.get_buffer_memory_size()).sum();
536        if let Some(n) = self.nulls.as_ref() {
537            size += n.buffer().capacity();
538        }
539        size
540    }
541
542    fn get_array_memory_size(&self) -> usize {
543        let mut size = self.fields.iter().map(|a| a.get_array_memory_size()).sum();
544        size += std::mem::size_of::<Self>();
545        if let Some(n) = self.nulls.as_ref() {
546            size += n.buffer().capacity();
547        }
548        size
549    }
550
551    #[cfg(feature = "pool")]
552    fn claim(&self, pool: &dyn arrow_buffer::MemoryPool) {
553        for field in &self.fields {
554            field.claim(pool);
555        }
556        if let Some(nulls) = &self.nulls {
557            nulls.claim(pool);
558        }
559    }
560}
561
562impl From<Vec<(FieldRef, ArrayRef)>> for StructArray {
563    fn from(v: Vec<(FieldRef, ArrayRef)>) -> Self {
564        let (fields, arrays): (Vec<_>, _) = v.into_iter().unzip();
565        StructArray::new(fields.into(), arrays, None)
566    }
567}
568
569impl std::fmt::Debug for StructArray {
570    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
571        writeln!(f, "StructArray")?;
572        writeln!(f, "-- validity:")?;
573        writeln!(f, "[")?;
574        print_long_array(self, f, &mut |_index, f| write!(f, "valid"))?;
575        writeln!(f, "]\n[")?;
576        for (child_index, name) in self.column_names().iter().enumerate() {
577            let column = self.column(child_index);
578            writeln!(
579                f,
580                "-- child {}: \"{}\" ({:?})",
581                child_index,
582                name,
583                column.data_type()
584            )?;
585            std::fmt::Debug::fmt(column, f)?;
586            writeln!(f)?;
587        }
588        write!(f, "]")
589    }
590}
591
592impl From<(Vec<(FieldRef, ArrayRef)>, Buffer)> for StructArray {
593    fn from(pair: (Vec<(FieldRef, ArrayRef)>, Buffer)) -> Self {
594        let len = pair.0.first().map(|x| x.1.len()).unwrap_or_default();
595        let (fields, arrays): (Vec<_>, Vec<_>) = pair.0.into_iter().unzip();
596        let nulls = NullBuffer::new(BooleanBuffer::new(pair.1, 0, len));
597        Self::new(fields.into(), arrays, Some(nulls))
598    }
599}
600
601impl From<RecordBatch> for StructArray {
602    fn from(value: RecordBatch) -> Self {
603        Self {
604            len: value.num_rows(),
605            data_type: DataType::Struct(value.schema().fields().clone()),
606            nulls: None,
607            fields: value.columns().to_vec(),
608        }
609    }
610}
611
612impl Index<&str> for StructArray {
613    type Output = ArrayRef;
614
615    /// Get a reference to a column's array by name.
616    ///
617    /// Note: A schema can currently have duplicate field names, in which case
618    /// the first field will always be selected.
619    /// This issue will be addressed in [ARROW-11178](https://issues.apache.org/jira/browse/ARROW-11178)
620    ///
621    /// # Panics
622    ///
623    /// Panics if the name is not in the schema.
624    fn index(&self, name: &str) -> &Self::Output {
625        self.column_by_name(name).unwrap()
626    }
627}
628
629#[cfg(test)]
630mod tests {
631    use super::*;
632
633    use crate::{
634        BooleanArray, Float32Array, Float64Array, Int32Array, Int64Array, StringArray,
635        cast::AsArray, types::Int32Type,
636    };
637    use arrow_buffer::ToByteSlice;
638
639    #[test]
640    fn test_struct_array_builder() {
641        let boolean_array = BooleanArray::from(vec![false, false, true, true]);
642        let int_array = Int64Array::from(vec![42, 28, 19, 31]);
643
644        let fields = vec![
645            Field::new("a", DataType::Boolean, false),
646            Field::new("b", DataType::Int64, false),
647        ];
648        let struct_array_data = ArrayData::builder(DataType::Struct(fields.into()))
649            .len(4)
650            .add_child_data(boolean_array.to_data())
651            .add_child_data(int_array.to_data())
652            .build()
653            .unwrap();
654        let struct_array = StructArray::from(struct_array_data);
655
656        assert_eq!(struct_array.column(0).as_ref(), &boolean_array);
657        assert_eq!(struct_array.column(1).as_ref(), &int_array);
658    }
659
660    #[test]
661    fn test_struct_array_from() {
662        let boolean = Arc::new(BooleanArray::from(vec![false, false, true, true]));
663        let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31]));
664
665        let struct_array = StructArray::from(vec![
666            (
667                Arc::new(Field::new("b", DataType::Boolean, false)),
668                boolean.clone() as ArrayRef,
669            ),
670            (
671                Arc::new(Field::new("c", DataType::Int32, false)),
672                int.clone() as ArrayRef,
673            ),
674        ]);
675        assert_eq!(struct_array.column(0).as_ref(), boolean.as_ref());
676        assert_eq!(struct_array.column(1).as_ref(), int.as_ref());
677        assert_eq!(4, struct_array.len());
678        assert_eq!(0, struct_array.null_count());
679        assert_eq!(0, struct_array.offset());
680    }
681
682    #[test]
683    fn test_struct_array_from_data_with_offset_and_length() {
684        // Various ways to make the struct array:
685        //
686        // [{x: 2}, {x: 3}, None]
687        //
688        // from slicing larger buffers/arrays with offsets and lengths
689        let int_arr = Int32Array::from(vec![1, 2, 3, 4, 5]);
690        let int_field = Field::new("x", DataType::Int32, false);
691        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, true, false]));
692        let int_data = int_arr.to_data();
693        // Case 1: Offset + length, nulls are not sliced
694        let case1 = ArrayData::builder(DataType::Struct(Fields::from(vec![int_field.clone()])))
695            .len(3)
696            .offset(1)
697            .nulls(Some(struct_nulls))
698            .add_child_data(int_data.clone())
699            .build()
700            .unwrap();
701
702        // Case 2: Offset + length, nulls are sliced
703        let struct_nulls =
704            NullBuffer::new(BooleanBuffer::from(vec![true, true, true, false, true]).slice(1, 3));
705        let case2 = ArrayData::builder(DataType::Struct(Fields::from(vec![int_field.clone()])))
706            .len(3)
707            .offset(1)
708            .nulls(Some(struct_nulls.clone()))
709            .add_child_data(int_data.clone())
710            .build()
711            .unwrap();
712
713        // Case 3: struct length is smaller than child length but no offset
714        let offset_int_data = int_data.slice(1, 4);
715        let case3 = ArrayData::builder(DataType::Struct(Fields::from(vec![int_field.clone()])))
716            .len(3)
717            .nulls(Some(struct_nulls))
718            .add_child_data(offset_int_data)
719            .build()
720            .unwrap();
721
722        let expected = StructArray::new(
723            Fields::from(vec![int_field.clone()]),
724            vec![Arc::new(int_arr)],
725            Some(NullBuffer::new(BooleanBuffer::from(vec![
726                true, true, true, false, true,
727            ]))),
728        )
729        .slice(1, 3);
730
731        for case in [case1, case2, case3] {
732            let struct_arr_from_data = StructArray::from(case);
733            assert_eq!(struct_arr_from_data, expected);
734            assert_eq!(struct_arr_from_data.column(0), expected.column(0));
735        }
736    }
737
738    #[test]
739    fn test_struct_array_data_slice() {
740        // Slicing a struct's `ArrayData` and rebuilding an array from it has to
741        // apply the offset to the children exactly once (#7595, #7750).
742        let x = Int32Array::from(vec![Some(0), Some(1), Some(2), Some(3), None, Some(5)]);
743        let struct_array = StructArray::new(
744            Fields::from(vec![Field::new("x", DataType::Int32, true)]),
745            vec![Arc::new(x.clone())],
746            Some(NullBuffer::from(vec![true, true, true, false, true, true])),
747        )
748        .into_data();
749        let sliced = struct_array.slice(1, 4);
750
751        let arr = make_array(sliced);
752        assert_eq!(
753            arr.as_struct().column(0).as_primitive::<Int32Type>(),
754            &x.slice(1, 4)
755        );
756
757        // A struct whose top-level `ArrayData` carries a non-zero offset over
758        // full-length children is how the C++ implementation of Arrow (and the
759        // C data interface) represents a sliced struct: the offset/length live
760        // on the struct, not on the children. arrow-rs must decode it to the
761        // same logical array its own `StructArray::slice` produces.
762        let x = Int32Array::from(vec![Some(0), Some(1), Some(2), Some(3), None, Some(5)]);
763        let y = Int32Array::from(vec![Some(5), Some(6), None, Some(8), Some(9), Some(10)]);
764        let struct_array = StructArray::new(
765            Fields::from(vec![
766                Field::new("x", DataType::Int32, true),
767                Field::new("y", DataType::Int32, true),
768            ]),
769            vec![Arc::new(x), Arc::new(y)],
770            Some(NullBuffer::from(vec![true, true, true, false, true, true])),
771        );
772        let struct_array = StructArray::new(
773            Fields::from(vec![Field::new(
774                "inner",
775                struct_array.data_type().clone(),
776                true,
777            )]),
778            vec![Arc::new(struct_array)],
779            Some(NullBuffer::from(vec![true, false, true, true, true, true])),
780        );
781
782        let cpp_sliced_array = make_array(
783            struct_array
784                .to_data()
785                .into_builder()
786                .offset(1)
787                .len(4)
788                .nulls(Some(NullBuffer::from(vec![false, true, true, true])))
789                .build()
790                .unwrap(),
791        );
792
793        assert_eq!(cpp_sliced_array.as_struct(), &struct_array.slice(1, 4));
794    }
795
796    #[test]
797    fn test_make_array_sliced_struct_data() {
798        // Exact reproducer from #7750: `make_array` on a sliced struct's
799        // `ArrayData`.
800        let strings: ArrayRef = Arc::new(StringArray::from(vec![
801            Some("joe"),
802            None,
803            None,
804            Some("mark"),
805            Some("doe"),
806        ]));
807        let ints: ArrayRef = Arc::new(Int32Array::from(vec![
808            Some(1),
809            Some(2),
810            Some(3),
811            Some(4),
812            Some(5),
813        ]));
814
815        let array = StructArray::try_from(vec![("f1", strings.clone()), ("f2", ints.clone())])
816            .unwrap()
817            .into_data()
818            .slice(1, 3);
819
820        let arr = make_array(array);
821        let expected = StructArray::try_from(vec![("f1", strings), ("f2", ints)])
822            .unwrap()
823            .slice(1, 3);
824        assert_eq!(arr.as_struct(), &expected);
825    }
826
827    #[test]
828    fn test_slice_struct_data_with_existing_offset() {
829        // Slicing a struct whose `ArrayData` already carries a non-zero offset
830        // over full-length children, which is how the C data interface hands us
831        // a sliced struct. The cumulative offset has to end up on the children
832        // only: anything left on the parent gets applied a second time by
833        // `From<ArrayData> for StructArray`.
834        let x = Int32Array::from(vec![Some(0), Some(1), Some(2), Some(3), None, Some(5)]);
835        let struct_array = StructArray::new(
836            Fields::from(vec![Field::new("x", DataType::Int32, true)]),
837            vec![Arc::new(x.clone())],
838            Some(NullBuffer::from(vec![true, true, true, false, true, true])),
839        );
840
841        let offset_data = struct_array
842            .to_data()
843            .into_builder()
844            .offset(1)
845            .len(5)
846            .nulls(Some(NullBuffer::from(vec![true, true, false, true, true])))
847            .build()
848            .unwrap();
849
850        let sliced = offset_data.slice(1, 3);
851        // The cumulative offset (1 + 1) lands on the child; the parent's own
852        // offset is reset to 0 so nothing re-applies it.
853        assert_eq!(sliced.offset(), 0);
854        assert_eq!(sliced.len(), 3);
855        assert_eq!(sliced.child_data()[0].offset(), 2);
856        assert_eq!(sliced.child_data()[0].len(), 3);
857
858        let arr = make_array(sliced);
859        assert_eq!(
860            arr.as_struct().column(0).as_primitive::<Int32Type>(),
861            &x.slice(2, 3)
862        );
863        assert_eq!(arr.as_struct(), &struct_array.slice(2, 3));
864    }
865
866    #[test]
867    fn test_struct_array_from_data_with_offset_and_length_error() {
868        let int_arr = Int32Array::from(vec![1, 2, 3, 4, 5]);
869        let int_field = Field::new("x", DataType::Int32, false);
870        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, true, false]));
871        let int_data = int_arr.to_data();
872        // If parent offset is 3 and len is 3 then child must have 6 items
873        let err = ArrayData::builder(DataType::Struct(Fields::from(vec![int_field.clone()])))
874            .len(3)
875            .offset(3)
876            .nulls(Some(struct_nulls))
877            .add_child_data(int_data)
878            .build()
879            .unwrap_err()
880            .to_string();
881
882        assert!(err.contains(
883            "child array #0 for field x has length smaller than expected for struct array (5 < 6)"
884        ));
885    }
886
887    /// validates that struct can be accessed using `column_name` as index i.e. `struct_array["column_name"]`.
888    #[test]
889    fn test_struct_array_index_access() {
890        let boolean = Arc::new(BooleanArray::from(vec![false, false, true, true]));
891        let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31]));
892
893        let struct_array = StructArray::from(vec![
894            (
895                Arc::new(Field::new("b", DataType::Boolean, false)),
896                boolean.clone() as ArrayRef,
897            ),
898            (
899                Arc::new(Field::new("c", DataType::Int32, false)),
900                int.clone() as ArrayRef,
901            ),
902        ]);
903        assert_eq!(struct_array["b"].as_ref(), boolean.as_ref());
904        assert_eq!(struct_array["c"].as_ref(), int.as_ref());
905    }
906
907    #[test]
908    fn test_struct_array_field_access() {
909        let boolean = Arc::new(BooleanArray::from(vec![false, false, true, true]));
910        let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31]));
911
912        let b_field = Arc::new(Field::new("b", DataType::Boolean, false));
913        let c_field = Arc::new(Field::new("c", DataType::Int32, false));
914
915        let struct_array = StructArray::from(vec![
916            (b_field.clone(), boolean as ArrayRef),
917            (c_field.clone(), int as ArrayRef),
918        ]);
919
920        assert_eq!(struct_array.field(0), &b_field);
921        assert_eq!(struct_array.field(1), &c_field);
922
923        assert_eq!(struct_array.field_by_name("b"), Some(&b_field));
924        assert_eq!(struct_array.field_by_name("c"), Some(&c_field));
925        assert_eq!(struct_array.field_by_name("d"), None);
926    }
927
928    /// validates that the in-memory representation follows [the spec](https://arrow.apache.org/docs/format/Columnar.html#struct-layout)
929    #[test]
930    fn test_struct_array_from_vec() {
931        let strings: ArrayRef = Arc::new(StringArray::from(vec![
932            Some("joe"),
933            None,
934            None,
935            Some("mark"),
936        ]));
937        let ints: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(2), None, Some(4)]));
938
939        let arr =
940            StructArray::try_from(vec![("f1", strings.clone()), ("f2", ints.clone())]).unwrap();
941
942        let struct_data = arr.into_data();
943        assert_eq!(4, struct_data.len());
944        assert_eq!(0, struct_data.null_count());
945
946        let expected_string_data = ArrayData::builder(DataType::Utf8)
947            .len(4)
948            .null_bit_buffer(Some(Buffer::from(&[9_u8])))
949            .add_buffer(Buffer::from([0, 3, 3, 3, 7].to_byte_slice()))
950            .add_buffer(Buffer::from(b"joemark"))
951            .build()
952            .unwrap();
953
954        let expected_int_data = ArrayData::builder(DataType::Int32)
955            .len(4)
956            .null_bit_buffer(Some(Buffer::from(&[11_u8])))
957            .add_buffer(Buffer::from([1, 2, 0, 4].to_byte_slice()))
958            .build()
959            .unwrap();
960
961        assert_eq!(expected_string_data, struct_data.child_data()[0]);
962        assert_eq!(expected_int_data, struct_data.child_data()[1]);
963    }
964
965    #[test]
966    fn test_struct_array_from_vec_error() {
967        let strings: ArrayRef = Arc::new(StringArray::from(vec![
968            Some("joe"),
969            None,
970            None,
971            // 3 elements, not 4
972        ]));
973        let ints: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(2), None, Some(4)]));
974
975        let err = StructArray::try_from(vec![("f1", strings.clone()), ("f2", ints.clone())])
976            .unwrap_err()
977            .to_string();
978
979        assert_eq!(
980            err,
981            "Invalid argument error: Incorrect array length for StructArray field \"f2\", expected 3 got 4"
982        )
983    }
984
985    #[test]
986    #[should_panic(
987        expected = "Incorrect datatype for StructArray field \\\"b\\\", expected Int16 got Boolean"
988    )]
989    fn test_struct_array_from_mismatched_types_single() {
990        drop(StructArray::from(vec![(
991            Arc::new(Field::new("b", DataType::Int16, false)),
992            Arc::new(BooleanArray::from(vec![false, false, true, true])) as Arc<dyn Array>,
993        )]));
994    }
995
996    #[test]
997    #[should_panic(
998        expected = "Incorrect datatype for StructArray field \\\"b\\\", expected Int16 got Boolean"
999    )]
1000    fn test_struct_array_from_mismatched_types_multiple() {
1001        drop(StructArray::from(vec![
1002            (
1003                Arc::new(Field::new("b", DataType::Int16, false)),
1004                Arc::new(BooleanArray::from(vec![false, false, true, true])) as Arc<dyn Array>,
1005            ),
1006            (
1007                Arc::new(Field::new("c", DataType::Utf8, false)),
1008                Arc::new(Int32Array::from(vec![42, 28, 19, 31])),
1009            ),
1010        ]));
1011    }
1012
1013    #[test]
1014    fn test_struct_array_slice() {
1015        let boolean_data = ArrayData::builder(DataType::Boolean)
1016            .len(5)
1017            .add_buffer(Buffer::from([0b00010000]))
1018            .null_bit_buffer(Some(Buffer::from([0b00010001])))
1019            .build()
1020            .unwrap();
1021        let int_data = ArrayData::builder(DataType::Int32)
1022            .len(5)
1023            .add_buffer(Buffer::from([0, 28, 42, 0, 0].to_byte_slice()))
1024            .null_bit_buffer(Some(Buffer::from([0b00000110])))
1025            .build()
1026            .unwrap();
1027
1028        let field_types = vec![
1029            Field::new("a", DataType::Boolean, true),
1030            Field::new("b", DataType::Int32, true),
1031        ];
1032        let struct_array_data = ArrayData::builder(DataType::Struct(field_types.into()))
1033            .len(5)
1034            .add_child_data(boolean_data.clone())
1035            .add_child_data(int_data.clone())
1036            .null_bit_buffer(Some(Buffer::from([0b00010111])))
1037            .build()
1038            .unwrap();
1039        let struct_array = StructArray::from(struct_array_data);
1040
1041        assert_eq!(5, struct_array.len());
1042        assert_eq!(1, struct_array.null_count());
1043        assert!(struct_array.is_valid(0));
1044        assert!(struct_array.is_valid(1));
1045        assert!(struct_array.is_valid(2));
1046        assert!(struct_array.is_null(3));
1047        assert!(struct_array.is_valid(4));
1048        assert_eq!(boolean_data, struct_array.column(0).to_data());
1049        assert_eq!(int_data, struct_array.column(1).to_data());
1050
1051        let c0 = struct_array.column(0);
1052        let c0 = c0.as_any().downcast_ref::<BooleanArray>().unwrap();
1053        assert_eq!(5, c0.len());
1054        assert_eq!(3, c0.null_count());
1055        assert!(c0.is_valid(0));
1056        assert!(!c0.value(0));
1057        assert!(c0.is_null(1));
1058        assert!(c0.is_null(2));
1059        assert!(c0.is_null(3));
1060        assert!(c0.is_valid(4));
1061        assert!(c0.value(4));
1062
1063        let c1 = struct_array.column(1);
1064        let c1 = c1.as_any().downcast_ref::<Int32Array>().unwrap();
1065        assert_eq!(5, c1.len());
1066        assert_eq!(3, c1.null_count());
1067        assert!(c1.is_null(0));
1068        assert!(c1.is_valid(1));
1069        assert_eq!(28, c1.value(1));
1070        assert!(c1.is_valid(2));
1071        assert_eq!(42, c1.value(2));
1072        assert!(c1.is_null(3));
1073        assert!(c1.is_null(4));
1074
1075        let sliced_array = struct_array.slice(2, 3);
1076        let sliced_array = sliced_array.as_any().downcast_ref::<StructArray>().unwrap();
1077        assert_eq!(3, sliced_array.len());
1078        assert_eq!(1, sliced_array.null_count());
1079        assert!(sliced_array.is_valid(0));
1080        assert!(sliced_array.is_null(1));
1081        assert!(sliced_array.is_valid(2));
1082
1083        let sliced_c0 = sliced_array.column(0);
1084        let sliced_c0 = sliced_c0.as_any().downcast_ref::<BooleanArray>().unwrap();
1085        assert_eq!(3, sliced_c0.len());
1086        assert!(sliced_c0.is_null(0));
1087        assert!(sliced_c0.is_null(1));
1088        assert!(sliced_c0.is_valid(2));
1089        assert!(sliced_c0.value(2));
1090
1091        let sliced_c1 = sliced_array.column(1);
1092        let sliced_c1 = sliced_c1.as_any().downcast_ref::<Int32Array>().unwrap();
1093        assert_eq!(3, sliced_c1.len());
1094        assert!(sliced_c1.is_valid(0));
1095        assert_eq!(42, sliced_c1.value(0));
1096        assert!(sliced_c1.is_null(1));
1097        assert!(sliced_c1.is_null(2));
1098    }
1099
1100    #[test]
1101    #[should_panic(
1102        expected = "Incorrect array length for StructArray field \\\"c\\\", expected 1 got 2"
1103    )]
1104    fn test_invalid_struct_child_array_lengths() {
1105        drop(StructArray::from(vec![
1106            (
1107                Arc::new(Field::new("b", DataType::Float32, false)),
1108                Arc::new(Float32Array::from(vec![1.1])) as Arc<dyn Array>,
1109            ),
1110            (
1111                Arc::new(Field::new("c", DataType::Float64, false)),
1112                Arc::new(Float64Array::from(vec![2.2, 3.3])),
1113            ),
1114        ]));
1115    }
1116
1117    #[test]
1118    #[should_panic(expected = "use StructArray::try_new_with_length")]
1119    fn test_struct_array_from_empty() {
1120        // This can't work because we don't know how many rows the array should have.  Previously we inferred 0 but
1121        // that often led to bugs.
1122        let _ = StructArray::from(vec![]);
1123    }
1124
1125    #[test]
1126    fn test_empty_struct_array() {
1127        assert!(StructArray::try_new(Fields::empty(), vec![], None).is_err());
1128
1129        let arr = StructArray::new_empty_fields(10, None);
1130        assert_eq!(arr.len(), 10);
1131        assert_eq!(arr.null_count(), 0);
1132        assert_eq!(arr.num_columns(), 0);
1133
1134        let arr2 = StructArray::try_new_with_length(Fields::empty(), vec![], None, 10).unwrap();
1135        assert_eq!(arr2.len(), 10);
1136
1137        let arr = StructArray::new_empty_fields(10, Some(NullBuffer::new_null(10)));
1138        assert_eq!(arr.len(), 10);
1139        assert_eq!(arr.null_count(), 10);
1140        assert_eq!(arr.num_columns(), 0);
1141
1142        let arr2 = StructArray::try_new_with_length(
1143            Fields::empty(),
1144            vec![],
1145            Some(NullBuffer::new_null(10)),
1146            10,
1147        )
1148        .unwrap();
1149        assert_eq!(arr2.len(), 10);
1150    }
1151
1152    #[test]
1153    #[should_panic(expected = "Found unmasked nulls for non-nullable StructArray field \\\"c\\\"")]
1154    fn test_struct_array_from_mismatched_nullability() {
1155        drop(StructArray::from(vec![(
1156            Arc::new(Field::new("c", DataType::Int32, false)),
1157            Arc::new(Int32Array::from(vec![Some(42), None, Some(19)])) as ArrayRef,
1158        )]));
1159    }
1160
1161    #[test]
1162    fn test_struct_array_fmt_debug() {
1163        let arr = StructArray::new(
1164            vec![Arc::new(Field::new("c", DataType::Int32, true))].into(),
1165            vec![Arc::new(Int32Array::from((0..30).collect::<Vec<_>>())) as ArrayRef],
1166            Some(NullBuffer::new(BooleanBuffer::from(
1167                (0..30).map(|i| i % 2 == 0).collect::<Vec<_>>(),
1168            ))),
1169        );
1170        assert_eq!(
1171            format!("{arr:?}"),
1172            "StructArray\n-- validity:\n[\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n  ...10 elements...,\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n  valid,\n  null,\n]\n[\n-- child 0: \"c\" (Int32)\nPrimitiveArray<Int32>\n[\n  0,\n  1,\n  2,\n  3,\n  4,\n  5,\n  6,\n  7,\n  8,\n  9,\n  ...10 elements...,\n  20,\n  21,\n  22,\n  23,\n  24,\n  25,\n  26,\n  27,\n  28,\n  29,\n]\n]"
1173        )
1174    }
1175
1176    #[test]
1177    fn test_struct_array_logical_nulls() {
1178        // Field is non-nullable
1179        let field = Field::new("a", DataType::Int32, false);
1180        let values = vec![1, 2, 3];
1181        // Create a NullBuffer with all bits set to valid (true)
1182        let nulls = NullBuffer::from(vec![true, true, true]);
1183        let array = Int32Array::new(values.into(), Some(nulls));
1184        let child = Arc::new(array) as ArrayRef;
1185        assert!(child.logical_nulls().is_some());
1186        assert_eq!(child.logical_nulls().unwrap().null_count(), 0);
1187
1188        let fields = Fields::from(vec![field]);
1189        let arrays = vec![child];
1190        let nulls = None;
1191
1192        StructArray::try_new(fields, arrays, nulls).expect("should not error");
1193    }
1194
1195    #[test]
1196    fn test_flatten_no_nulls() {
1197        let child = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1198        let sa = StructArray::from(vec![(
1199            Arc::new(Field::new("a", DataType::Int32, false)),
1200            child,
1201        )]);
1202
1203        let (fields, columns) = sa.flatten();
1204
1205        assert_eq!(columns.len(), 1);
1206        assert!(!fields[0].is_nullable());
1207        assert_eq!(columns[0].null_count(), 0);
1208        assert_eq!(columns[0].len(), 3);
1209    }
1210
1211    #[test]
1212    fn test_flatten_struct_nulls_child_no_nulls() {
1213        let child = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1214        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
1215        let sa = StructArray::new(
1216            Fields::from(vec![Field::new("a", DataType::Int32, false)]),
1217            vec![child],
1218            Some(struct_nulls),
1219        );
1220
1221        let (fields, columns) = sa.flatten();
1222
1223        assert!(fields[0].is_nullable());
1224        assert!(columns[0].is_valid(0));
1225        assert!(columns[0].is_null(1));
1226        assert!(columns[0].is_valid(2));
1227        assert_eq!(columns[0].null_count(), 1);
1228    }
1229
1230    #[test]
1231    fn test_flatten_both_have_nulls() {
1232        // struct validity: [valid, null,  valid, valid]
1233        // child validity:  [valid, valid, null,  valid]
1234        // expected:        [valid, null,  null,  valid]
1235        let child = Arc::new(Int32Array::from(vec![Some(1), Some(2), None, Some(4)])) as ArrayRef;
1236        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, false, true, true]));
1237        let sa = StructArray::new(
1238            Fields::from(vec![Field::new("a", DataType::Int32, true)]),
1239            vec![child],
1240            Some(struct_nulls),
1241        );
1242
1243        let (fields, columns) = sa.flatten();
1244
1245        assert!(fields[0].is_nullable());
1246        assert!(columns[0].is_valid(0));
1247        assert!(columns[0].is_null(1));
1248        assert!(columns[0].is_null(2));
1249        assert!(columns[0].is_valid(3));
1250        assert_eq!(columns[0].null_count(), 2);
1251    }
1252
1253    #[test]
1254    fn test_flatten_sliced_struct() {
1255        let child = Arc::new(Int32Array::from(vec![1, 2, 3, 4])) as ArrayRef;
1256        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, false, true, false]));
1257        let sa = StructArray::new(
1258            Fields::from(vec![Field::new("a", DataType::Int32, false)]),
1259            vec![child],
1260            Some(struct_nulls),
1261        );
1262        let sliced = sa.slice(1, 2);
1263
1264        let (fields, columns) = sliced.flatten();
1265
1266        assert!(fields[0].is_nullable());
1267        assert_eq!(columns[0].len(), 2);
1268        assert!(columns[0].is_null(0));
1269        assert!(columns[0].is_valid(1));
1270    }
1271
1272    #[test]
1273    fn test_flatten_multiple_children() {
1274        let int_child = Arc::new(Int32Array::from(vec![Some(1), Some(2), None])) as ArrayRef;
1275        let str_child = Arc::new(StringArray::from(vec![Some("a"), None, Some("c")])) as ArrayRef;
1276        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
1277        let sa = StructArray::new(
1278            Fields::from(vec![
1279                Field::new("ints", DataType::Int32, true),
1280                Field::new("strs", DataType::Utf8, true),
1281            ]),
1282            vec![int_child, str_child],
1283            Some(struct_nulls),
1284        );
1285
1286        let (fields, columns) = sa.flatten();
1287
1288        assert_eq!(fields.len(), 2);
1289        // int: [valid, null(struct), null(child)] => null_count=2
1290        assert_eq!(columns[0].null_count(), 2);
1291        assert!(columns[0].is_valid(0));
1292        assert!(columns[0].is_null(1));
1293        assert!(columns[0].is_null(2));
1294        // str: [valid, null(struct+child), valid] => null_count=1
1295        assert_eq!(columns[1].null_count(), 1);
1296        assert!(columns[1].is_valid(0));
1297        assert!(columns[1].is_null(1));
1298        assert!(columns[1].is_valid(2));
1299    }
1300
1301    #[test]
1302    fn test_flatten_empty_struct() {
1303        let sa = StructArray::new_empty_fields(5, Some(NullBuffer::new_null(5)));
1304
1305        let (fields, columns) = sa.flatten();
1306
1307        assert_eq!(fields.len(), 0);
1308        assert_eq!(columns.len(), 0);
1309    }
1310
1311    #[test]
1312    fn test_flatten_field_nullability_update() {
1313        let non_null_child = Arc::new(Int32Array::from(vec![1, 2, 3])) as ArrayRef;
1314        let nullable_child = Arc::new(Int32Array::from(vec![Some(1), None, Some(3)])) as ArrayRef;
1315        let struct_nulls = NullBuffer::new(BooleanBuffer::from(vec![true, true, false]));
1316        let sa = StructArray::new(
1317            Fields::from(vec![
1318                Field::new("non_null", DataType::Int32, false),
1319                Field::new("nullable", DataType::Int32, true),
1320            ]),
1321            vec![non_null_child, nullable_child],
1322            Some(struct_nulls),
1323        );
1324
1325        let (fields, _columns) = sa.flatten();
1326
1327        assert!(fields[0].is_nullable()); // was false, now true
1328        assert!(fields[1].is_nullable()); // was true, stays true
1329    }
1330}