44use vortex_buffer:: ByteBufferMut ;
55use vortex_buffer:: buffer;
66use vortex_error:: VortexResult ;
7+ use vortex_error:: vortex_err;
78use vortex_mask:: Mask ;
89use vortex_session:: registry:: ReadContext ;
910
1011use crate :: ArrayContext ;
12+ use crate :: Canonical ;
13+ use crate :: CanonicalValidity ;
1114use crate :: IntoArray ;
1215use crate :: VortexSessionExecute ;
1316use crate :: array_session;
@@ -302,12 +305,18 @@ fn constant_union_builds_nested_union_placeholders() -> VortexResult<()> {
302305 vec ! [ DType :: Primitive ( PType :: I64 , Nullability :: NonNullable ) ] ,
303306 vec ! [ 3 ] ,
304307 ) ?;
308+ let nested_placeholder = Scalar :: union (
309+ nested_variants. clone ( ) ,
310+ 3 ,
311+ 0_i64 . into ( ) ,
312+ Nullability :: NonNullable ,
313+ ) ?;
305314 let nested_dtype = DType :: Union ( nested_variants, Nullability :: NonNullable ) ;
306315 let outer_variants = UnionVariants :: try_new (
307316 [ "number" , "nested" ] . into ( ) ,
308317 vec ! [
309318 DType :: Primitive ( PType :: I32 , Nullability :: NonNullable ) ,
310- nested_dtype. clone ( ) ,
319+ nested_dtype,
311320 ] ,
312321 vec ! [ 5 , 9 ] ,
313322 ) ?;
@@ -324,7 +333,7 @@ fn constant_union_builds_nested_union_placeholders() -> VortexResult<()> {
324333 . execute :: < UnionArray > ( & mut ctx) ?;
325334 assert_eq ! (
326335 array. child_by_name( "nested" ) ?. execute_scalar( 0 , & mut ctx) ?,
327- Scalar :: zero_value ( & nested_dtype )
336+ nested_placeholder
328337 ) ;
329338
330339 let outer_null = Scalar :: null ( DType :: Union ( outer_variants, Nullability :: Nullable ) ) ;
@@ -333,12 +342,130 @@ fn constant_union_builds_nested_union_placeholders() -> VortexResult<()> {
333342 . execute :: < UnionArray > ( & mut ctx) ?;
334343 assert_eq ! (
335344 array. child_by_name( "nested" ) ?. execute_scalar( 0 , & mut ctx) ?,
336- Scalar :: zero_value ( & nested_dtype )
345+ nested_placeholder
337346 ) ;
338347
339348 Ok ( ( ) )
340349}
341350
351+ #[ test]
352+ fn constant_union_builds_deeply_nested_union_placeholders ( ) -> VortexResult < ( ) > {
353+ let nested_variants = UnionVariants :: try_new (
354+ [ "value" ] . into ( ) ,
355+ vec ! [ DType :: Primitive ( PType :: I64 , Nullability :: NonNullable ) ] ,
356+ vec ! [ 3 ] ,
357+ ) ?;
358+ let nested_placeholder = Scalar :: union (
359+ nested_variants. clone ( ) ,
360+ 3 ,
361+ 0_i64 . into ( ) ,
362+ Nullability :: NonNullable ,
363+ ) ?;
364+ let nested_dtype = DType :: Union ( nested_variants, Nullability :: NonNullable ) ;
365+ let wrapper_dtype = DType :: struct_ ( [ ( "nested" , nested_dtype) ] , Nullability :: NonNullable ) ;
366+ let outer_variants = UnionVariants :: try_new (
367+ [ "number" , "wrapper" ] . into ( ) ,
368+ vec ! [
369+ DType :: Primitive ( PType :: I32 , Nullability :: NonNullable ) ,
370+ wrapper_dtype,
371+ ] ,
372+ vec ! [ 5 , 9 ] ,
373+ ) ?;
374+ let selected_number =
375+ Scalar :: union ( outer_variants, 5 , 42_i32 . into ( ) , Nullability :: NonNullable ) ?;
376+ let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
377+ let array = ConstantArray :: new ( selected_number, 2 )
378+ . into_array ( )
379+ . execute :: < UnionArray > ( & mut ctx) ?;
380+ let wrapper = array
381+ . child_by_name ( "wrapper" ) ?
382+ . execute_scalar ( 0 , & mut ctx) ?;
383+
384+ assert_eq ! (
385+ wrapper. as_struct( ) . field( "nested" ) ,
386+ Some ( nested_placeholder)
387+ ) ;
388+
389+ Ok ( ( ) )
390+ }
391+
392+ #[ test]
393+ fn constant_union_rejects_uninhabited_placeholders_without_panicking ( ) -> VortexResult < ( ) > {
394+ let uninhabited = DType :: Union (
395+ UnionVariants :: new ( Default :: default ( ) , vec ! [ ] ) ?,
396+ Nullability :: NonNullable ,
397+ ) ;
398+ let outer_variants = UnionVariants :: try_new (
399+ [ "number" , "uninhabited" ] . into ( ) ,
400+ vec ! [
401+ DType :: Primitive ( PType :: I32 , Nullability :: NonNullable ) ,
402+ uninhabited,
403+ ] ,
404+ vec ! [ 5 , 9 ] ,
405+ ) ?;
406+ let selected_number =
407+ Scalar :: union ( outer_variants, 5 , 42_i32 . into ( ) , Nullability :: NonNullable ) ?;
408+ let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
409+
410+ assert ! (
411+ ConstantArray :: new( selected_number. clone( ) , 1 )
412+ . into_array( )
413+ . execute:: <UnionArray >( & mut ctx)
414+ . is_err( )
415+ ) ;
416+ assert_eq ! (
417+ ConstantArray :: new( selected_number, 0 )
418+ . into_array( )
419+ . execute:: <UnionArray >( & mut ctx) ?
420+ . len( ) ,
421+ 0
422+ ) ;
423+
424+ Ok ( ( ) )
425+ }
426+
427+ #[ test]
428+ fn empty_union_supports_variant_children ( ) -> VortexResult < ( ) > {
429+ let variants = UnionVariants :: try_new (
430+ [ "dynamic" ] . into ( ) ,
431+ vec ! [ DType :: Variant ( Nullability :: NonNullable ) ] ,
432+ vec ! [ 5 ] ,
433+ ) ?;
434+ let array = Canonical :: empty ( & DType :: Union ( variants, Nullability :: NonNullable ) ) . into_union ( ) ;
435+
436+ assert_eq ! ( array. len( ) , 0 ) ;
437+ assert_eq ! (
438+ array. child( 0 ) . map( |child| child. dtype( ) ) ,
439+ Some ( & DType :: Variant ( Nullability :: NonNullable ) )
440+ ) ;
441+
442+ Ok ( ( ) )
443+ }
444+
445+ #[ test]
446+ fn canonical_validity_canonicalizes_union_type_ids ( ) -> VortexResult < ( ) > {
447+ let array = UnionArray :: try_new (
448+ ConstantArray :: new ( Scalar :: primitive ( 5_u8 , Nullability :: Nullable ) , 2 ) . into_array ( ) ,
449+ variants ( ) ?,
450+ vec ! [
451+ PrimitiveArray :: from_iter( [ 10_i32 , 20 ] ) . into_array( ) ,
452+ BoolArray :: from_iter( [ false , true ] ) . into_array( ) ,
453+ ] ,
454+ ) ?;
455+ let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
456+ let Canonical :: Union ( array) = array. into_array ( ) . execute :: < CanonicalValidity > ( & mut ctx) ?. 0
457+ else {
458+ return Err ( vortex_err ! (
459+ "UnionArray must remain canonical Union storage"
460+ ) ) ;
461+ } ;
462+
463+ assert ! ( array. type_ids( ) . is:: <crate :: arrays:: Primitive >( ) ) ;
464+ assert_eq ! ( array. dtype( ) . nullability( ) , Nullability :: Nullable ) ;
465+
466+ Ok ( ( ) )
467+ }
468+
342469#[ test]
343470fn chunked_union_packs_components ( ) -> VortexResult < ( ) > {
344471 let first = union_array ( ) ?. into_array ( ) . slice ( 0 ..1 ) ?;
0 commit comments