@@ -40,6 +40,8 @@ use vortex_scan::selection::Selection;
4040use vortex_session:: VortexSession ;
4141
4242use crate :: LayoutReaderRef ;
43+ use crate :: scan:: limit:: SharedRowLimit ;
44+ use crate :: scan:: limit:: limit_array_stream_shared;
4345use crate :: scan:: scan_builder:: ScanBuilder ;
4446
4547/// An implementation of a [`DataSource`] that reads data from a [`LayoutReaderRef`].
@@ -157,13 +159,16 @@ impl DataSource for LayoutReaderDataSource {
157159 }
158160 }
159161
162+ let shared_limit = scan_request. limit . map ( SharedRowLimit :: new) ;
163+
160164 Ok ( Box :: new ( LayoutReaderScan {
161165 reader : Arc :: clone ( & self . reader ) ,
162166 session : self . session . clone ( ) ,
163167 dtype,
164168 projection : scan_request. projection ,
165169 filter : scan_request. filter ,
166170 limit : scan_request. limit ,
171+ shared_limit,
167172 selection : scan_request. selection ,
168173 ordered : scan_request. ordered ,
169174 metrics_registry : self . metrics_registry . clone ( ) ,
@@ -185,6 +190,7 @@ struct LayoutReaderScan {
185190 projection : Expression ,
186191 filter : Option < Expression > ,
187192 limit : Option < u64 > ,
193+ shared_limit : Option < SharedRowLimit > ,
188194 ordered : bool ,
189195 selection : Selection ,
190196 metrics_registry : Option < Arc < dyn MetricsRegistry > > ,
@@ -251,6 +257,7 @@ impl Stream for LayoutReaderScan {
251257 projection : this. projection . clone ( ) ,
252258 filter : this. filter . clone ( ) ,
253259 limit : split_limit,
260+ shared_limit : this. shared_limit . clone ( ) ,
254261 ordered : this. ordered ,
255262 row_range,
256263 selection : this. selection . clone ( ) ,
@@ -278,6 +285,7 @@ struct LayoutReaderSplit {
278285 projection : Expression ,
279286 filter : Option < Expression > ,
280287 limit : Option < u64 > ,
288+ shared_limit : Option < SharedRowLimit > ,
281289 ordered : bool ,
282290 row_range : Range < u64 > ,
283291 selection : Selection ,
@@ -312,6 +320,7 @@ impl Partition for LayoutReaderSplit {
312320 }
313321
314322 fn execute ( self : Box < Self > ) -> VortexResult < SendableArrayStream > {
323+ let shared_limit = self . shared_limit . clone ( ) ;
315324 let builder = ScanBuilder :: new ( self . session , self . reader )
316325 . with_row_range ( self . row_range )
317326 . with_selection ( self . selection )
@@ -324,7 +333,7 @@ impl Partition for LayoutReaderSplit {
324333 let dtype = builder. dtype ( ) ?;
325334 // Use into_stream() which creates a LazyScanStream that spawns individual I/O
326335 // tasks onto the runtime, enabling parallel execution across executor threads.
327- let stream = builder. into_stream ( ) ?;
336+ let stream = limit_array_stream_shared ( builder. into_stream ( ) ?, shared_limit ) ;
328337
329338 Ok ( ArrayStreamExt :: boxed ( ArrayStreamAdapter :: new (
330339 dtype, stream,
@@ -391,3 +400,143 @@ impl Partition for Empty {
391400 ) ) )
392401 }
393402}
403+
404+ #[ cfg( test) ]
405+ mod tests {
406+ use std:: ops:: Range ;
407+ use std:: sync:: Arc ;
408+
409+ use futures:: TryStreamExt ;
410+ use vortex_array:: IntoArray ;
411+ use vortex_array:: MaskFuture ;
412+ use vortex_array:: VortexSessionExecute ;
413+ use vortex_array:: array_session;
414+ use vortex_array:: arrays:: PrimitiveArray ;
415+ use vortex_array:: dtype:: DType ;
416+ use vortex_array:: dtype:: FieldMask ;
417+ use vortex_array:: dtype:: Nullability ;
418+ use vortex_array:: dtype:: PType ;
419+ use vortex_array:: expr:: Expression ;
420+ use vortex_array:: expr:: root;
421+ use vortex_error:: VortexResult ;
422+ use vortex_io:: runtime:: BlockingRuntime ;
423+ use vortex_io:: runtime:: single:: SingleThreadRuntime ;
424+ use vortex_mask:: Mask ;
425+ use vortex_scan:: DataSource ;
426+ use vortex_scan:: ScanRequest ;
427+
428+ use super :: LayoutReaderDataSource ;
429+ use crate :: ArrayFuture ;
430+ use crate :: LayoutReader ;
431+ use crate :: RowSplits ;
432+ use crate :: SplitRange ;
433+ use crate :: scan:: test:: session_with_handle;
434+
435+ #[ derive( Debug ) ]
436+ struct TestLayoutReader {
437+ name : Arc < str > ,
438+ dtype : DType ,
439+ row_count : u64 ,
440+ }
441+
442+ impl TestLayoutReader {
443+ fn new ( row_count : u64 ) -> Self {
444+ Self {
445+ name : Arc :: from ( "test" ) ,
446+ dtype : DType :: Primitive ( PType :: I32 , Nullability :: NonNullable ) ,
447+ row_count,
448+ }
449+ }
450+ }
451+
452+ impl LayoutReader for TestLayoutReader {
453+ fn name ( & self ) -> & Arc < str > {
454+ & self . name
455+ }
456+
457+ fn as_any ( & self ) -> & dyn std:: any:: Any {
458+ self
459+ }
460+
461+ fn dtype ( & self ) -> & DType {
462+ & self . dtype
463+ }
464+
465+ fn row_count ( & self ) -> u64 {
466+ self . row_count
467+ }
468+
469+ fn register_splits (
470+ & self ,
471+ _field_mask : & [ FieldMask ] ,
472+ split_range : & SplitRange ,
473+ splits : & mut RowSplits ,
474+ ) -> VortexResult < ( ) > {
475+ splits. push ( split_range. root_row_range ( ) . end ) ;
476+ Ok ( ( ) )
477+ }
478+
479+ fn pruning_evaluation (
480+ & self ,
481+ _row_range : & Range < u64 > ,
482+ _expr : & Expression ,
483+ mask : Mask ,
484+ ) -> VortexResult < MaskFuture > {
485+ Ok ( MaskFuture :: ready ( mask) )
486+ }
487+
488+ fn filter_evaluation (
489+ & self ,
490+ _row_range : & Range < u64 > ,
491+ _expr : & Expression ,
492+ mask : MaskFuture ,
493+ ) -> VortexResult < MaskFuture > {
494+ Ok ( mask)
495+ }
496+
497+ fn projection_evaluation (
498+ & self ,
499+ row_range : & Range < u64 > ,
500+ _expr : & Expression ,
501+ mask : MaskFuture ,
502+ ) -> VortexResult < ArrayFuture > {
503+ let row_range = row_range. clone ( ) ;
504+
505+ Ok ( Box :: pin ( async move {
506+ let start = i32:: try_from ( row_range. start ) ?;
507+ let end = i32:: try_from ( row_range. end ) ?;
508+ PrimitiveArray :: from_iter ( start..end)
509+ . into_array ( )
510+ . filter ( mask. await ?)
511+ } ) )
512+ }
513+ }
514+
515+ #[ test]
516+ fn filtered_limit_is_global_across_scan_partitions ( ) -> VortexResult < ( ) > {
517+ let runtime = SingleThreadRuntime :: default ( ) ;
518+ let session = session_with_handle ( runtime. handle ( ) ) ;
519+ let source = LayoutReaderDataSource :: new ( Arc :: new ( TestLayoutReader :: new ( 6 ) ) , session)
520+ . with_split_max_row_count ( 2 ) ;
521+
522+ let scan = runtime. block_on ( source. scan ( ScanRequest {
523+ filter : Some ( root ( ) ) ,
524+ limit : Some ( 3 ) ,
525+ ordered : true ,
526+ ..Default :: default ( )
527+ } ) ) ?;
528+ let partitions = runtime. block_on ( scan. partitions ( ) . try_collect :: < Vec < _ > > ( ) ) ?;
529+
530+ let mut ctx = array_session ( ) . create_execution_ctx ( ) ;
531+ let mut values = Vec :: new ( ) ;
532+ for partition in partitions {
533+ for chunk in runtime. block_on_stream ( partition. execute ( ) ?) {
534+ let primitive = chunk?. execute :: < PrimitiveArray > ( & mut ctx) ?;
535+ values. extend ( primitive. into_buffer :: < i32 > ( ) ) ;
536+ }
537+ }
538+
539+ assert_eq ! ( values, [ 0 , 1 , 2 ] ) ;
540+ Ok ( ( ) )
541+ }
542+ }
0 commit comments