@@ -9,6 +9,8 @@ use rstest::rstest;
99use vortex_buffer:: BufferMut ;
1010use vortex_error:: VortexResult ;
1111use vortex_error:: vortex_bail;
12+ use vortex_error:: vortex_ensure;
13+ use vortex_error:: vortex_err;
1214use vortex_session:: registry:: CachedId ;
1315
1416use super :: finalize_kernel_output;
@@ -18,6 +20,7 @@ use crate::VortexSessionExecute;
1820use crate :: array_session;
1921use crate :: arrays:: ConstantArray ;
2022use crate :: arrays:: ExtensionArray ;
23+ use crate :: arrays:: FixedSizeListArray ;
2124use crate :: arrays:: PrimitiveArray ;
2225use crate :: assert_arrays_eq;
2326use crate :: dtype:: DType ;
@@ -30,6 +33,8 @@ use crate::scalar::Scalar;
3033use crate :: scalar_fn:: EmptyOptions ;
3134use crate :: scalar_fn:: ScalarFnId ;
3235use crate :: scalar_fn:: VecExecutionArgs ;
36+ use crate :: scalar_fn:: unstable:: row:: FixedSizeListSink ;
37+ use crate :: scalar_fn:: unstable:: row:: InitializedRow ;
3338use crate :: scalar_fn:: unstable:: row:: OutputElement ;
3439use crate :: scalar_fn:: unstable:: row:: OutputSink ;
3540use crate :: scalar_fn:: unstable:: row:: RowFn ;
@@ -78,15 +83,16 @@ struct I64Sink(BufferMut<i64>);
7883// SAFETY: every row is initialized by `BufferMut::zeroed`, and the sink exposes exactly that
7984// initialized slice. The `()` write token therefore proves no additional invariant.
8085unsafe impl OutputSink for I64Sink {
86+ type Params = ( ) ;
8187 type Rows < ' a > = & ' a mut [ i64 ] ;
8288 type Row < ' a > = & ' a mut i64 ;
8389 type WriteToken = ( ) ;
8490
85- fn storage_dtype ( ) -> DType {
91+ fn storage_dtype ( _params : & Self :: Params ) -> DType {
8692 DType :: from ( i64:: PTYPE )
8793 }
8894
89- fn with_capacity ( rows : usize ) -> VortexResult < Self > {
95+ fn with_capacity ( rows : usize , _params : & Self :: Params ) -> VortexResult < Self > {
9096 Ok ( Self ( BufferMut :: zeroed ( rows) ) )
9197 }
9298
@@ -104,6 +110,38 @@ unsafe impl OutputSink for I64Sink {
104110 }
105111}
106112
113+ #[ derive( Clone ) ]
114+ struct RepeatValue ;
115+
116+ impl RowFn for RepeatValue {
117+ type Options = usize ;
118+
119+ const ARG_NAMES : & ' static [ & ' static str ] = & [ "value" ] ;
120+ const INFALLIBLE : bool = true ;
121+
122+ fn id ( & self ) -> ScalarFnId {
123+ static ID : CachedId = CachedId :: new ( "test.repeat_value" ) ;
124+ * ID
125+ }
126+
127+ fn dispatch < V : RowVisitor > (
128+ & self ,
129+ width : & Self :: Options ,
130+ _args : & [ DType ] ,
131+ visitor : V ,
132+ ) -> VortexResult < V :: VisitResult > {
133+ vortex_ensure ! (
134+ u32 :: try_from( * width) . is_ok( ) ,
135+ InvalidArgument :
136+ "test.repeat_value width must fit in the fixed-size-list u32 list size, got {width}" ,
137+ ) ;
138+
139+ visitor. visit_into :: < ( i64 , ) , FixedSizeListSink < i64 > , _ > ( * width, |( value, ) , row| {
140+ InitializedRow :: fill ( row, |_| value)
141+ } )
142+ }
143+ }
144+
107145impl RowFn for DeferredAdd {
108146 type Options = EmptyOptions ;
109147
@@ -156,7 +194,7 @@ impl RowFn for ValidOnlyIdentity {
156194 _args : & [ DType ] ,
157195 visitor : V ,
158196 ) -> VortexResult < V :: VisitResult > {
159- visitor. visit_into :: < ( i64 , ) , I64Sink , VortexResult < ( ) > > ( |( value, ) , output| {
197+ visitor. visit_into :: < ( i64 , ) , I64Sink , VortexResult < ( ) > > ( ( ) , |( value, ) , output| {
160198 * output = value;
161199 Ok ( ( ) )
162200 } )
@@ -184,6 +222,58 @@ impl RowFn for InvalidKernelOutput {
184222 }
185223}
186224
225+ #[ rstest]
226+ #[ case:: dense_width_two(
227+ vec![ 1_i64 , 2 ] ,
228+ Validity :: NonNullable ,
229+ 2 ,
230+ vec![ 1_i64 , 1 , 2 , 2 ] ,
231+ ) ]
232+ #[ case:: dense_width_four(
233+ vec![ 3_i64 , 4 ] ,
234+ Validity :: NonNullable ,
235+ 4 ,
236+ vec![ 3_i64 , 3 , 3 , 3 , 4 , 4 , 4 , 4 ] ,
237+ ) ]
238+ #[ case:: empty( vec![ ] , Validity :: NonNullable , 3 , vec![ ] ) ]
239+ #[ case:: zero_width( vec![ 3_i64 , 4 ] , Validity :: NonNullable , 0 , vec![ ] ) ]
240+ #[ case:: all_null(
241+ vec![ 5_i64 , 6 ] ,
242+ Validity :: AllInvalid ,
243+ 2 ,
244+ vec![ 0_i64 , 0 , 0 , 0 ] ,
245+ ) ]
246+ #[ case:: partially_valid(
247+ vec![ 7_i64 , 8 , 9 ] ,
248+ Validity :: from_iter( [ true , false , true ] ) ,
249+ 3 ,
250+ vec![ 7_i64 , 7 , 7 , 0 , 0 , 0 , 9 , 9 , 9 ] ,
251+ ) ]
252+ fn test_fixed_size_list_sink_uses_runtime_width (
253+ #[ case] input_values : Vec < i64 > ,
254+ #[ case] validity : Validity ,
255+ #[ case] width : usize ,
256+ #[ case] expected_elements : Vec < i64 > ,
257+ ) -> VortexResult < ( ) > {
258+ let row_count = input_values. len ( ) ;
259+ let input = PrimitiveArray :: new ( input_values, validity. clone ( ) ) . into_array ( ) ;
260+ let args = VecExecutionArgs :: new ( vec ! [ input] , row_count) ;
261+ let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
262+
263+ let actual = execute_rows ( & RepeatValue , & width, & args, & mut ctx) ?;
264+ let expected = FixedSizeListArray :: new (
265+ PrimitiveArray :: from_iter ( expected_elements) . into_array ( ) ,
266+ u32:: try_from ( width)
267+ . map_err ( |_| vortex_err ! ( InvalidArgument : "test width must fit in u32, got {width}" ) ) ?,
268+ validity,
269+ row_count,
270+ )
271+ . into_array ( ) ;
272+
273+ assert_arrays_eq ! ( & actual, & expected, & mut ctx) ;
274+ Ok ( ( ) )
275+ }
276+
187277#[ test]
188278fn test_finalize_kernel_output_rejects_nested_dtype_mismatch ( ) -> VortexResult < ( ) > {
189279 static ID : CachedId = CachedId :: new ( "test.finalize_kernel_output" ) ;
@@ -403,7 +493,7 @@ impl RowFn for DeclaredSinkOutput {
403493 ) -> VortexResult < V :: VisitResult > {
404494 visitor
405495 . with_output_dtype ( timestamp_dtype ( Nullability :: NonNullable ) )
406- . visit_into :: < ( i64 , ) , I64Sink , VortexResult < ( ) > > ( |( value, ) , output| {
496+ . visit_into :: < ( i64 , ) , I64Sink , VortexResult < ( ) > > ( ( ) , |( value, ) , output| {
407497 * output = value;
408498 Ok ( ( ) )
409499 } )
0 commit comments