@@ -19,8 +19,8 @@ use crate::array::EmptyArrayData;
1919use crate :: array:: TypedArrayRef ;
2020use crate :: arrays:: PrimitiveArray ;
2121use crate :: arrays:: Union ;
22- use crate :: arrays:: union:: TYPE_IDS_DTYPE ;
2322use crate :: dtype:: DType ;
23+ use crate :: dtype:: Nullability ;
2424use crate :: dtype:: UnionVariants ;
2525use crate :: legacy_session;
2626
@@ -35,11 +35,18 @@ pub(super) fn make_union_parts(
3535 children : & [ ArrayRef ] ,
3636) -> ArrayParts < Union > {
3737 let len = type_ids. len ( ) ;
38+ let nullability = type_ids. dtype ( ) . nullability ( ) ;
3839 let slots: ArraySlots = once ( Some ( type_ids) )
3940 . chain ( children. iter ( ) . cloned ( ) . map ( Some ) )
4041 . collect ( ) ;
4142
42- ArrayParts :: new ( Union , DType :: Union ( variants) , len, EmptyArrayData ) . with_slots ( slots)
43+ ArrayParts :: new (
44+ Union ,
45+ DType :: Union ( variants, nullability) ,
46+ len,
47+ EmptyArrayData ,
48+ )
49+ . with_slots ( slots)
4350}
4451
4552/// Concrete parts of a [`UnionArray`](super::UnionArray).
@@ -57,12 +64,12 @@ pub trait UnionArrayExt: TypedArrayRef<Union> {
5764 /// The union's variant schema.
5865 fn variants ( & self ) -> & UnionVariants {
5966 match self . as_ref ( ) . dtype ( ) {
60- DType :: Union ( variants) => variants,
67+ DType :: Union ( variants, _ ) => variants,
6168 _ => unreachable ! ( "UnionArrayExt requires a union dtype" ) ,
6269 }
6370 }
6471
65- /// The row-aligned non-nullable `i8 ` type IDs.
72+ /// The row-aligned `u8 ` type IDs whose nulls represent outer union nulls .
6673 fn type_ids ( & self ) -> & ArrayRef {
6774 self . as_ref ( ) . slots ( ) [ TYPE_IDS_SLOT ]
6875 . as_ref ( )
@@ -87,7 +94,7 @@ pub trait UnionArrayExt: TypedArrayRef<Union> {
8794 }
8895
8996 /// Return a sparse child selected by a data-level type ID.
90- fn child_by_type_id ( & self , type_id : i8 ) -> Option < & ArrayRef > {
97+ fn child_by_type_id ( & self , type_id : u8 ) -> Option < & ArrayRef > {
9198 self . child ( self . variants ( ) . tag_to_child_index ( type_id) ?)
9299 }
93100
@@ -118,13 +125,22 @@ fn validate_type_id_values(array: &Array<Union>) -> VortexResult<()> {
118125 return Ok ( ( ) ) ;
119126 }
120127
128+ let mut ctx = legacy_session ( ) . create_execution_ctx ( ) ;
121129 let type_ids = array
122130 . type_ids ( )
123131 . clone ( )
124- . execute :: < PrimitiveArray > ( & mut legacy_session ( ) . create_execution_ctx ( ) ) ?;
125- for type_id in type_ids. as_slice :: < i8 > ( ) {
132+ . execute :: < PrimitiveArray > ( & mut ctx) ?;
133+ let values = type_ids. as_slice :: < u8 > ( ) ;
134+ let validity = type_ids. validity ( ) ?. execute_mask ( array. len ( ) , & mut ctx) ?;
135+ let valid_indices: Box < dyn Iterator < Item = usize > + ' _ > = match & validity {
136+ vortex_mask:: Mask :: AllTrue ( _) => Box :: new ( 0 ..values. len ( ) ) ,
137+ vortex_mask:: Mask :: AllFalse ( _) => Box :: new ( std:: iter:: empty ( ) ) ,
138+ vortex_mask:: Mask :: Values ( mask) => Box :: new ( mask. indices ( ) . iter ( ) . copied ( ) ) ,
139+ } ;
140+ for index in valid_indices {
141+ let type_id = values[ index] ;
126142 vortex_ensure ! (
127- array. variants( ) . tag_to_child_index( * type_id) . is_some( ) ,
143+ array. variants( ) . tag_to_child_index( type_id) . is_some( ) ,
128144 "UnionArray type ID {type_id} is not present in {:?}" ,
129145 array. variants( ) . type_ids( )
130146 ) ;
@@ -150,8 +166,6 @@ impl Array<Union> {
150166
151167 /// Try to construct a canonical sparse union array.
152168 ///
153- /// Until Union nullability semantics are settled, every child must be non-nullable.
154- ///
155169 /// # Errors
156170 ///
157171 /// Returns an error if the type IDs and sparse children do not match the variant schema or do
@@ -162,8 +176,11 @@ impl Array<Union> {
162176 children : impl Into < Arc < [ ArrayRef ] > > ,
163177 ) -> VortexResult < Self > {
164178 vortex_ensure ! (
165- type_ids. dtype( ) == & TYPE_IDS_DTYPE ,
166- "UnionArray type_ids must be non-nullable i8, got {}" ,
179+ matches!(
180+ type_ids. dtype( ) ,
181+ DType :: Primitive ( crate :: dtype:: PType :: U8 , _)
182+ ) ,
183+ "UnionArray type_ids must be u8, got {}" ,
167184 type_ids. dtype( )
168185 ) ;
169186
@@ -178,9 +195,9 @@ impl Array<Union> {
178195 ///
179196 /// # Safety
180197 ///
181- /// The caller must ensure the type IDs are non-nullable `i8` values declared by `variants`,
182- /// every child has the corresponding variant dtype, and all arrays have the same length. The
183- /// children must currently be non-nullable .
198+ /// The caller must ensure every non-null type ID is a `u8` value declared by `variants`, every
199+ /// child has the corresponding variant dtype, and all arrays have the same length. Null type
200+ /// IDs represent outer union nulls .
184201 pub unsafe fn new_unchecked (
185202 type_ids : ArrayRef ,
186203 variants : UnionVariants ,
@@ -202,9 +219,9 @@ impl Array<Union> {
202219 }
203220 }
204221
205- /// Create an empty array for a non-nullable union dtype.
206- pub ( crate ) fn empty ( variants : UnionVariants ) -> Self {
207- let type_ids = PrimitiveArray :: from_iter ( Vec :: < i8 > :: new ( ) ) . into_array ( ) ;
222+ /// Create an empty array for a union dtype.
223+ pub ( crate ) fn empty ( variants : UnionVariants , nullability : Nullability ) -> Self {
224+ let type_ids = PrimitiveArray :: empty :: < u8 > ( nullability ) . into_array ( ) ;
208225 let children: Vec < _ > = variants
209226 . variants ( )
210227 . map ( |dtype| crate :: Canonical :: empty ( & dtype) . into_array ( ) )
0 commit comments