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