Skip to main content

arrow_array/array/
union_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#![allow(clippy::enum_clike_unportable_variant)]
18
19use crate::{Array, ArrayRef, make_array};
20use arrow_buffer::bit_chunk_iterator::{BitChunkIterator, BitChunks};
21use arrow_buffer::buffer::NullBuffer;
22use arrow_buffer::{BooleanBuffer, Buffer, MutableBuffer, ScalarBuffer};
23use arrow_data::{ArrayData, ArrayDataBuilder};
24use arrow_schema::{ArrowError, DataType, UnionFields, UnionMode};
25/// Contains the `UnionArray` type.
26///
27use std::any::Any;
28use std::collections::HashSet;
29use std::sync::Arc;
30
31/// An array of [values of varying types](https://arrow.apache.org/docs/format/Columnar.html#union-layout)
32///
33/// Each slot in a [UnionArray] can have a value chosen from a number
34/// of types.  Each of the possible types are named like the fields of
35/// a [`StructArray`](crate::StructArray).  A `UnionArray` can
36/// have two possible memory layouts, "dense" or "sparse".  For more
37/// information on please see the
38/// [specification](https://arrow.apache.org/docs/format/Columnar.html#union-layout).
39///
40/// [UnionBuilder](crate::builder::UnionBuilder) can be used to
41/// create [UnionArray]'s of primitive types. `UnionArray`'s of nested
42/// types are also supported but not via `UnionBuilder`, see the tests
43/// for examples.
44///
45/// # Examples
46/// ## Create a dense UnionArray `[1, 3.2, 34]`
47/// ```
48/// use arrow_buffer::ScalarBuffer;
49/// use arrow_schema::*;
50/// use std::sync::Arc;
51/// use arrow_array::{Array, Int32Array, Float64Array, UnionArray};
52///
53/// let int_array = Int32Array::from(vec![1, 34]);
54/// let float_array = Float64Array::from(vec![3.2]);
55/// let type_ids = [0, 1, 0].into_iter().collect::<ScalarBuffer<i8>>();
56/// let offsets = [0, 0, 1].into_iter().collect::<ScalarBuffer<i32>>();
57///
58/// let union_fields = [
59///     (0, Arc::new(Field::new("A", DataType::Int32, false))),
60///     (1, Arc::new(Field::new("B", DataType::Float64, false))),
61/// ].into_iter().collect::<UnionFields>();
62///
63/// let children = vec![
64///     Arc::new(int_array) as Arc<dyn Array>,
65///     Arc::new(float_array),
66/// ];
67///
68/// let array = UnionArray::try_new(
69///     union_fields,
70///     type_ids,
71///     Some(offsets),
72///     children,
73/// ).unwrap();
74///
75/// let value = array.value(0).as_any().downcast_ref::<Int32Array>().unwrap().value(0);
76/// assert_eq!(1, value);
77///
78/// let value = array.value(1).as_any().downcast_ref::<Float64Array>().unwrap().value(0);
79/// assert!(3.2 - value < f64::EPSILON);
80///
81/// let value = array.value(2).as_any().downcast_ref::<Int32Array>().unwrap().value(0);
82/// assert_eq!(34, value);
83/// ```
84///
85/// ## Create a sparse UnionArray `[1, 3.2, 34]`
86/// ```
87/// use arrow_buffer::ScalarBuffer;
88/// use arrow_schema::*;
89/// use std::sync::Arc;
90/// use arrow_array::{Array, Int32Array, Float64Array, UnionArray};
91///
92/// let int_array = Int32Array::from(vec![Some(1), None, Some(34)]);
93/// let float_array = Float64Array::from(vec![None, Some(3.2), None]);
94/// let type_ids = [0_i8, 1, 0].into_iter().collect::<ScalarBuffer<i8>>();
95///
96/// let union_fields = [
97///     (0, Arc::new(Field::new("A", DataType::Int32, false))),
98///     (1, Arc::new(Field::new("B", DataType::Float64, false))),
99/// ].into_iter().collect::<UnionFields>();
100///
101/// let children = vec![
102///     Arc::new(int_array) as Arc<dyn Array>,
103///     Arc::new(float_array),
104/// ];
105///
106/// let array = UnionArray::try_new(
107///     union_fields,
108///     type_ids,
109///     None,
110///     children,
111/// ).unwrap();
112///
113/// let value = array.value(0).as_any().downcast_ref::<Int32Array>().unwrap().value(0);
114/// assert_eq!(1, value);
115///
116/// let value = array.value(1).as_any().downcast_ref::<Float64Array>().unwrap().value(0);
117/// assert!(3.2 - value < f64::EPSILON);
118///
119/// let value = array.value(2).as_any().downcast_ref::<Int32Array>().unwrap().value(0);
120/// assert_eq!(34, value);
121/// ```
122#[derive(Clone)]
123pub struct UnionArray {
124    data_type: DataType,
125    type_ids: ScalarBuffer<i8>,
126    offsets: Option<ScalarBuffer<i32>>,
127    fields: Vec<Option<ArrayRef>>,
128}
129
130impl UnionArray {
131    /// Creates a new `UnionArray`.
132    ///
133    /// Accepts type ids, child arrays and optionally offsets (for dense unions) to create
134    /// a new `UnionArray`.  This method makes no attempt to validate the data provided by the
135    /// caller and assumes that each of the components are correct and consistent with each other.
136    /// See `try_new` for an alternative that validates the data provided.
137    ///
138    /// # Safety
139    ///
140    /// The `type_ids` values should be non-negative and must match one of the type ids of the fields provided in `fields`.
141    /// These values are used to index into the `children` arrays.
142    ///
143    /// The `offsets` is provided in the case of a dense union, sparse unions should use `None`.
144    /// If provided the `offsets` values should be non-negative and must be less than the length of the
145    /// corresponding array.
146    ///
147    /// In both cases above we use signed integer types to maintain compatibility with other
148    /// Arrow implementations.
149    pub unsafe fn new_unchecked(
150        fields: UnionFields,
151        type_ids: ScalarBuffer<i8>,
152        offsets: Option<ScalarBuffer<i32>>,
153        children: Vec<ArrayRef>,
154    ) -> Self {
155        let mode = if offsets.is_some() {
156            UnionMode::Dense
157        } else {
158            UnionMode::Sparse
159        };
160
161        let len = type_ids.len();
162        let builder = ArrayData::builder(DataType::Union(fields, mode))
163            .add_buffer(type_ids.into_inner())
164            .child_data(children.into_iter().map(Array::into_data).collect())
165            .len(len);
166
167        let data = match offsets {
168            Some(offsets) => unsafe { builder.add_buffer(offsets.into_inner()).build_unchecked() },
169            None => unsafe { builder.build_unchecked() },
170        };
171        Self::from(data)
172    }
173
174    /// Attempts to create a new `UnionArray`, validating the inputs provided.
175    ///
176    /// The order of child arrays child array order must match the fields order
177    pub fn try_new(
178        fields: UnionFields,
179        type_ids: ScalarBuffer<i8>,
180        offsets: Option<ScalarBuffer<i32>>,
181        children: Vec<ArrayRef>,
182    ) -> Result<Self, ArrowError> {
183        // There must be a child array for every field.
184        if fields.len() != children.len() {
185            return Err(ArrowError::InvalidArgumentError(
186                "Union fields length must match child arrays length".to_string(),
187            ));
188        }
189
190        if let Some(offsets) = &offsets {
191            // There must be an offset value for every type id value.
192            if offsets.len() != type_ids.len() {
193                return Err(ArrowError::InvalidArgumentError(
194                    "Type Ids and Offsets lengths must match".to_string(),
195                ));
196            }
197        } else {
198            // Sparse union child arrays must be equal in length to the length of the union
199            for child in &children {
200                if child.len() != type_ids.len() {
201                    return Err(ArrowError::InvalidArgumentError(
202                        "Sparse union child arrays must be equal in length to the length of the union".to_string(),
203                    ));
204                }
205            }
206        }
207
208        // Create mapping from type id to array lengths.
209        let max_id = fields.iter().map(|(i, _)| i).max().unwrap_or_default() as usize;
210        let mut array_lens = vec![None; max_id + 1];
211        for (cd, (field_id, _)) in children.iter().zip(fields.iter()) {
212            array_lens[field_id as usize] = Some(cd.len());
213        }
214
215        // Type id values must match one of the fields.
216        for id in &type_ids {
217            match array_lens.get(*id as usize) {
218                Some(Some(_)) => {}
219                _ => {
220                    return Err(ArrowError::InvalidArgumentError(
221                        "Type Ids values must match one of the field type ids".to_owned(),
222                    ));
223                }
224            }
225        }
226
227        // Check the value offsets are in bounds.
228        if let Some(offsets) = &offsets {
229            let mut iter = type_ids.iter().zip(offsets.iter());
230            if iter.any(|(type_id, &offset)| {
231                offset < 0 || offset as usize >= array_lens[*type_id as usize].unwrap()
232            }) {
233                return Err(ArrowError::InvalidArgumentError(
234                    "Offsets must be non-negative and within the length of the Array".to_owned(),
235                ));
236            }
237        }
238
239        // Safety:
240        // - Arguments validated above.
241        let union_array = unsafe { Self::new_unchecked(fields, type_ids, offsets, children) };
242        Ok(union_array)
243    }
244
245    /// Accesses the child array for `type_id`.
246    ///
247    /// # Panics
248    ///
249    /// Panics if the `type_id` provided is not present in the array's DataType
250    /// in the `Union`.
251    pub fn child(&self, type_id: i8) -> &ArrayRef {
252        assert!((type_id as usize) < self.fields.len());
253        let boxed = &self.fields[type_id as usize];
254        boxed.as_ref().expect("invalid type id")
255    }
256
257    /// Returns the `type_id` for the array slot at `index`.
258    ///
259    /// # Panics
260    ///
261    /// Panics if `index` is greater than or equal to the number of child arrays
262    pub fn type_id(&self, index: usize) -> i8 {
263        assert!(index < self.type_ids.len());
264        self.type_ids[index]
265    }
266
267    /// Returns the `type_ids` buffer for this array
268    pub fn type_ids(&self) -> &ScalarBuffer<i8> {
269        &self.type_ids
270    }
271
272    /// Returns the `offsets` buffer if this is a dense array
273    pub fn offsets(&self) -> Option<&ScalarBuffer<i32>> {
274        self.offsets.as_ref()
275    }
276
277    /// Returns the offset into the underlying values array for the array slot at `index`.
278    ///
279    /// # Panics
280    ///
281    /// Panics if `index` is greater than or equal the length of the array.
282    pub fn value_offset(&self, index: usize) -> usize {
283        assert!(index < self.len());
284        match &self.offsets {
285            Some(offsets) => offsets[index] as usize,
286            None => self.offset() + index,
287        }
288    }
289
290    /// Returns the array's value at index `i`.
291    ///
292    /// Note: This method does not check for nulls and the value is arbitrary
293    /// (but still well-defined) if [`is_null`](Self::is_null) returns true for the index.
294    ///
295    /// # Panics
296    /// Panics if index `i` is out of bounds
297    pub fn value(&self, i: usize) -> ArrayRef {
298        let type_id = self.type_id(i);
299        let value_offset = self.value_offset(i);
300        let child = self.child(type_id);
301        child.slice(value_offset, 1)
302    }
303
304    /// Returns the names of the types in the union.
305    pub fn type_names(&self) -> Vec<&str> {
306        match self.data_type() {
307            DataType::Union(fields, _) => fields
308                .iter()
309                .map(|(_, f)| f.name().as_str())
310                .collect::<Vec<&str>>(),
311            _ => unreachable!("Union array's data type is not a union!"),
312        }
313    }
314
315    /// Returns the [`UnionFields`] for the union.
316    pub fn fields(&self) -> &UnionFields {
317        match self.data_type() {
318            DataType::Union(fields, _) => fields,
319            _ => unreachable!("Union array's data type is not a union!"),
320        }
321    }
322
323    /// Returns whether the `UnionArray` is dense (or sparse if `false`).
324    pub fn is_dense(&self) -> bool {
325        match self.data_type() {
326            DataType::Union(_, mode) => mode == &UnionMode::Dense,
327            _ => unreachable!("Union array's data type is not a union!"),
328        }
329    }
330
331    /// Returns a zero-copy slice of this array with the indicated offset and length.
332    ///
333    /// # Panics
334    /// Panics if `offset + length > self.len()`
335    pub fn slice(&self, offset: usize, length: usize) -> Self {
336        let (offsets, fields) = match self.offsets.as_ref() {
337            // If dense union, slice offsets
338            Some(offsets) => (Some(offsets.slice(offset, length)), self.fields.clone()),
339            // Otherwise need to slice sparse children
340            None => {
341                let fields = self
342                    .fields
343                    .iter()
344                    .map(|x| x.as_ref().map(|x| x.slice(offset, length)))
345                    .collect();
346                (None, fields)
347            }
348        };
349
350        Self {
351            data_type: self.data_type.clone(),
352            type_ids: self.type_ids.slice(offset, length),
353            offsets,
354            fields,
355        }
356    }
357
358    /// Deconstruct this array into its constituent parts
359    ///
360    /// # Example
361    ///
362    /// ```
363    /// # use arrow_array::array::UnionArray;
364    /// # use arrow_array::types::Int32Type;
365    /// # use arrow_array::builder::UnionBuilder;
366    /// # use arrow_buffer::ScalarBuffer;
367    /// # fn main() -> Result<(), arrow_schema::ArrowError> {
368    /// let mut builder = UnionBuilder::new_dense();
369    /// builder.append::<Int32Type>("a", 1).unwrap();
370    /// let union_array = builder.build()?;
371    ///
372    /// // Deconstruct into parts
373    /// let (union_fields, type_ids, offsets, children) = union_array.into_parts();
374    ///
375    /// // Reconstruct from parts
376    /// let union_array = UnionArray::try_new(
377    ///     union_fields,
378    ///     type_ids,
379    ///     offsets,
380    ///     children,
381    /// );
382    /// # Ok(())
383    /// # }
384    /// ```
385    pub fn into_parts(
386        self,
387    ) -> (
388        UnionFields,
389        ScalarBuffer<i8>,
390        Option<ScalarBuffer<i32>>,
391        Vec<ArrayRef>,
392    ) {
393        let Self {
394            data_type,
395            type_ids,
396            offsets,
397            mut fields,
398        } = self;
399        match data_type {
400            DataType::Union(union_fields, _) => {
401                let children = union_fields
402                    .iter()
403                    .map(|(type_id, _)| fields[type_id as usize].take().unwrap())
404                    .collect();
405                (union_fields, type_ids, offsets, children)
406            }
407            _ => unreachable!(),
408        }
409    }
410
411    /// Computes the logical nulls for a sparse union, optimized for when there's a lot of fields without nulls
412    fn mask_sparse_skip_without_nulls(&self, nulls: Vec<(i8, NullBuffer)>) -> BooleanBuffer {
413        // Example logic for a union with 5 fields, a, b & c with nulls, d & e without nulls:
414        // let [a_nulls, b_nulls, c_nulls] = nulls;
415        // let [is_a, is_b, is_c] = masks;
416        // let is_d_or_e = !(is_a | is_b | is_c)
417        // let union_chunk_nulls = is_d_or_e  | (is_a & a_nulls) | (is_b & b_nulls) | (is_c & c_nulls)
418        let fold = |(with_nulls_selected, union_nulls), (is_field, field_nulls)| {
419            (
420                with_nulls_selected | is_field,
421                union_nulls | (is_field & field_nulls),
422            )
423        };
424
425        self.mask_sparse_helper(
426            nulls,
427            |type_ids_chunk_array, nulls_masks_iters| {
428                let (with_nulls_selected, union_nulls) = nulls_masks_iters
429                    .iter_mut()
430                    .map(|(field_type_id, field_nulls)| {
431                        let field_nulls = field_nulls.next().unwrap();
432                        let is_field = selection_mask(type_ids_chunk_array, *field_type_id);
433
434                        (is_field, field_nulls)
435                    })
436                    .fold((0, 0), fold);
437
438                // In the example above, this is the is_d_or_e = !(is_a | is_b) part
439                let without_nulls_selected = !with_nulls_selected;
440
441                // if a field without nulls is selected, the value is always true(set bit)
442                // otherwise, the true/set bits have been computed above
443                without_nulls_selected | union_nulls
444            },
445            |type_ids_remainder, bit_chunks| {
446                let (with_nulls_selected, union_nulls) = bit_chunks
447                    .iter()
448                    .map(|(field_type_id, field_bit_chunks)| {
449                        let field_nulls = field_bit_chunks.remainder_bits();
450                        let is_field = selection_mask(type_ids_remainder, *field_type_id);
451
452                        (is_field, field_nulls)
453                    })
454                    .fold((0, 0), fold);
455
456                let without_nulls_selected = !with_nulls_selected;
457
458                without_nulls_selected | union_nulls
459            },
460        )
461    }
462
463    /// Computes the logical nulls for a sparse union, optimized for when there's a lot of fields fully null
464    fn mask_sparse_skip_fully_null(&self, mut nulls: Vec<(i8, NullBuffer)>) -> BooleanBuffer {
465        let DataType::Union(fields, _) = self.data_type() else {
466            unreachable!("Union array's data type is not a union!")
467        };
468
469        let type_ids = fields.iter().map(|(id, _)| id).collect::<HashSet<_>>();
470        let with_nulls = nulls.iter().map(|(id, _)| *id).collect::<HashSet<_>>();
471
472        let without_nulls_ids = type_ids
473            .difference(&with_nulls)
474            .copied()
475            .collect::<Vec<_>>();
476
477        nulls.retain(|(_, nulls)| nulls.null_count() < nulls.len());
478
479        // Example logic for a union with 6 fields, a, b & c with nulls, d & e without nulls, and f fully_null:
480        // let [a_nulls, b_nulls, c_nulls] = nulls;
481        // let [is_a, is_b, is_c, is_d, is_e] = masks;
482        // let union_chunk_nulls = is_d | is_e | (is_a & a_nulls) | (is_b & b_nulls) | (is_c & c_nulls)
483        self.mask_sparse_helper(
484            nulls,
485            |type_ids_chunk_array, nulls_masks_iters| {
486                let union_nulls = nulls_masks_iters.iter_mut().fold(
487                    0,
488                    |union_nulls, (field_type_id, nulls_iter)| {
489                        let field_nulls = nulls_iter.next().unwrap();
490
491                        if field_nulls == 0 {
492                            union_nulls
493                        } else {
494                            let is_field = selection_mask(type_ids_chunk_array, *field_type_id);
495
496                            union_nulls | (is_field & field_nulls)
497                        }
498                    },
499                );
500
501                // Given the example above, this is the is_d_or_e = (is_d | is_e) part
502                let without_nulls_selected =
503                    without_nulls_selected(type_ids_chunk_array, &without_nulls_ids);
504
505                // if a field without nulls is selected, the value is always true(set bit)
506                // otherwise, the true/set bits have been computed above
507                union_nulls | without_nulls_selected
508            },
509            |type_ids_remainder, bit_chunks| {
510                let union_nulls =
511                    bit_chunks
512                        .iter()
513                        .fold(0, |union_nulls, (field_type_id, field_bit_chunks)| {
514                            let is_field = selection_mask(type_ids_remainder, *field_type_id);
515                            let field_nulls = field_bit_chunks.remainder_bits();
516
517                            union_nulls | is_field & field_nulls
518                        });
519
520                union_nulls | without_nulls_selected(type_ids_remainder, &without_nulls_ids)
521            },
522        )
523    }
524
525    /// Computes the logical nulls for a sparse union, optimized for when all fields contains nulls
526    fn mask_sparse_all_with_nulls_skip_one(&self, nulls: Vec<(i8, NullBuffer)>) -> BooleanBuffer {
527        // Example logic for a union with 3 fields, a, b & c, all containing nulls:
528        // let [a_nulls, b_nulls, c_nulls] = nulls;
529        // We can skip the first field: it's selection mask is the negation of all others selection mask
530        // let [is_b, is_c] = selection_masks;
531        // let is_a = !(is_b | is_c)
532        // let union_chunk_nulls = (is_a & a_nulls) | (is_b & b_nulls) | (is_c & c_nulls)
533        self.mask_sparse_helper(
534            nulls,
535            |type_ids_chunk_array, nulls_masks_iters| {
536                let (is_not_first, union_nulls) = nulls_masks_iters[1..] // skip first
537                    .iter_mut()
538                    .fold(
539                        (0, 0),
540                        |(is_not_first, union_nulls), (field_type_id, nulls_iter)| {
541                            let field_nulls = nulls_iter.next().unwrap();
542                            let is_field = selection_mask(type_ids_chunk_array, *field_type_id);
543
544                            (
545                                is_not_first | is_field,
546                                union_nulls | (is_field & field_nulls),
547                            )
548                        },
549                    );
550
551                let is_first = !is_not_first;
552                let first_nulls = nulls_masks_iters[0].1.next().unwrap();
553
554                (is_first & first_nulls) | union_nulls
555            },
556            |type_ids_remainder, bit_chunks| {
557                bit_chunks
558                    .iter()
559                    .fold(0, |union_nulls, (field_type_id, field_bit_chunks)| {
560                        let field_nulls = field_bit_chunks.remainder_bits();
561                        // The same logic as above, except that since this runs at most once,
562                        // it doesn't make difference to speed-up the first selection mask
563                        let is_field = selection_mask(type_ids_remainder, *field_type_id);
564
565                        union_nulls | (is_field & field_nulls)
566                    })
567            },
568        )
569    }
570
571    /// Maps `nulls` to `BitChunk's` and then to `BitChunkIterator's`, then divides `self.type_ids` into exact chunks of 64 values,
572    /// calling `mask_chunk` for every exact chunk, and `mask_remainder` for the remainder, if any, collecting the result in a `BooleanBuffer`
573    fn mask_sparse_helper(
574        &self,
575        nulls: Vec<(i8, NullBuffer)>,
576        mut mask_chunk: impl FnMut(&[i8; 64], &mut [(i8, BitChunkIterator)]) -> u64,
577        mask_remainder: impl FnOnce(&[i8], &[(i8, BitChunks)]) -> u64,
578    ) -> BooleanBuffer {
579        let bit_chunks = nulls
580            .iter()
581            .map(|(type_id, nulls)| (*type_id, nulls.inner().bit_chunks()))
582            .collect::<Vec<_>>();
583
584        let mut nulls_masks_iter = bit_chunks
585            .iter()
586            .map(|(type_id, bit_chunks)| (*type_id, bit_chunks.iter()))
587            .collect::<Vec<_>>();
588
589        let (chunks_exact, remainder) = self.type_ids.as_chunks::<64>();
590
591        let chunks = chunks_exact
592            .iter()
593            .map(|type_ids_chunk| mask_chunk(type_ids_chunk, &mut nulls_masks_iter));
594
595        // SAFETY:
596        // chunks is a ChunksExact iterator, which implements TrustedLen, and correctly reports its length
597        let mut buffer = unsafe { MutableBuffer::from_trusted_len_iter(chunks) };
598
599        if !remainder.is_empty() {
600            buffer.push(mask_remainder(remainder, &bit_chunks));
601        }
602
603        BooleanBuffer::new(buffer.into(), 0, self.type_ids.len())
604    }
605
606    /// Computes the logical nulls for a sparse or dense union, by gathering individual bits from the null buffer of the selected field
607    fn gather_nulls(&self, nulls: Vec<(i8, NullBuffer)>) -> BooleanBuffer {
608        let one_null = NullBuffer::new_null(1);
609        let one_valid = NullBuffer::new_valid(1);
610
611        // Unsafe code below depend on it:
612        // To remove one branch from the loop, if the a type_id is not utilized, or it's logical_nulls is None/all set,
613        // we use a null buffer of len 1 and a index_mask of 0, or the true null buffer and usize::MAX otherwise.
614        // We then unconditionally access the null buffer with index & index_mask,
615        // which always return 0 for the 1-len buffer, or the true index unchanged otherwise
616        // We also use a 256 array, so llvm knows that `type_id as u8 as usize` is always in bounds
617        let mut logical_nulls_array = [(&one_valid, Mask::Zero); 256];
618
619        for (type_id, nulls) in &nulls {
620            if nulls.null_count() == nulls.len() {
621                // Similarly, if all values are null, use a 1-null null-buffer to reduce cache pressure a bit
622                logical_nulls_array[*type_id as u8 as usize] = (&one_null, Mask::Zero);
623            } else {
624                logical_nulls_array[*type_id as u8 as usize] = (nulls, Mask::Max);
625            }
626        }
627
628        match &self.offsets {
629            Some(offsets) => {
630                assert_eq!(self.type_ids.len(), offsets.len());
631
632                BooleanBuffer::collect_bool(self.type_ids.len(), |i| unsafe {
633                    // SAFETY: BooleanBuffer::collect_bool calls us 0..self.type_ids.len()
634                    let type_id = *self.type_ids.get_unchecked(i);
635                    // SAFETY: We asserted that offsets len and self.type_ids len are equal
636                    let offset = *offsets.get_unchecked(i);
637
638                    let (nulls, offset_mask) = &logical_nulls_array[type_id as u8 as usize];
639
640                    // SAFETY:
641                    // If offset_mask is Max
642                    // 1. Offset validity is checked at union creation
643                    // 2. If the null buffer len equals it's array len is checked at array creation
644                    // If offset_mask is Zero, the null buffer len is 1
645                    nulls
646                        .inner()
647                        .value_unchecked(offset as usize & *offset_mask as usize)
648                })
649            }
650            None => {
651                BooleanBuffer::collect_bool(self.type_ids.len(), |index| unsafe {
652                    // SAFETY: BooleanBuffer::collect_bool calls us 0..self.type_ids.len()
653                    let type_id = *self.type_ids.get_unchecked(index);
654
655                    let (nulls, index_mask) = &logical_nulls_array[type_id as u8 as usize];
656
657                    // SAFETY:
658                    // If index_mask is Max
659                    // 1. On sparse union, every child len match it's parent, this is checked at union creation
660                    // 2. If the null buffer len equals it's array len is checked at array creation
661                    // If index_mask is Zero, the null buffer len is 1
662                    nulls.inner().value_unchecked(index & *index_mask as usize)
663                })
664            }
665        }
666    }
667
668    /// Returns a vector of tuples containing each field's type_id and its logical null buffer.
669    /// Only fields with non-zero null counts are included.
670    fn fields_logical_nulls(&self) -> Vec<(i8, NullBuffer)> {
671        self.fields
672            .iter()
673            .enumerate()
674            .filter_map(|(type_id, field)| Some((type_id as i8, field.as_ref()?.logical_nulls()?)))
675            .filter(|(_, nulls)| nulls.null_count() > 0)
676            .collect()
677    }
678}
679
680impl From<ArrayData> for UnionArray {
681    fn from(data: ArrayData) -> Self {
682        let (data_type, len, _nulls, offset, buffers, child_data) = data.into_parts();
683
684        let (fields, mode) = match &data_type {
685            DataType::Union(fields, mode) => (fields, mode),
686            d => panic!("UnionArray expected ArrayData with type Union got {d}"),
687        };
688
689        let (type_ids, offsets) = match mode {
690            UnionMode::Sparse => {
691                let [buffer]: [Buffer; 1] = buffers.try_into().expect("1 buffer for type_ids");
692                (ScalarBuffer::new(buffer, offset, len), None)
693            }
694            UnionMode::Dense => {
695                let [type_ids_buffer, offsets_buffer]: [Buffer; 2] = buffers
696                    .try_into()
697                    .expect("2 buffers for type_ids and offsets");
698                (
699                    ScalarBuffer::new(type_ids_buffer, offset, len),
700                    Some(ScalarBuffer::new(offsets_buffer, offset, len)),
701                )
702            }
703        };
704
705        let max_id = fields.iter().map(|(i, _)| i).max().unwrap_or_default() as usize;
706        let mut boxed_fields = vec![None; max_id + 1];
707        for (cd, (field_id, _)) in child_data.into_iter().zip(fields.iter()) {
708            let cd = match mode {
709                UnionMode::Sparse => cd.sliced(offset, len),
710                UnionMode::Dense => cd,
711            };
712            boxed_fields[field_id as usize] = Some(make_array(cd));
713        }
714        Self {
715            data_type,
716            type_ids,
717            offsets,
718            fields: boxed_fields,
719        }
720    }
721}
722
723impl From<UnionArray> for ArrayData {
724    fn from(array: UnionArray) -> Self {
725        let len = array.len();
726        let DataType::Union(f, _) = &array.data_type else {
727            unreachable!()
728        };
729        let buffers = match array.offsets {
730            Some(o) => vec![array.type_ids.into_inner(), o.into_inner()],
731            None => vec![array.type_ids.into_inner()],
732        };
733
734        let child = f
735            .iter()
736            .map(|(i, _)| array.fields[i as usize].as_ref().unwrap().to_data())
737            .collect();
738
739        let builder = ArrayDataBuilder::new(array.data_type)
740            .len(len)
741            .buffers(buffers)
742            .child_data(child);
743        unsafe { builder.build_unchecked() }
744    }
745}
746
747/// SAFETY: Correctly implements the contract of Arrow Arrays
748unsafe impl Array for UnionArray {
749    fn as_any(&self) -> &dyn Any {
750        self
751    }
752
753    fn to_data(&self) -> ArrayData {
754        self.clone().into()
755    }
756
757    fn into_data(self) -> ArrayData {
758        self.into()
759    }
760
761    fn data_type(&self) -> &DataType {
762        &self.data_type
763    }
764
765    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
766        Arc::new(self.slice(offset, length))
767    }
768
769    fn len(&self) -> usize {
770        self.type_ids.len()
771    }
772
773    fn is_empty(&self) -> bool {
774        self.type_ids.is_empty()
775    }
776
777    fn shrink_to_fit(&mut self) {
778        self.type_ids.shrink_to_fit();
779        if let Some(offsets) = &mut self.offsets {
780            offsets.shrink_to_fit();
781        }
782        for array in self.fields.iter_mut().flatten() {
783            array.shrink_to_fit();
784        }
785        self.fields.shrink_to_fit();
786    }
787
788    fn offset(&self) -> usize {
789        0
790    }
791
792    fn nulls(&self) -> Option<&NullBuffer> {
793        None
794    }
795
796    fn logical_nulls(&self) -> Option<NullBuffer> {
797        let DataType::Union(fields, _) = self.data_type() else {
798            unreachable!()
799        };
800
801        if fields.len() <= 1 {
802            return self
803                .fields
804                .iter()
805                .enumerate()
806                .find_map(|(type_id, field_opt)| {
807                    field_opt
808                        .as_ref()
809                        .and_then(|field| field.logical_nulls())
810                        .map(|logical_nulls| {
811                            if self.is_dense() {
812                                self.gather_nulls(vec![(type_id as i8, logical_nulls)])
813                                    .into()
814                            } else {
815                                logical_nulls
816                            }
817                        })
818                });
819        }
820
821        let logical_nulls = self.fields_logical_nulls();
822
823        if logical_nulls.is_empty() {
824            return None;
825        }
826
827        let fully_null_count = logical_nulls
828            .iter()
829            .filter(|(_, nulls)| nulls.null_count() == nulls.len())
830            .count();
831
832        if fully_null_count == fields.len() {
833            if let Some((_, exactly_sized)) = logical_nulls
834                .iter()
835                .find(|(_, nulls)| nulls.len() == self.len())
836            {
837                return Some(exactly_sized.clone());
838            }
839
840            if let Some((_, bigger)) = logical_nulls
841                .iter()
842                .find(|(_, nulls)| nulls.len() > self.len())
843            {
844                return Some(bigger.slice(0, self.len()));
845            }
846
847            return Some(NullBuffer::new_null(self.len()));
848        }
849
850        let boolean_buffer = match &self.offsets {
851            Some(_) => self.gather_nulls(logical_nulls),
852            None => {
853                // Choose the fastest way to compute the logical nulls
854                // Gather computes one null per iteration, while the others work on 64 nulls chunks,
855                // but must also compute selection masks, which is expensive,
856                // so it's cost is the number of selection masks computed per chunk
857                // Since computing the selection mask gets auto-vectorized, it's performance depends on which simd feature is enabled
858                // For gather, the cost is the threshold where masking becomes slower than gather, which is determined with benchmarks
859                // TODO: bench on avx512f(feature is still unstable)
860                let gather_relative_cost = if cfg!(target_feature = "avx2") {
861                    10
862                } else if cfg!(target_feature = "sse4.1") {
863                    3
864                } else if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
865                    // x86 baseline includes sse2
866                    2
867                } else {
868                    // TODO: bench on non x86
869                    // Always use gather on non benchmarked archs because even though it may slower on some cases,
870                    // it's performance depends only on the union length, without being affected by the number of fields
871                    0
872                };
873
874                let strategies = [
875                    (SparseStrategy::Gather, gather_relative_cost, true),
876                    (
877                        SparseStrategy::MaskAllFieldsWithNullsSkipOne,
878                        fields.len() - 1,
879                        fields.len() == logical_nulls.len(),
880                    ),
881                    (
882                        SparseStrategy::MaskSkipWithoutNulls,
883                        logical_nulls.len(),
884                        true,
885                    ),
886                    (
887                        SparseStrategy::MaskSkipFullyNull,
888                        fields.len() - fully_null_count,
889                        true,
890                    ),
891                ];
892
893                let (strategy, _, _) = strategies
894                    .iter()
895                    .filter(|(_, _, applicable)| *applicable)
896                    .min_by_key(|(_, cost, _)| cost)
897                    .unwrap();
898
899                match strategy {
900                    SparseStrategy::Gather => self.gather_nulls(logical_nulls),
901                    SparseStrategy::MaskAllFieldsWithNullsSkipOne => {
902                        self.mask_sparse_all_with_nulls_skip_one(logical_nulls)
903                    }
904                    SparseStrategy::MaskSkipWithoutNulls => {
905                        self.mask_sparse_skip_without_nulls(logical_nulls)
906                    }
907                    SparseStrategy::MaskSkipFullyNull => {
908                        self.mask_sparse_skip_fully_null(logical_nulls)
909                    }
910                }
911            }
912        };
913
914        let null_buffer = NullBuffer::from(boolean_buffer);
915
916        if null_buffer.null_count() > 0 {
917            Some(null_buffer)
918        } else {
919            None
920        }
921    }
922
923    fn is_nullable(&self) -> bool {
924        self.fields
925            .iter()
926            .flatten()
927            .any(|field| field.is_nullable())
928    }
929
930    fn get_buffer_memory_size(&self) -> usize {
931        let mut sum = self.type_ids.inner().capacity();
932        if let Some(o) = self.offsets.as_ref() {
933            sum += o.inner().capacity()
934        }
935        self.fields
936            .iter()
937            .filter_map(|x| x.as_ref().map(|x| x.get_buffer_memory_size()))
938            .sum::<usize>()
939            + sum
940    }
941
942    fn get_array_memory_size(&self) -> usize {
943        let mut sum = self.type_ids.inner().capacity();
944        if let Some(o) = self.offsets.as_ref() {
945            sum += o.inner().capacity()
946        }
947        std::mem::size_of::<Self>()
948            + self
949                .fields
950                .iter()
951                .filter_map(|x| x.as_ref().map(|x| x.get_array_memory_size()))
952                .sum::<usize>()
953            + sum
954    }
955
956    #[cfg(feature = "pool")]
957    fn claim(&self, pool: &dyn arrow_buffer::MemoryPool) {
958        self.type_ids.claim(pool);
959        if let Some(offsets) = &self.offsets {
960            offsets.claim(pool);
961        }
962        for field in self.fields.iter().flatten() {
963            field.claim(pool);
964        }
965    }
966}
967
968impl std::fmt::Debug for UnionArray {
969    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
970        let header = if self.is_dense() {
971            "UnionArray(Dense)\n["
972        } else {
973            "UnionArray(Sparse)\n["
974        };
975        writeln!(f, "{header}")?;
976
977        writeln!(f, "-- type id buffer:")?;
978        writeln!(f, "{:?}", self.type_ids)?;
979
980        if let Some(offsets) = &self.offsets {
981            writeln!(f, "-- offsets buffer:")?;
982            writeln!(f, "{offsets:?}")?;
983        }
984
985        let DataType::Union(fields, _) = self.data_type() else {
986            unreachable!()
987        };
988
989        for (type_id, field) in fields.iter() {
990            let child = self.child(type_id);
991            writeln!(
992                f,
993                "-- child {}: \"{}\" ({:?})",
994                type_id,
995                field.name(),
996                field.data_type()
997            )?;
998            std::fmt::Debug::fmt(child, f)?;
999            writeln!(f)?;
1000        }
1001        writeln!(f, "]")
1002    }
1003}
1004
1005/// How to compute the logical nulls of a sparse union. All strategies return the same result.
1006/// Those starting with Mask perform bitwise masking for each chunk of 64 values, including
1007/// computing expensive selection masks of fields: which fields masks must be computed is the
1008/// difference between them
1009enum SparseStrategy {
1010    /// Gather individual bits from the null buffer of the selected field
1011    Gather,
1012    /// All fields contains nulls, so we can skip the selection mask computation of one field by negating the others
1013    MaskAllFieldsWithNullsSkipOne,
1014    /// Skip the selection mask computation of the fields without nulls
1015    MaskSkipWithoutNulls,
1016    /// Skip the selection mask computation of the fully nulls fields
1017    MaskSkipFullyNull,
1018}
1019
1020#[derive(Copy, Clone)]
1021#[repr(usize)]
1022enum Mask {
1023    Zero = 0,
1024    Max = usize::MAX,
1025}
1026
1027fn selection_mask(type_ids_chunk: &[i8], type_id: i8) -> u64 {
1028    type_ids_chunk
1029        .iter()
1030        .copied()
1031        .enumerate()
1032        .fold(0, |packed, (bit_idx, v)| {
1033            packed | (((v == type_id) as u64) << bit_idx)
1034        })
1035}
1036
1037/// Returns a bitmask where bits indicate if any id from `without_nulls_ids` exist in `type_ids_chunk`.
1038fn without_nulls_selected(type_ids_chunk: &[i8], without_nulls_ids: &[i8]) -> u64 {
1039    without_nulls_ids
1040        .iter()
1041        .fold(0, |fully_valid_selected, field_type_id| {
1042            fully_valid_selected | selection_mask(type_ids_chunk, *field_type_id)
1043        })
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048    use super::*;
1049    use std::collections::HashSet;
1050
1051    use crate::array::Int8Type;
1052    use crate::builder::UnionBuilder;
1053    use crate::cast::AsArray;
1054    use crate::types::{Float32Type, Float64Type, Int32Type, Int64Type};
1055    use crate::{Float64Array, Int32Array, Int64Array, NullArray, StringArray};
1056    use crate::{Int8Array, RecordBatch};
1057    use arrow_buffer::Buffer;
1058    use arrow_schema::{Field, Schema};
1059
1060    #[test]
1061    fn test_dense_i32() {
1062        let mut builder = UnionBuilder::new_dense();
1063        builder.append::<Int32Type>("a", 1).unwrap();
1064        builder.append::<Int32Type>("b", 2).unwrap();
1065        builder.append::<Int32Type>("c", 3).unwrap();
1066        builder.append::<Int32Type>("a", 4).unwrap();
1067        builder.append::<Int32Type>("c", 5).unwrap();
1068        builder.append::<Int32Type>("a", 6).unwrap();
1069        builder.append::<Int32Type>("b", 7).unwrap();
1070        let union = builder.build().unwrap();
1071
1072        let expected_type_ids = vec![0_i8, 1, 2, 0, 2, 0, 1];
1073        let expected_offsets = vec![0_i32, 0, 0, 1, 1, 2, 1];
1074        let expected_array_values = [1_i32, 2, 3, 4, 5, 6, 7];
1075
1076        // Check type ids
1077        assert_eq!(*union.type_ids(), expected_type_ids);
1078        for (i, id) in expected_type_ids.iter().enumerate() {
1079            assert_eq!(id, &union.type_id(i));
1080        }
1081
1082        // Check offsets
1083        assert_eq!(*union.offsets().unwrap(), expected_offsets);
1084        for (i, id) in expected_offsets.iter().enumerate() {
1085            assert_eq!(union.value_offset(i), *id as usize);
1086        }
1087
1088        // Check data
1089        assert_eq!(
1090            *union.child(0).as_primitive::<Int32Type>().values(),
1091            [1_i32, 4, 6]
1092        );
1093        assert_eq!(
1094            *union.child(1).as_primitive::<Int32Type>().values(),
1095            [2_i32, 7]
1096        );
1097        assert_eq!(
1098            *union.child(2).as_primitive::<Int32Type>().values(),
1099            [3_i32, 5]
1100        );
1101
1102        assert_eq!(expected_array_values.len(), union.len());
1103        for (i, expected_value) in expected_array_values.iter().enumerate() {
1104            assert!(!union.is_null(i));
1105            let slot = union.value(i);
1106            let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1107            assert_eq!(slot.len(), 1);
1108            let value = slot.value(0);
1109            assert_eq!(expected_value, &value);
1110        }
1111    }
1112
1113    #[test]
1114    fn slice_union_array_single_field() {
1115        // Dense Union
1116        // [1, null, 3, null, 4]
1117        let union_array = {
1118            let mut builder = UnionBuilder::new_dense();
1119            builder.append::<Int32Type>("a", 1).unwrap();
1120            builder.append_null::<Int32Type>("a").unwrap();
1121            builder.append::<Int32Type>("a", 3).unwrap();
1122            builder.append_null::<Int32Type>("a").unwrap();
1123            builder.append::<Int32Type>("a", 4).unwrap();
1124            builder.build().unwrap()
1125        };
1126
1127        // [null, 3, null]
1128        let union_slice = union_array.slice(1, 3);
1129        let logical_nulls = union_slice.logical_nulls().unwrap();
1130
1131        assert_eq!(logical_nulls.len(), 3);
1132        assert!(logical_nulls.is_null(0));
1133        assert!(logical_nulls.is_valid(1));
1134        assert!(logical_nulls.is_null(2));
1135    }
1136
1137    #[test]
1138    fn test_dense_i32_large() {
1139        let mut builder = UnionBuilder::new_dense();
1140
1141        let expected_type_ids = vec![0_i8; 1024];
1142        let expected_offsets: Vec<_> = (0..1024).collect();
1143        let expected_array_values: Vec<_> = (1..=1024).collect();
1144
1145        expected_array_values
1146            .iter()
1147            .for_each(|v| builder.append::<Int32Type>("a", *v).unwrap());
1148
1149        let union = builder.build().unwrap();
1150
1151        // Check type ids
1152        assert_eq!(*union.type_ids(), expected_type_ids);
1153        for (i, id) in expected_type_ids.iter().enumerate() {
1154            assert_eq!(id, &union.type_id(i));
1155        }
1156
1157        // Check offsets
1158        assert_eq!(*union.offsets().unwrap(), expected_offsets);
1159        for (i, id) in expected_offsets.iter().enumerate() {
1160            assert_eq!(union.value_offset(i), *id as usize);
1161        }
1162
1163        for (i, expected_value) in expected_array_values.iter().enumerate() {
1164            assert!(!union.is_null(i));
1165            let slot = union.value(i);
1166            let slot = slot.as_primitive::<Int32Type>();
1167            assert_eq!(slot.len(), 1);
1168            let value = slot.value(0);
1169            assert_eq!(expected_value, &value);
1170        }
1171    }
1172
1173    #[test]
1174    fn test_dense_mixed() {
1175        let mut builder = UnionBuilder::new_dense();
1176        builder.append::<Int32Type>("a", 1).unwrap();
1177        builder.append::<Int64Type>("c", 3).unwrap();
1178        builder.append::<Int32Type>("a", 4).unwrap();
1179        builder.append::<Int64Type>("c", 5).unwrap();
1180        builder.append::<Int32Type>("a", 6).unwrap();
1181        let union = builder.build().unwrap();
1182
1183        assert_eq!(5, union.len());
1184        for i in 0..union.len() {
1185            let slot = union.value(i);
1186            assert!(!union.is_null(i));
1187            match i {
1188                0 => {
1189                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1190                    assert_eq!(slot.len(), 1);
1191                    let value = slot.value(0);
1192                    assert_eq!(1_i32, value);
1193                }
1194                1 => {
1195                    let slot = slot.as_any().downcast_ref::<Int64Array>().unwrap();
1196                    assert_eq!(slot.len(), 1);
1197                    let value = slot.value(0);
1198                    assert_eq!(3_i64, value);
1199                }
1200                2 => {
1201                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1202                    assert_eq!(slot.len(), 1);
1203                    let value = slot.value(0);
1204                    assert_eq!(4_i32, value);
1205                }
1206                3 => {
1207                    let slot = slot.as_any().downcast_ref::<Int64Array>().unwrap();
1208                    assert_eq!(slot.len(), 1);
1209                    let value = slot.value(0);
1210                    assert_eq!(5_i64, value);
1211                }
1212                4 => {
1213                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1214                    assert_eq!(slot.len(), 1);
1215                    let value = slot.value(0);
1216                    assert_eq!(6_i32, value);
1217                }
1218                _ => unreachable!(),
1219            }
1220        }
1221    }
1222
1223    #[test]
1224    fn test_dense_mixed_with_nulls() {
1225        let mut builder = UnionBuilder::new_dense();
1226        builder.append::<Int32Type>("a", 1).unwrap();
1227        builder.append::<Int64Type>("c", 3).unwrap();
1228        builder.append::<Int32Type>("a", 10).unwrap();
1229        builder.append_null::<Int32Type>("a").unwrap();
1230        builder.append::<Int32Type>("a", 6).unwrap();
1231        let union = builder.build().unwrap();
1232
1233        assert_eq!(5, union.len());
1234        for i in 0..union.len() {
1235            let slot = union.value(i);
1236            match i {
1237                0 => {
1238                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1239                    assert!(!slot.is_null(0));
1240                    assert_eq!(slot.len(), 1);
1241                    let value = slot.value(0);
1242                    assert_eq!(1_i32, value);
1243                }
1244                1 => {
1245                    let slot = slot.as_any().downcast_ref::<Int64Array>().unwrap();
1246                    assert!(!slot.is_null(0));
1247                    assert_eq!(slot.len(), 1);
1248                    let value = slot.value(0);
1249                    assert_eq!(3_i64, value);
1250                }
1251                2 => {
1252                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1253                    assert!(!slot.is_null(0));
1254                    assert_eq!(slot.len(), 1);
1255                    let value = slot.value(0);
1256                    assert_eq!(10_i32, value);
1257                }
1258                3 => assert!(slot.is_null(0)),
1259                4 => {
1260                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1261                    assert!(!slot.is_null(0));
1262                    assert_eq!(slot.len(), 1);
1263                    let value = slot.value(0);
1264                    assert_eq!(6_i32, value);
1265                }
1266                _ => unreachable!(),
1267            }
1268        }
1269    }
1270
1271    #[test]
1272    fn test_dense_mixed_with_nulls_and_offset() {
1273        let mut builder = UnionBuilder::new_dense();
1274        builder.append::<Int32Type>("a", 1).unwrap();
1275        builder.append::<Int64Type>("c", 3).unwrap();
1276        builder.append::<Int32Type>("a", 10).unwrap();
1277        builder.append_null::<Int32Type>("a").unwrap();
1278        builder.append::<Int32Type>("a", 6).unwrap();
1279        let union = builder.build().unwrap();
1280
1281        let slice = union.slice(2, 3);
1282        let new_union = slice.as_any().downcast_ref::<UnionArray>().unwrap();
1283
1284        assert_eq!(3, new_union.len());
1285        for i in 0..new_union.len() {
1286            let slot = new_union.value(i);
1287            match i {
1288                0 => {
1289                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1290                    assert!(!slot.is_null(0));
1291                    assert_eq!(slot.len(), 1);
1292                    let value = slot.value(0);
1293                    assert_eq!(10_i32, value);
1294                }
1295                1 => assert!(slot.is_null(0)),
1296                2 => {
1297                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1298                    assert!(!slot.is_null(0));
1299                    assert_eq!(slot.len(), 1);
1300                    let value = slot.value(0);
1301                    assert_eq!(6_i32, value);
1302                }
1303                _ => unreachable!(),
1304            }
1305        }
1306    }
1307
1308    #[test]
1309    fn test_dense_mixed_with_str() {
1310        let string_array = StringArray::from(vec!["foo", "bar", "baz"]);
1311        let int_array = Int32Array::from(vec![5, 6]);
1312        let float_array = Float64Array::from(vec![10.0]);
1313
1314        let type_ids = [1, 0, 0, 2, 0, 1].into_iter().collect::<ScalarBuffer<i8>>();
1315        let offsets = [0, 0, 1, 0, 2, 1]
1316            .into_iter()
1317            .collect::<ScalarBuffer<i32>>();
1318
1319        let fields = [
1320            (0, Arc::new(Field::new("A", DataType::Utf8, false))),
1321            (1, Arc::new(Field::new("B", DataType::Int32, false))),
1322            (2, Arc::new(Field::new("C", DataType::Float64, false))),
1323        ]
1324        .into_iter()
1325        .collect::<UnionFields>();
1326        let children = [
1327            Arc::new(string_array) as Arc<dyn Array>,
1328            Arc::new(int_array),
1329            Arc::new(float_array),
1330        ]
1331        .into_iter()
1332        .collect();
1333        let array =
1334            UnionArray::try_new(fields, type_ids.clone(), Some(offsets.clone()), children).unwrap();
1335
1336        // Check type ids
1337        assert_eq!(*array.type_ids(), type_ids);
1338        for (i, id) in type_ids.iter().enumerate() {
1339            assert_eq!(id, &array.type_id(i));
1340        }
1341
1342        // Check offsets
1343        assert_eq!(*array.offsets().unwrap(), offsets);
1344        for (i, id) in offsets.iter().enumerate() {
1345            assert_eq!(*id as usize, array.value_offset(i));
1346        }
1347
1348        // Check values
1349        assert_eq!(6, array.len());
1350
1351        let slot = array.value(0);
1352        let value = slot.as_any().downcast_ref::<Int32Array>().unwrap().value(0);
1353        assert_eq!(5, value);
1354
1355        let slot = array.value(1);
1356        let value = slot
1357            .as_any()
1358            .downcast_ref::<StringArray>()
1359            .unwrap()
1360            .value(0);
1361        assert_eq!("foo", value);
1362
1363        let slot = array.value(2);
1364        let value = slot
1365            .as_any()
1366            .downcast_ref::<StringArray>()
1367            .unwrap()
1368            .value(0);
1369        assert_eq!("bar", value);
1370
1371        let slot = array.value(3);
1372        let value = slot
1373            .as_any()
1374            .downcast_ref::<Float64Array>()
1375            .unwrap()
1376            .value(0);
1377        assert_eq!(10.0, value);
1378
1379        let slot = array.value(4);
1380        let value = slot
1381            .as_any()
1382            .downcast_ref::<StringArray>()
1383            .unwrap()
1384            .value(0);
1385        assert_eq!("baz", value);
1386
1387        let slot = array.value(5);
1388        let value = slot.as_any().downcast_ref::<Int32Array>().unwrap().value(0);
1389        assert_eq!(6, value);
1390    }
1391
1392    #[test]
1393    fn test_sparse_i32() {
1394        let mut builder = UnionBuilder::new_sparse();
1395        builder.append::<Int32Type>("a", 1).unwrap();
1396        builder.append::<Int32Type>("b", 2).unwrap();
1397        builder.append::<Int32Type>("c", 3).unwrap();
1398        builder.append::<Int32Type>("a", 4).unwrap();
1399        builder.append::<Int32Type>("c", 5).unwrap();
1400        builder.append::<Int32Type>("a", 6).unwrap();
1401        builder.append::<Int32Type>("b", 7).unwrap();
1402        let union = builder.build().unwrap();
1403
1404        let expected_type_ids = vec![0_i8, 1, 2, 0, 2, 0, 1];
1405        let expected_array_values = [1_i32, 2, 3, 4, 5, 6, 7];
1406
1407        // Check type ids
1408        assert_eq!(*union.type_ids(), expected_type_ids);
1409        for (i, id) in expected_type_ids.iter().enumerate() {
1410            assert_eq!(id, &union.type_id(i));
1411        }
1412
1413        // Check offsets, sparse union should only have a single buffer
1414        assert!(union.offsets().is_none());
1415
1416        // Check data
1417        assert_eq!(
1418            *union.child(0).as_primitive::<Int32Type>().values(),
1419            [1_i32, 0, 0, 4, 0, 6, 0],
1420        );
1421        assert_eq!(
1422            *union.child(1).as_primitive::<Int32Type>().values(),
1423            [0_i32, 2_i32, 0, 0, 0, 0, 7]
1424        );
1425        assert_eq!(
1426            *union.child(2).as_primitive::<Int32Type>().values(),
1427            [0_i32, 0, 3_i32, 0, 5, 0, 0]
1428        );
1429
1430        assert_eq!(expected_array_values.len(), union.len());
1431        for (i, expected_value) in expected_array_values.iter().enumerate() {
1432            assert!(!union.is_null(i));
1433            let slot = union.value(i);
1434            let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1435            assert_eq!(slot.len(), 1);
1436            let value = slot.value(0);
1437            assert_eq!(expected_value, &value);
1438        }
1439    }
1440
1441    #[test]
1442    fn test_sparse_mixed() {
1443        let mut builder = UnionBuilder::new_sparse();
1444        builder.append::<Int32Type>("a", 1).unwrap();
1445        builder.append::<Float64Type>("c", 3.0).unwrap();
1446        builder.append::<Int32Type>("a", 4).unwrap();
1447        builder.append::<Float64Type>("c", 5.0).unwrap();
1448        builder.append::<Int32Type>("a", 6).unwrap();
1449        let union = builder.build().unwrap();
1450
1451        let expected_type_ids = vec![0_i8, 1, 0, 1, 0];
1452
1453        // Check type ids
1454        assert_eq!(*union.type_ids(), expected_type_ids);
1455        for (i, id) in expected_type_ids.iter().enumerate() {
1456            assert_eq!(id, &union.type_id(i));
1457        }
1458
1459        // Check offsets, sparse union should only have a single buffer, i.e. no offsets
1460        assert!(union.offsets().is_none());
1461
1462        for i in 0..union.len() {
1463            let slot = union.value(i);
1464            assert!(!union.is_null(i));
1465            match i {
1466                0 => {
1467                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1468                    assert_eq!(slot.len(), 1);
1469                    let value = slot.value(0);
1470                    assert_eq!(1_i32, value);
1471                }
1472                1 => {
1473                    let slot = slot.as_any().downcast_ref::<Float64Array>().unwrap();
1474                    assert_eq!(slot.len(), 1);
1475                    let value = slot.value(0);
1476                    assert_eq!(value, 3_f64);
1477                }
1478                2 => {
1479                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1480                    assert_eq!(slot.len(), 1);
1481                    let value = slot.value(0);
1482                    assert_eq!(4_i32, value);
1483                }
1484                3 => {
1485                    let slot = slot.as_any().downcast_ref::<Float64Array>().unwrap();
1486                    assert_eq!(slot.len(), 1);
1487                    let value = slot.value(0);
1488                    assert_eq!(5_f64, value);
1489                }
1490                4 => {
1491                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1492                    assert_eq!(slot.len(), 1);
1493                    let value = slot.value(0);
1494                    assert_eq!(6_i32, value);
1495                }
1496                _ => unreachable!(),
1497            }
1498        }
1499    }
1500
1501    #[test]
1502    fn test_sparse_mixed_with_nulls() {
1503        let mut builder = UnionBuilder::new_sparse();
1504        builder.append::<Int32Type>("a", 1).unwrap();
1505        builder.append_null::<Int32Type>("a").unwrap();
1506        builder.append::<Float64Type>("c", 3.0).unwrap();
1507        builder.append::<Int32Type>("a", 4).unwrap();
1508        let union = builder.build().unwrap();
1509
1510        let expected_type_ids = vec![0_i8, 0, 1, 0];
1511
1512        // Check type ids
1513        assert_eq!(*union.type_ids(), expected_type_ids);
1514        for (i, id) in expected_type_ids.iter().enumerate() {
1515            assert_eq!(id, &union.type_id(i));
1516        }
1517
1518        // Check offsets, sparse union should only have a single buffer, i.e. no offsets
1519        assert!(union.offsets().is_none());
1520
1521        for i in 0..union.len() {
1522            let slot = union.value(i);
1523            match i {
1524                0 => {
1525                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1526                    assert!(!slot.is_null(0));
1527                    assert_eq!(slot.len(), 1);
1528                    let value = slot.value(0);
1529                    assert_eq!(1_i32, value);
1530                }
1531                1 => assert!(slot.is_null(0)),
1532                2 => {
1533                    let slot = slot.as_any().downcast_ref::<Float64Array>().unwrap();
1534                    assert!(!slot.is_null(0));
1535                    assert_eq!(slot.len(), 1);
1536                    let value = slot.value(0);
1537                    assert_eq!(value, 3_f64);
1538                }
1539                3 => {
1540                    let slot = slot.as_any().downcast_ref::<Int32Array>().unwrap();
1541                    assert!(!slot.is_null(0));
1542                    assert_eq!(slot.len(), 1);
1543                    let value = slot.value(0);
1544                    assert_eq!(4_i32, value);
1545                }
1546                _ => unreachable!(),
1547            }
1548        }
1549    }
1550
1551    #[test]
1552    fn test_sparse_mixed_with_nulls_and_offset() {
1553        let mut builder = UnionBuilder::new_sparse();
1554        builder.append::<Int32Type>("a", 1).unwrap();
1555        builder.append_null::<Int32Type>("a").unwrap();
1556        builder.append::<Float64Type>("c", 3.0).unwrap();
1557        builder.append_null::<Float64Type>("c").unwrap();
1558        builder.append::<Int32Type>("a", 4).unwrap();
1559        let union = builder.build().unwrap();
1560
1561        let slice = union.slice(1, 4);
1562        let new_union = slice.as_any().downcast_ref::<UnionArray>().unwrap();
1563
1564        assert_eq!(4, new_union.len());
1565        for i in 0..new_union.len() {
1566            let slot = new_union.value(i);
1567            match i {
1568                0 => assert!(slot.is_null(0)),
1569                1 => {
1570                    let slot = slot.as_primitive::<Float64Type>();
1571                    assert!(!slot.is_null(0));
1572                    assert_eq!(slot.len(), 1);
1573                    let value = slot.value(0);
1574                    assert_eq!(value, 3_f64);
1575                }
1576                2 => assert!(slot.is_null(0)),
1577                3 => {
1578                    let slot = slot.as_primitive::<Int32Type>();
1579                    assert!(!slot.is_null(0));
1580                    assert_eq!(slot.len(), 1);
1581                    let value = slot.value(0);
1582                    assert_eq!(4_i32, value);
1583                }
1584                _ => unreachable!(),
1585            }
1586        }
1587    }
1588
1589    fn test_union_validity(union_array: &UnionArray) {
1590        assert_eq!(union_array.null_count(), 0);
1591
1592        for i in 0..union_array.len() {
1593            assert!(!union_array.is_null(i));
1594            assert!(union_array.is_valid(i));
1595        }
1596    }
1597
1598    #[test]
1599    fn test_union_array_validity() {
1600        let mut builder = UnionBuilder::new_sparse();
1601        builder.append::<Int32Type>("a", 1).unwrap();
1602        builder.append_null::<Int32Type>("a").unwrap();
1603        builder.append::<Float64Type>("c", 3.0).unwrap();
1604        builder.append_null::<Float64Type>("c").unwrap();
1605        builder.append::<Int32Type>("a", 4).unwrap();
1606        let union = builder.build().unwrap();
1607
1608        test_union_validity(&union);
1609
1610        let mut builder = UnionBuilder::new_dense();
1611        builder.append::<Int32Type>("a", 1).unwrap();
1612        builder.append_null::<Int32Type>("a").unwrap();
1613        builder.append::<Float64Type>("c", 3.0).unwrap();
1614        builder.append_null::<Float64Type>("c").unwrap();
1615        builder.append::<Int32Type>("a", 4).unwrap();
1616        let union = builder.build().unwrap();
1617
1618        test_union_validity(&union);
1619    }
1620
1621    #[test]
1622    fn test_type_check() {
1623        let mut builder = UnionBuilder::new_sparse();
1624        builder.append::<Float32Type>("a", 1.0).unwrap();
1625        let err = builder.append::<Int32Type>("a", 1).unwrap_err().to_string();
1626        assert!(
1627            err.contains(
1628                "Attempt to write col \"a\" with type Int32 doesn't match existing type Float32"
1629            ),
1630            "{}",
1631            err
1632        );
1633    }
1634
1635    #[test]
1636    fn slice_union_array() {
1637        // [1, null, 3.0, null, 4]
1638        fn create_union(mut builder: UnionBuilder) -> UnionArray {
1639            builder.append::<Int32Type>("a", 1).unwrap();
1640            builder.append_null::<Int32Type>("a").unwrap();
1641            builder.append::<Float64Type>("c", 3.0).unwrap();
1642            builder.append_null::<Float64Type>("c").unwrap();
1643            builder.append::<Int32Type>("a", 4).unwrap();
1644            builder.build().unwrap()
1645        }
1646
1647        fn create_batch(union: UnionArray) -> RecordBatch {
1648            let schema = Schema::new(vec![Field::new(
1649                "struct_array",
1650                union.data_type().clone(),
1651                true,
1652            )]);
1653
1654            RecordBatch::try_new(Arc::new(schema), vec![Arc::new(union)]).unwrap()
1655        }
1656
1657        fn test_slice_union(record_batch_slice: RecordBatch) {
1658            let union_slice = record_batch_slice
1659                .column(0)
1660                .as_any()
1661                .downcast_ref::<UnionArray>()
1662                .unwrap();
1663
1664            assert_eq!(union_slice.type_id(0), 0);
1665            assert_eq!(union_slice.type_id(1), 1);
1666            assert_eq!(union_slice.type_id(2), 1);
1667
1668            let slot = union_slice.value(0);
1669            let array = slot.as_primitive::<Int32Type>();
1670            assert_eq!(array.len(), 1);
1671            assert!(array.is_null(0));
1672
1673            let slot = union_slice.value(1);
1674            let array = slot.as_primitive::<Float64Type>();
1675            assert_eq!(array.len(), 1);
1676            assert!(array.is_valid(0));
1677            assert_eq!(array.value(0), 3.0);
1678
1679            let slot = union_slice.value(2);
1680            let array = slot.as_primitive::<Float64Type>();
1681            assert_eq!(array.len(), 1);
1682            assert!(array.is_null(0));
1683        }
1684
1685        // Sparse Union
1686        let builder = UnionBuilder::new_sparse();
1687        let record_batch = create_batch(create_union(builder));
1688        // [null, 3.0, null]
1689        let record_batch_slice = record_batch.slice(1, 3);
1690        test_slice_union(record_batch_slice);
1691
1692        // Dense Union
1693        let builder = UnionBuilder::new_dense();
1694        let record_batch = create_batch(create_union(builder));
1695        // [null, 3.0, null]
1696        let record_batch_slice = record_batch.slice(1, 3);
1697        test_slice_union(record_batch_slice);
1698    }
1699
1700    #[test]
1701    fn test_union_from_sliced_array_data() {
1702        for mode in [UnionMode::Sparse, UnionMode::Dense] {
1703            let fields = UnionFields::try_new(
1704                [3, 7],
1705                [
1706                    Field::new("ints", DataType::Int32, false),
1707                    Field::new("floats", DataType::Float64, false),
1708                ],
1709            )
1710            .unwrap();
1711            let offsets = (mode == UnionMode::Dense).then(|| vec![0, 1, 2, 3].into());
1712            let array = UnionArray::try_new(
1713                fields,
1714                vec![3, 7, 3, 7].into(),
1715                offsets,
1716                vec![
1717                    Arc::new(Int32Array::from(vec![10, 20, 30, 40])),
1718                    Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0])),
1719                ],
1720            )
1721            .unwrap();
1722            for (offset, len) in [(1, 2), (0, 2), (4, 0), (0, 4)] {
1723                let expected = array.slice(offset, len);
1724                let actual = make_array(array.to_data().slice(offset, len));
1725                let actual = actual.as_any().downcast_ref::<UnionArray>().unwrap();
1726                for i in 0..len {
1727                    assert_eq!(actual.value(i).to_data(), expected.value(i).to_data());
1728                }
1729                assert_eq!(actual.to_data(), expected.to_data());
1730            }
1731        }
1732    }
1733
1734    #[test]
1735    fn test_sparse_union_from_sliced_array_data_logical_nulls() {
1736        let child = Int32Array::from(vec![Some(10), None, Some(30)]);
1737        let fields =
1738            UnionFields::try_new([0], [Field::new("ints", DataType::Int32, true)]).unwrap();
1739        let array =
1740            UnionArray::try_new(fields, vec![0, 0, 0].into(), None, vec![Arc::new(child)]).unwrap();
1741        for (offset, len) in [(0, 1), (1, 1), (2, 1), (3, 0)] {
1742            let expected = array.slice(offset, len);
1743            let actual = make_array(array.to_data().slice(offset, len));
1744            assert_eq!(actual.logical_nulls(), expected.logical_nulls());
1745            assert_eq!(actual.to_data(), expected.to_data());
1746        }
1747    }
1748
1749    #[test]
1750    fn test_custom_type_ids() {
1751        let data_type = DataType::Union(
1752            UnionFields::try_new(
1753                vec![8, 4, 9],
1754                vec![
1755                    Field::new("strings", DataType::Utf8, false),
1756                    Field::new("integers", DataType::Int32, false),
1757                    Field::new("floats", DataType::Float64, false),
1758                ],
1759            )
1760            .unwrap(),
1761            UnionMode::Dense,
1762        );
1763
1764        let string_array = StringArray::from(vec!["foo", "bar", "baz"]);
1765        let int_array = Int32Array::from(vec![5, 6, 4]);
1766        let float_array = Float64Array::from(vec![10.0]);
1767
1768        let type_ids = Buffer::from_vec(vec![4_i8, 8, 4, 8, 9, 4, 8]);
1769        let value_offsets = Buffer::from_vec(vec![0_i32, 0, 1, 1, 0, 2, 2]);
1770
1771        let data = ArrayData::builder(data_type)
1772            .len(7)
1773            .buffers(vec![type_ids, value_offsets])
1774            .child_data(vec![
1775                string_array.into_data(),
1776                int_array.into_data(),
1777                float_array.into_data(),
1778            ])
1779            .build()
1780            .unwrap();
1781
1782        let array = UnionArray::from(data);
1783
1784        let v = array.value(0);
1785        assert_eq!(v.data_type(), &DataType::Int32);
1786        assert_eq!(v.len(), 1);
1787        assert_eq!(v.as_primitive::<Int32Type>().value(0), 5);
1788
1789        let v = array.value(1);
1790        assert_eq!(v.data_type(), &DataType::Utf8);
1791        assert_eq!(v.len(), 1);
1792        assert_eq!(v.as_string::<i32>().value(0), "foo");
1793
1794        let v = array.value(2);
1795        assert_eq!(v.data_type(), &DataType::Int32);
1796        assert_eq!(v.len(), 1);
1797        assert_eq!(v.as_primitive::<Int32Type>().value(0), 6);
1798
1799        let v = array.value(3);
1800        assert_eq!(v.data_type(), &DataType::Utf8);
1801        assert_eq!(v.len(), 1);
1802        assert_eq!(v.as_string::<i32>().value(0), "bar");
1803
1804        let v = array.value(4);
1805        assert_eq!(v.data_type(), &DataType::Float64);
1806        assert_eq!(v.len(), 1);
1807        assert_eq!(v.as_primitive::<Float64Type>().value(0), 10.0);
1808
1809        let v = array.value(5);
1810        assert_eq!(v.data_type(), &DataType::Int32);
1811        assert_eq!(v.len(), 1);
1812        assert_eq!(v.as_primitive::<Int32Type>().value(0), 4);
1813
1814        let v = array.value(6);
1815        assert_eq!(v.data_type(), &DataType::Utf8);
1816        assert_eq!(v.len(), 1);
1817        assert_eq!(v.as_string::<i32>().value(0), "baz");
1818    }
1819
1820    #[test]
1821    fn into_parts() {
1822        let mut builder = UnionBuilder::new_dense();
1823        builder.append::<Int32Type>("a", 1).unwrap();
1824        builder.append::<Int8Type>("b", 2).unwrap();
1825        builder.append::<Int32Type>("a", 3).unwrap();
1826        let dense_union = builder.build().unwrap();
1827
1828        let field = [
1829            &Arc::new(Field::new("a", DataType::Int32, false)),
1830            &Arc::new(Field::new("b", DataType::Int8, false)),
1831        ];
1832        let (union_fields, type_ids, offsets, children) = dense_union.into_parts();
1833        assert_eq!(
1834            union_fields
1835                .iter()
1836                .map(|(_, field)| field)
1837                .collect::<Vec<_>>(),
1838            field
1839        );
1840        assert_eq!(type_ids, [0, 1, 0]);
1841        assert!(offsets.is_some());
1842        assert_eq!(offsets.as_ref().unwrap(), &[0, 0, 1]);
1843
1844        let result = UnionArray::try_new(union_fields, type_ids, offsets, children);
1845        assert!(result.is_ok());
1846        assert_eq!(result.unwrap().len(), 3);
1847
1848        let mut builder = UnionBuilder::new_sparse();
1849        builder.append::<Int32Type>("a", 1).unwrap();
1850        builder.append::<Int8Type>("b", 2).unwrap();
1851        builder.append::<Int32Type>("a", 3).unwrap();
1852        let sparse_union = builder.build().unwrap();
1853
1854        let (union_fields, type_ids, offsets, children) = sparse_union.into_parts();
1855        assert_eq!(type_ids, [0, 1, 0]);
1856        assert!(offsets.is_none());
1857
1858        let result = UnionArray::try_new(union_fields, type_ids, offsets, children);
1859        assert!(result.is_ok());
1860        assert_eq!(result.unwrap().len(), 3);
1861    }
1862
1863    #[test]
1864    fn into_parts_custom_type_ids() {
1865        let set_field_type_ids: [i8; 3] = [8, 4, 9];
1866        let data_type = DataType::Union(
1867            UnionFields::try_new(
1868                set_field_type_ids,
1869                [
1870                    Field::new("strings", DataType::Utf8, false),
1871                    Field::new("integers", DataType::Int32, false),
1872                    Field::new("floats", DataType::Float64, false),
1873                ],
1874            )
1875            .unwrap(),
1876            UnionMode::Dense,
1877        );
1878        let string_array = StringArray::from(vec!["foo", "bar", "baz"]);
1879        let int_array = Int32Array::from(vec![5, 6, 4]);
1880        let float_array = Float64Array::from(vec![10.0]);
1881        let type_ids = Buffer::from_vec(vec![4_i8, 8, 4, 8, 9, 4, 8]);
1882        let value_offsets = Buffer::from_vec(vec![0_i32, 0, 1, 1, 0, 2, 2]);
1883        let data = ArrayData::builder(data_type)
1884            .len(7)
1885            .buffers(vec![type_ids, value_offsets])
1886            .child_data(vec![
1887                string_array.into_data(),
1888                int_array.into_data(),
1889                float_array.into_data(),
1890            ])
1891            .build()
1892            .unwrap();
1893        let array = UnionArray::from(data);
1894
1895        let (union_fields, type_ids, offsets, children) = array.into_parts();
1896        assert_eq!(
1897            type_ids.iter().collect::<HashSet<_>>(),
1898            set_field_type_ids.iter().collect::<HashSet<_>>()
1899        );
1900        let result = UnionArray::try_new(union_fields, type_ids, offsets, children);
1901        assert!(result.is_ok());
1902        let array = result.unwrap();
1903        assert_eq!(array.len(), 7);
1904    }
1905
1906    #[test]
1907    fn test_dense_union_large_child() {
1908        let fields =
1909            UnionFields::try_new([3], [Field::new("nulls", DataType::Null, true)]).unwrap();
1910
1911        // NullArray represents these lengths without allocating a values buffer.
1912        for child_len in [i32::MAX as usize + 1, i32::MAX as usize + 2] {
1913            let array = UnionArray::try_new(
1914                fields.clone(),
1915                vec![3, 3].into(),
1916                Some(vec![0, i32::MAX].into()),
1917                vec![Arc::new(NullArray::new(child_len))],
1918            )
1919            .unwrap();
1920
1921            assert_eq!(array.child(3).len(), child_len);
1922            assert_eq!(array.value(1).len(), 1);
1923            array.to_data().validate_full().unwrap();
1924        }
1925    }
1926
1927    #[test]
1928    fn test_invalid() {
1929        let fields = UnionFields::try_new(
1930            [3, 2],
1931            [
1932                Field::new("a", DataType::Utf8, false),
1933                Field::new("b", DataType::Utf8, false),
1934            ],
1935        )
1936        .unwrap();
1937        let children = vec![
1938            Arc::new(StringArray::from_iter_values(["a", "b"])) as _,
1939            Arc::new(StringArray::from_iter_values(["c", "d"])) as _,
1940        ];
1941
1942        let type_ids = vec![3, 3, 2].into();
1943        let err =
1944            UnionArray::try_new(fields.clone(), type_ids, None, children.clone()).unwrap_err();
1945        assert_eq!(
1946            err.to_string(),
1947            "Invalid argument error: Sparse union child arrays must be equal in length to the length of the union"
1948        );
1949
1950        let type_ids = vec![1, 2].into();
1951        let err =
1952            UnionArray::try_new(fields.clone(), type_ids, None, children.clone()).unwrap_err();
1953        assert_eq!(
1954            err.to_string(),
1955            "Invalid argument error: Type Ids values must match one of the field type ids"
1956        );
1957
1958        let type_ids = vec![7, 2].into();
1959        let err = UnionArray::try_new(fields.clone(), type_ids, None, children).unwrap_err();
1960        assert_eq!(
1961            err.to_string(),
1962            "Invalid argument error: Type Ids values must match one of the field type ids"
1963        );
1964
1965        let children = vec![
1966            Arc::new(StringArray::from_iter_values(["a", "b"])) as _,
1967            Arc::new(StringArray::from_iter_values(["c"])) as _,
1968        ];
1969        let type_ids = ScalarBuffer::from(vec![3_i8, 3, 2]);
1970        let offsets = Some(vec![0, 1, 0].into());
1971        UnionArray::try_new(fields.clone(), type_ids.clone(), offsets, children.clone()).unwrap();
1972
1973        let offsets = Some(vec![0, 1, 1].into());
1974        let err = UnionArray::try_new(fields.clone(), type_ids.clone(), offsets, children.clone())
1975            .unwrap_err();
1976
1977        assert_eq!(
1978            err.to_string(),
1979            "Invalid argument error: Offsets must be non-negative and within the length of the Array"
1980        );
1981
1982        let offsets = Some(vec![0, -1, 0].into());
1983        let err = UnionArray::try_new(fields.clone(), type_ids.clone(), offsets, children.clone())
1984            .unwrap_err();
1985
1986        assert_eq!(
1987            err.to_string(),
1988            "Invalid argument error: Offsets must be non-negative and within the length of the Array"
1989        );
1990
1991        let offsets = Some(vec![0, 1].into());
1992        let err =
1993            UnionArray::try_new(fields.clone(), type_ids.clone(), offsets, children).unwrap_err();
1994
1995        assert_eq!(
1996            err.to_string(),
1997            "Invalid argument error: Type Ids and Offsets lengths must match"
1998        );
1999
2000        let err = UnionArray::try_new(fields.clone(), type_ids, None, vec![]).unwrap_err();
2001
2002        assert_eq!(
2003            err.to_string(),
2004            "Invalid argument error: Union fields length must match child arrays length"
2005        );
2006    }
2007
2008    #[test]
2009    fn test_logical_nulls_fast_paths() {
2010        // fields.len() <= 1
2011        let array = UnionArray::try_new(UnionFields::empty(), vec![].into(), None, vec![]).unwrap();
2012
2013        assert_eq!(array.logical_nulls(), None);
2014
2015        let fields = UnionFields::try_new(
2016            [1, 3],
2017            [
2018                Field::new("a", DataType::Int8, false), // non nullable
2019                Field::new("b", DataType::Int8, false), // non nullable
2020            ],
2021        )
2022        .unwrap();
2023        let array = UnionArray::try_new(
2024            fields,
2025            vec![1].into(),
2026            None,
2027            vec![
2028                Arc::new(Int8Array::from_value(5, 1)),
2029                Arc::new(Int8Array::from_value(5, 1)),
2030            ],
2031        )
2032        .unwrap();
2033
2034        assert_eq!(array.logical_nulls(), None);
2035
2036        let nullable_fields = UnionFields::try_new(
2037            [1, 3],
2038            [
2039                Field::new("a", DataType::Int8, true), // nullable but without nulls
2040                Field::new("b", DataType::Int8, true), // nullable but without nulls
2041            ],
2042        )
2043        .unwrap();
2044        let array = UnionArray::try_new(
2045            nullable_fields.clone(),
2046            vec![1, 1].into(),
2047            None,
2048            vec![
2049                Arc::new(Int8Array::from_value(-5, 2)), // nullable but without nulls
2050                Arc::new(Int8Array::from_value(-5, 2)), // nullable but without nulls
2051            ],
2052        )
2053        .unwrap();
2054
2055        assert_eq!(array.logical_nulls(), None);
2056
2057        let array = UnionArray::try_new(
2058            nullable_fields.clone(),
2059            vec![1, 1].into(),
2060            None,
2061            vec![
2062                // every child is completely null
2063                Arc::new(Int8Array::new_null(2)), // all null, same len as it's parent
2064                Arc::new(Int8Array::new_null(2)), // all null, same len as it's parent
2065            ],
2066        )
2067        .unwrap();
2068
2069        assert_eq!(array.logical_nulls(), Some(NullBuffer::new_null(2)));
2070
2071        let array = UnionArray::try_new(
2072            nullable_fields.clone(),
2073            vec![1, 1].into(),
2074            Some(vec![0, 1].into()),
2075            vec![
2076                // every child is completely null
2077                Arc::new(Int8Array::new_null(3)), // bigger that parent
2078                Arc::new(Int8Array::new_null(3)), // bigger that parent
2079            ],
2080        )
2081        .unwrap();
2082
2083        assert_eq!(array.logical_nulls(), Some(NullBuffer::new_null(2)));
2084    }
2085
2086    #[test]
2087    fn test_dense_union_logical_nulls_gather() {
2088        // union of [{A=1}, {A=2}, {B=3.2}, {B=}, {C=}, {C=}]
2089        let int_array = Int32Array::from(vec![1, 2]);
2090        let float_array = Float64Array::from(vec![Some(3.2), None]);
2091        let str_array = StringArray::new_null(1);
2092        let type_ids = [1, 1, 3, 3, 4, 4].into_iter().collect::<ScalarBuffer<i8>>();
2093        let offsets = [0, 1, 0, 1, 0, 0]
2094            .into_iter()
2095            .collect::<ScalarBuffer<i32>>();
2096
2097        let children = vec![
2098            Arc::new(int_array) as Arc<dyn Array>,
2099            Arc::new(float_array),
2100            Arc::new(str_array),
2101        ];
2102
2103        let array = UnionArray::try_new(union_fields(), type_ids, Some(offsets), children).unwrap();
2104
2105        let expected = BooleanBuffer::from(vec![true, true, true, false, false, false]);
2106
2107        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2108        assert_eq!(expected, array.gather_nulls(array.fields_logical_nulls()));
2109    }
2110
2111    #[test]
2112    fn test_dense_union_one_field_nonzero_type_id_logical_nulls() {
2113        let fields = UnionFields::try_new([5], [Field::new("a", DataType::Int32, true)]).unwrap();
2114        let child = Arc::new(Int32Array::from(vec![None, Some(1)]));
2115        let array = UnionArray::try_new(
2116            fields,
2117            vec![5, 5, 5].into(),
2118            Some(vec![1, 0, 1].into()),
2119            vec![child],
2120        )
2121        .unwrap();
2122
2123        let expected = NullBuffer::from(vec![true, false, true]);
2124        assert_eq!(array.logical_nulls(), Some(expected));
2125        assert_eq!(array.logical_null_count(), 1);
2126    }
2127
2128    #[test]
2129    fn test_sparse_union_logical_nulls_mask_all_nulls_skip_one() {
2130        let fields: UnionFields = [
2131            (1, Arc::new(Field::new("A", DataType::Int32, true))),
2132            (3, Arc::new(Field::new("B", DataType::Float64, true))),
2133        ]
2134        .into_iter()
2135        .collect();
2136
2137        // union of [{A=}, {A=}, {B=3.2}, {B=}]
2138        let int_array = Int32Array::new_null(4);
2139        let float_array = Float64Array::from(vec![None, None, Some(3.2), None]);
2140        let type_ids = [1, 1, 3, 3].into_iter().collect::<ScalarBuffer<i8>>();
2141
2142        let children = vec![Arc::new(int_array) as Arc<dyn Array>, Arc::new(float_array)];
2143
2144        let array = UnionArray::try_new(fields.clone(), type_ids, None, children).unwrap();
2145
2146        let expected = BooleanBuffer::from(vec![false, false, true, false]);
2147
2148        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2149        assert_eq!(
2150            expected,
2151            array.mask_sparse_all_with_nulls_skip_one(array.fields_logical_nulls())
2152        );
2153
2154        //like above, but repeated to generate two exact bitmasks and a non empty remainder
2155        let len = 2 * 64 + 32;
2156
2157        let int_array = Int32Array::new_null(len);
2158        let float_array = Float64Array::from_iter([Some(3.2), None].into_iter().cycle().take(len));
2159        let type_ids = ScalarBuffer::from_iter([1, 1, 3, 3].into_iter().cycle().take(len));
2160
2161        let array = UnionArray::try_new(
2162            fields,
2163            type_ids,
2164            None,
2165            vec![Arc::new(int_array), Arc::new(float_array)],
2166        )
2167        .unwrap();
2168
2169        let expected =
2170            BooleanBuffer::from_iter([false, false, true, false].into_iter().cycle().take(len));
2171
2172        assert_eq!(array.len(), len);
2173        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2174        assert_eq!(
2175            expected,
2176            array.mask_sparse_all_with_nulls_skip_one(array.fields_logical_nulls())
2177        );
2178    }
2179
2180    #[test]
2181    fn test_sparse_union_logical_mask_mixed_nulls_skip_fully_valid() {
2182        // union of [{A=2}, {A=2}, {B=3.2}, {B=}, {C=}, {C=}]
2183        let int_array = Int32Array::from_value(2, 6);
2184        let float_array = Float64Array::from_value(4.2, 6);
2185        let str_array = StringArray::new_null(6);
2186        let type_ids = [1, 1, 3, 3, 4, 4].into_iter().collect::<ScalarBuffer<i8>>();
2187
2188        let children = vec![
2189            Arc::new(int_array) as Arc<dyn Array>,
2190            Arc::new(float_array),
2191            Arc::new(str_array),
2192        ];
2193
2194        let array = UnionArray::try_new(union_fields(), type_ids, None, children).unwrap();
2195
2196        let expected = BooleanBuffer::from(vec![true, true, true, true, false, false]);
2197
2198        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2199        assert_eq!(
2200            expected,
2201            array.mask_sparse_skip_without_nulls(array.fields_logical_nulls())
2202        );
2203
2204        //like above, but repeated to generate two exact bitmasks and a non empty remainder
2205        let len = 2 * 64 + 32;
2206
2207        let int_array = Int32Array::from_value(2, len);
2208        let float_array = Float64Array::from_value(4.2, len);
2209        let str_array = StringArray::from_iter([None, Some("a")].into_iter().cycle().take(len));
2210        let type_ids = ScalarBuffer::from_iter([1, 1, 3, 3, 4, 4].into_iter().cycle().take(len));
2211
2212        let children = vec![
2213            Arc::new(int_array) as Arc<dyn Array>,
2214            Arc::new(float_array),
2215            Arc::new(str_array),
2216        ];
2217
2218        let array = UnionArray::try_new(union_fields(), type_ids, None, children).unwrap();
2219
2220        let expected = BooleanBuffer::from_iter(
2221            [true, true, true, true, false, true]
2222                .into_iter()
2223                .cycle()
2224                .take(len),
2225        );
2226
2227        assert_eq!(array.len(), len);
2228        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2229        assert_eq!(
2230            expected,
2231            array.mask_sparse_skip_without_nulls(array.fields_logical_nulls())
2232        );
2233    }
2234
2235    #[test]
2236    fn test_sparse_union_logical_mask_mixed_nulls_skip_fully_null() {
2237        // union of [{A=}, {A=}, {B=4.2}, {B=4.2}, {C=}, {C=}]
2238        let int_array = Int32Array::new_null(6);
2239        let float_array = Float64Array::from_value(4.2, 6);
2240        let str_array = StringArray::new_null(6);
2241        let type_ids = [1, 1, 3, 3, 4, 4].into_iter().collect::<ScalarBuffer<i8>>();
2242
2243        let children = vec![
2244            Arc::new(int_array) as Arc<dyn Array>,
2245            Arc::new(float_array),
2246            Arc::new(str_array),
2247        ];
2248
2249        let array = UnionArray::try_new(union_fields(), type_ids, None, children).unwrap();
2250
2251        let expected = BooleanBuffer::from(vec![false, false, true, true, false, false]);
2252
2253        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2254        assert_eq!(
2255            expected,
2256            array.mask_sparse_skip_fully_null(array.fields_logical_nulls())
2257        );
2258
2259        //like above, but repeated to generate two exact bitmasks and a non empty remainder
2260        let len = 2 * 64 + 32;
2261
2262        let int_array = Int32Array::new_null(len);
2263        let float_array = Float64Array::from_value(4.2, len);
2264        let str_array = StringArray::new_null(len);
2265        let type_ids = ScalarBuffer::from_iter([1, 1, 3, 3, 4, 4].into_iter().cycle().take(len));
2266
2267        let children = vec![
2268            Arc::new(int_array) as Arc<dyn Array>,
2269            Arc::new(float_array),
2270            Arc::new(str_array),
2271        ];
2272
2273        let array = UnionArray::try_new(union_fields(), type_ids, None, children).unwrap();
2274
2275        let expected = BooleanBuffer::from_iter(
2276            [false, false, true, true, false, false]
2277                .into_iter()
2278                .cycle()
2279                .take(len),
2280        );
2281
2282        assert_eq!(array.len(), len);
2283        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2284        assert_eq!(
2285            expected,
2286            array.mask_sparse_skip_fully_null(array.fields_logical_nulls())
2287        );
2288    }
2289
2290    #[test]
2291    fn test_sparse_union_logical_nulls_gather() {
2292        let n_fields = 50;
2293
2294        let non_null = Int32Array::from_value(2, 4);
2295        let mixed = Int32Array::from(vec![None, None, Some(1), None]);
2296        let fully_null = Int32Array::new_null(4);
2297
2298        let array = UnionArray::try_new(
2299            (1..)
2300                .step_by(2)
2301                .map(|i| {
2302                    (
2303                        i,
2304                        Arc::new(Field::new(format!("f{i}"), DataType::Int32, true)),
2305                    )
2306                })
2307                .take(n_fields)
2308                .collect(),
2309            vec![1, 3, 3, 5].into(),
2310            None,
2311            [
2312                Arc::new(non_null) as ArrayRef,
2313                Arc::new(mixed),
2314                Arc::new(fully_null),
2315            ]
2316            .into_iter()
2317            .cycle()
2318            .take(n_fields)
2319            .collect(),
2320        )
2321        .unwrap();
2322
2323        let expected = BooleanBuffer::from(vec![true, false, true, false]);
2324
2325        assert_eq!(expected, array.logical_nulls().unwrap().into_inner());
2326        assert_eq!(expected, array.gather_nulls(array.fields_logical_nulls()));
2327    }
2328
2329    fn union_fields() -> UnionFields {
2330        [
2331            (1, Arc::new(Field::new("A", DataType::Int32, true))),
2332            (3, Arc::new(Field::new("B", DataType::Float64, true))),
2333            (4, Arc::new(Field::new("C", DataType::Utf8, true))),
2334        ]
2335        .into_iter()
2336        .collect()
2337    }
2338
2339    #[test]
2340    fn test_is_nullable() {
2341        assert!(!create_union_array(false, false).is_nullable());
2342        assert!(create_union_array(true, false).is_nullable());
2343        assert!(create_union_array(false, true).is_nullable());
2344        assert!(create_union_array(true, true).is_nullable());
2345    }
2346
2347    /// Create a union array with a float and integer field
2348    ///
2349    /// If the `int_nullable` is true, the integer field will have nulls
2350    /// If the `float_nullable` is true, the float field will have nulls
2351    ///
2352    /// Note the `Field` definitions are always declared to be nullable
2353    fn create_union_array(int_nullable: bool, float_nullable: bool) -> UnionArray {
2354        let int_array = if int_nullable {
2355            Int32Array::from(vec![Some(1), None, Some(3)])
2356        } else {
2357            Int32Array::from(vec![1, 2, 3])
2358        };
2359        let float_array = if float_nullable {
2360            Float64Array::from(vec![Some(3.2), None, Some(4.2)])
2361        } else {
2362            Float64Array::from(vec![3.2, 4.2, 5.2])
2363        };
2364        let type_ids = [0, 1, 0].into_iter().collect::<ScalarBuffer<i8>>();
2365        let offsets = [0, 0, 0].into_iter().collect::<ScalarBuffer<i32>>();
2366        let union_fields = [
2367            (0, Arc::new(Field::new("A", DataType::Int32, true))),
2368            (1, Arc::new(Field::new("B", DataType::Float64, true))),
2369        ]
2370        .into_iter()
2371        .collect::<UnionFields>();
2372
2373        let children = vec![Arc::new(int_array) as Arc<dyn Array>, Arc::new(float_array)];
2374
2375        UnionArray::try_new(union_fields, type_ids, Some(offsets), children).unwrap()
2376    }
2377}