11// SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
4- use vortex_error:: VortexExpect ;
54use vortex_error:: VortexResult ;
65use vortex_error:: vortex_ensure;
7- use vortex_mask:: Mask ;
86
97use crate :: ArrayRef ;
108use crate :: ExecutionCtx ;
119use crate :: IntoArray ;
1210use crate :: arrays:: ChunkedArray ;
13- use crate :: arrays:: MaskedArray ;
1411use crate :: builders:: ArrayBuilder ;
1512use crate :: builders:: builder_with_capacity;
1613use crate :: dtype:: DType ;
17- use crate :: dtype:: Nullability ;
1814use crate :: scalar:: Scalar ;
19- use crate :: validity:: Validity ;
2015
2116/// Accumulates the child of a nested [`ArrayBuilder`] without canonicalizing appended arrays.
2217///
@@ -121,42 +116,6 @@ impl ChildBuilder {
121116 self . pending . reserve_exact ( additional)
122117 }
123118
124- /// Overrides the validity of every value appended so far.
125- ///
126- /// # Safety
127- ///
128- /// `validity` must have the same length as [`self.len()`](Self::len).
129- ///
130- /// # Panics
131- ///
132- /// Panics if a chunk that was kept in its original encoding contains nulls, since replacing
133- /// the validity of such a chunk would require decoding it.
134- pub unsafe fn set_validity_unchecked ( & mut self , validity : Mask ) {
135- if !self . dtype . is_nullable ( ) {
136- return ;
137- }
138-
139- if self . chunks . is_empty ( ) {
140- // Fast path: every value lives in the scalar builder, which owns its null buffer.
141- unsafe { self . pending . set_validity_unchecked ( validity) } ;
142- return ;
143- }
144-
145- // The chunks carry their own validity, so the override has to be pushed into each of them.
146- self . flush_pending ( ) ;
147- let mut offset = 0 ;
148- for chunk in & mut self . chunks {
149- let end = offset + chunk. len ( ) ;
150- * chunk = MaskedArray :: try_new (
151- chunk. clone ( ) ,
152- Validity :: from_mask ( validity. slice ( offset..end) , Nullability :: Nullable ) ,
153- )
154- . vortex_expect ( "cannot override the validity of a child chunk that contains nulls" )
155- . into_array ( ) ;
156- offset = end;
157- }
158- }
159-
160119 /// Finishes the child, combining the accumulated chunks into a [`ChunkedArray`] when there is
161120 /// more than one of them.
162121 pub fn finish ( & mut self ) -> ArrayRef {
@@ -172,9 +131,7 @@ impl ChildBuilder {
172131 return chunks. remove ( 0 ) ;
173132 }
174133
175- ChunkedArray :: try_new ( chunks, self . dtype . clone ( ) )
176- . vortex_expect ( "every child chunk has the child dtype" )
177- . into_array ( )
134+ unsafe { ChunkedArray :: new_unchecked ( chunks, self . dtype . clone ( ) ) } . into_array ( )
178135 }
179136
180137 /// Moves whatever the scalar builder holds into `chunks`, keeping the chunks in logical order.
@@ -192,9 +149,7 @@ impl ChildBuilder {
192149mod tests {
193150 use rstest:: rstest;
194151 use vortex_buffer:: buffer;
195- use vortex_error:: VortexExpect ;
196152 use vortex_error:: VortexResult ;
197- use vortex_mask:: Mask ;
198153
199154 use super :: ChildBuilder ;
200155 use crate :: ArrayRef ;
@@ -205,7 +160,6 @@ mod tests {
205160 use crate :: arrays:: ChunkedArray ;
206161 use crate :: arrays:: Constant ;
207162 use crate :: arrays:: ConstantArray ;
208- use crate :: arrays:: Masked ;
209163 use crate :: arrays:: Primitive ;
210164 use crate :: arrays:: PrimitiveArray ;
211165 use crate :: arrays:: chunked:: ChunkedArrayExt ;
@@ -380,104 +334,6 @@ mod tests {
380334 Ok ( ( ) )
381335 }
382336
383- /// Overriding the validity once chunks exist has to push the override into each chunk, sliced
384- /// to that chunk's own range.
385- #[ test]
386- fn test_set_validity_pushes_the_override_into_every_chunk ( ) -> VortexResult < ( ) > {
387- let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
388- let dtype = DType :: Primitive ( I32 , Nullable ) ;
389- let mut builder = ChildBuilder :: with_capacity ( & dtype, 0 ) ;
390-
391- builder. append_array ( & nullable_constant ( 1 , CHUNK_LEN ) , & mut ctx) ?;
392- builder. append_array ( & nullable_constant ( 2 , CHUNK_LEN ) , & mut ctx) ?;
393-
394- // Straddle the chunk boundary, so an override sliced wrongly cannot pass.
395- let invalid = [ CHUNK_LEN - 1 , CHUNK_LEN ] ;
396- let validity = Mask :: from_iter ( ( 0 ..2 * CHUNK_LEN ) . map ( |i| !invalid. contains ( & i) ) ) ;
397- unsafe { builder. set_validity_unchecked ( validity) } ;
398-
399- let child = builder. finish ( ) ;
400- let chunked = child. as_ :: < Chunked > ( ) ;
401- assert_eq ! ( chunked. nchunks( ) , 2 ) ;
402- // The override was layered over the chunks rather than decoding them.
403- assert ! ( chunked. iter_chunks( ) . all( |chunk| chunk. is:: <Masked >( ) ) ) ;
404-
405- let expected = PrimitiveArray :: from_option_iter (
406- ( 0 ..2 * CHUNK_LEN )
407- . map ( |i| ( !invalid. contains ( & i) ) . then_some ( if i < CHUNK_LEN { 1i32 } else { 2 } ) ) ,
408- )
409- . into_array ( ) ;
410- assert_arrays_eq ! ( & child, & expected, & mut ctx) ;
411-
412- Ok ( ( ) )
413- }
414-
415- /// Values still sitting in the scalar builder are part of the override too.
416- #[ test]
417- fn test_set_validity_covers_pending_scalars ( ) -> VortexResult < ( ) > {
418- let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
419- let dtype = DType :: Primitive ( I32 , Nullable ) ;
420- let mut builder = ChildBuilder :: with_capacity ( & dtype, 0 ) ;
421-
422- builder. append_array ( & nullable_constant ( 1 , CHUNK_LEN ) , & mut ctx) ?;
423- builder. append_scalar ( & Scalar :: primitive ( 2i32 , Nullable ) ) ?;
424-
425- let mut validity = vec ! [ true ; CHUNK_LEN + 1 ] ;
426- validity[ CHUNK_LEN ] = false ;
427- unsafe { builder. set_validity_unchecked ( Mask :: from_iter ( validity) ) } ;
428-
429- let child = builder. finish ( ) ;
430- let expected = PrimitiveArray :: from_option_iter (
431- std:: iter:: repeat_n ( Some ( 1i32 ) , CHUNK_LEN ) . chain ( [ None ] ) ,
432- )
433- . into_array ( ) ;
434- assert_arrays_eq ! ( & child, & expected, & mut ctx) ;
435-
436- Ok ( ( ) )
437- }
438-
439- /// A non-nullable child cannot carry nulls, so the override is dropped and the chunks are left
440- /// exactly as they were appended.
441- #[ test]
442- fn test_set_validity_is_a_noop_for_a_non_nullable_child ( ) -> VortexResult < ( ) > {
443- let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
444- let mut builder = ChildBuilder :: with_capacity ( & DType :: from ( I32 ) , 0 ) ;
445-
446- builder. append_array ( & constant ( 1 , CHUNK_LEN ) , & mut ctx) ?;
447- builder. append_array ( & constant ( 2 , CHUNK_LEN ) , & mut ctx) ?;
448- unsafe { builder. set_validity_unchecked ( Mask :: new_false ( 2 * CHUNK_LEN ) ) } ;
449-
450- let child = builder. finish ( ) ;
451- let chunked = child. as_ :: < Chunked > ( ) ;
452- assert ! ( chunked. iter_chunks( ) . all( |chunk| chunk. is:: <Constant >( ) ) ) ;
453-
454- let expected = ChunkedArray :: try_new (
455- vec ! [ constant( 1 , CHUNK_LEN ) , constant( 2 , CHUNK_LEN ) ] ,
456- DType :: from ( I32 ) ,
457- ) ?
458- . into_array ( ) ;
459- assert_arrays_eq ! ( & child, & expected, & mut ctx) ;
460-
461- Ok ( ( ) )
462- }
463-
464- /// Replacing the validity of a chunk that already contains nulls would mean decoding it, which
465- /// is exactly what the chunk exists to avoid.
466- #[ test]
467- #[ should_panic( expected = "cannot override the validity of a child chunk that contains nulls" ) ]
468- fn test_set_validity_rejects_a_chunk_that_contains_nulls ( ) {
469- let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
470- let dtype = DType :: Primitive ( I32 , Nullable ) ;
471- let mut builder = ChildBuilder :: with_capacity ( & dtype, 0 ) ;
472-
473- let with_nulls = ConstantArray :: new ( Scalar :: null ( dtype) , CHUNK_LEN ) . into_array ( ) ;
474- builder
475- . append_array ( & with_nulls, & mut ctx)
476- . vortex_expect ( "append" ) ;
477-
478- unsafe { builder. set_validity_unchecked ( Mask :: new_true ( CHUNK_LEN ) ) } ;
479- }
480-
481337 #[ test]
482338 fn test_finish_resets_the_builder ( ) -> VortexResult < ( ) > {
483339 let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
0 commit comments