Skip to content

Commit 30091f2

Browse files
authored
Add UnionArray (#8834)
## Rationale for this change Tracking issue: #8769 This is the array layer of the Union stack. The compute integration has been split into the stacked follow-up #8884. ## What changes are included in this PR? - Adds the canonical sparse `UnionArray` encoding with nullable u8 type IDs and row-aligned children. - Adds construction and invariant validation, typed accessors, scalar access, top-level validity, serialization, and session registration. - Registers UnionArray as the canonical representation for Union dtypes. - Leaves filter, take, slice, mask, cast, aggregate, constant/chunked canonicalization, and compression support to #8884. I think that this is a mostly straightforward implementation. The one implementation thing I will shout out is that we do not validate that the entire type IDs array has valid type IDs. If we come across an invalid type ID, we just panic, and that is similar behavior to `DictArray` validation. ## What APIs are changed? Are there any user-facing changes? Adds the public Union, UnionArray, UnionArrayExt, and UnionDataParts APIs. --------- Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent 57962f4 commit 30091f2

24 files changed

Lines changed: 861 additions & 8 deletions

File tree

fuzz/src/array/fill_null.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ pub fn fill_null_canonical_array(
4949
| Canonical::List(_)
5050
| Canonical::FixedSizeList(_)
5151
| Canonical::Extension(_) => canonical.into_array().fill_null(fill_value.clone())?,
52+
Canonical::Union(_) => {
53+
todo!("TODO(connor)[Union]: support Union arrays in the fill_null fuzzer")
54+
}
5255
Canonical::Variant(_) => unreachable!("Variant arrays are not fuzzed"),
5356
})
5457
}

fuzz/src/array/mask.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ pub fn mask_canonical_array(
149149
.with_nullability(masked_storage.dtype().nullability());
150150
ExtensionArray::new(ext_dtype, masked_storage).into_array()
151151
}
152+
Canonical::Union(_) => {
153+
todo!("TODO(connor)[Union]: support Union arrays in the mask fuzzer")
154+
}
152155
Canonical::Variant(_) => unreachable!("Variant arrays are not fuzzed"),
153156
})
154157
}

fuzz/src/array/scalar_at.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ pub fn scalar_at_canonical_array(
104104
let storage_scalar = scalar_at_canonical_array(storage_canonical, index, ctx)?;
105105
Scalar::extension_ref(array.ext_dtype().clone(), storage_scalar)
106106
}
107+
Canonical::Union(_) => {
108+
todo!("TODO(connor)[Union]: support Union arrays in the scalar_at fuzzer")
109+
}
107110
Canonical::Variant(_) => unreachable!("Variant arrays are not fuzzed"),
108111
})
109112
}

vortex-array/src/aggregate_fn/fns/is_constant/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,9 @@ impl AggregateFnVTable for IsConstant {
404404
Canonical::List(l) => check_listview_constant(l, ctx)?,
405405
Canonical::FixedSizeList(f) => check_fixed_size_list_constant(f, ctx)?,
406406
Canonical::Null(_) => true,
407+
Canonical::Union(_) => {
408+
todo!("TODO(connor)[Union]: implement IsConstant for Union arrays")
409+
}
407410
Canonical::Variant(_) => {
408411
vortex_bail!("Variant arrays don't support IsConstant")
409412
}

vortex-array/src/aggregate_fn/fns/min_max/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ impl AggregateFnVTable for MinMax {
417417
Canonical::Decimal(d) => accumulate_decimal(partial, d, ctx),
418418
Canonical::Extension(e) => accumulate_extension(partial, e, ctx),
419419
Canonical::Null(_) => Ok(()),
420+
Canonical::Union(_) => {
421+
todo!("TODO(connor)[Union]: implement min_max for Union arrays")
422+
}
420423
Canonical::Struct(_)
421424
| Canonical::List(_)
422425
| Canonical::FixedSizeList(_)

vortex-array/src/aggregate_fn/fns/uncompressed_size_in_bytes/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ pub(crate) fn canonical_uncompressed_size_in_bytes(
199199
Canonical::List(array) => list_view_uncompressed_size_in_bytes(array, ctx),
200200
Canonical::FixedSizeList(array) => fixed_size_list_uncompressed_size_in_bytes(array, ctx),
201201
Canonical::Struct(array) => struct_uncompressed_size_in_bytes(array, ctx),
202+
Canonical::Union(_) => {
203+
todo!("TODO(connor)[Union]: implement UncompressedSizeInBytes for Union arrays")
204+
}
202205
Canonical::Extension(array) => extension_uncompressed_size_in_bytes(array, ctx),
203206
Canonical::Variant(_) => {
204207
vortex_bail!("UncompressedSizeInBytes is not supported for Variant arrays")

vortex-array/src/arrays/dict/execute.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pub(crate) fn take_canonical(
5353
Canonical::FixedSizeList(take_fixed_size_list(&a, codes, ctx))
5454
}
5555
Canonical::Struct(a) => Canonical::Struct(take_struct(&a, codes)),
56+
Canonical::Union(_) => {
57+
todo!("TODO(connor)[Union]: implement dictionary execution for Union arrays")
58+
}
5659
Canonical::Extension(a) => Canonical::Extension(take_extension(&a, codes, ctx)),
5760
Canonical::Variant(a) => {
5861
let indices = codes.clone().into_array();

vortex-array/src/arrays/filter/execute/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub(super) fn execute_filter(canonical: Canonical, mask: &Arc<MaskValues>) -> Ca
9595
Canonical::FixedSizeList(fixed_size_list::filter_fixed_size_list(&a, mask))
9696
}
9797
Canonical::Struct(a) => Canonical::Struct(struct_::filter_struct(&a, mask)),
98+
Canonical::Union(_) => {
99+
todo!("TODO(connor)[Union]: implement filter for Union arrays")
100+
}
98101
Canonical::Extension(a) => {
99102
let filtered_storage = a
100103
.storage_array()

vortex-array/src/arrays/masked/execute.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ pub fn mask_validity_canonical(
5050
Canonical::FixedSizeList(mask_validity_fixed_size_list(a, validity)?)
5151
}
5252
Canonical::Struct(a) => Canonical::Struct(mask_validity_struct(a, validity)?),
53+
Canonical::Union(_) => {
54+
todo!("TODO(connor)[Union]: implement masking for Union arrays")
55+
}
5356
Canonical::Extension(a) => Canonical::Extension(mask_validity_extension(a, validity, ctx)?),
5457
Canonical::Variant(a) => Canonical::Variant(mask_validity_variant(a, validity, ctx)?),
5558
})

vortex-array/src/arrays/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
//!
66
//! Canonical arrays are the default uncompressed representation for a logical dtype:
77
//! [`NullArray`], [`BoolArray`], [`PrimitiveArray`], [`DecimalArray`], [`VarBinViewArray`],
8-
//! [`ListViewArray`], [`FixedSizeListArray`], [`StructArray`], [`ExtensionArray`], and
9-
//! [`VariantArray`].
8+
//! [`ListViewArray`], [`FixedSizeListArray`], [`StructArray`], [`UnionArray`],
9+
//! [`ExtensionArray`], and [`VariantArray`].
1010
//!
1111
//! Utility and lazy arrays represent common transformations without immediately materializing
1212
//! their result. Examples include [`ChunkedArray`] for concatenation, [`ConstantArray`] for repeated
@@ -112,6 +112,10 @@ pub mod struct_;
112112
pub use struct_::Struct;
113113
pub use struct_::StructArray;
114114

115+
pub mod union;
116+
pub use union::Union;
117+
pub use union::UnionArray;
118+
115119
pub mod varbin;
116120
pub use varbin::VarBin;
117121
pub use varbin::VarBinArray;

0 commit comments

Comments
 (0)