@@ -56,7 +56,10 @@ use crate::{
5656 } ,
5757 event:: DEFAULT_TIMESTAMP_KEY ,
5858 hottier:: HotTierManager ,
59- metrics:: { QUERY_CACHE_HIT , increment_files_scanned_in_query_by_date} ,
59+ metrics:: {
60+ QUERY_CACHE_HIT , increment_files_scanned_in_hottier_by_date,
61+ increment_files_scanned_in_query_by_date,
62+ } ,
6063 option:: Mode ,
6164 parseable:: { DEFAULT_TENANT , PARSEABLE , STREAM_EXISTS } ,
6265 storage:: { ObjectStorage , ObjectStoreFormat } ,
@@ -205,6 +208,13 @@ impl StandardTableProvider {
205208 . await
206209 . map_err ( |err| DataFusionError :: External ( Box :: new ( err) ) ) ?;
207210
211+ increment_files_scanned_in_hottier_by_date (
212+ hot_tier_files. len ( ) as u64 ,
213+ & chrono:: Utc :: now ( ) . date_naive ( ) . to_string ( ) ,
214+ self . tenant_id . as_deref ( ) . unwrap_or ( DEFAULT_TENANT ) ,
215+ & self . stream ,
216+ ) ;
217+
208218 let hot_tier_files: Vec < File > = hot_tier_files
209219 . into_iter ( )
210220 . map ( |mut file| {
@@ -352,101 +362,108 @@ impl StandardTableProvider {
352362 & self ,
353363 manifest_files : Vec < File > ,
354364 ) -> ( Vec < Vec < PartitionedFile > > , datafusion:: common:: Statistics ) {
355- let target_partition: usize = num_cpus:: get ( ) ;
356- let mut partitioned_files = Vec :: from_iter ( ( 0 ..target_partition) . map ( |_| Vec :: new ( ) ) ) ;
357- let mut column_statistics = HashMap :: < String , Option < TypedStatistics > > :: new ( ) ;
358- let mut count = 0 ;
359- let mut file_count = 0u64 ;
360- for ( index, file) in manifest_files
361- . into_iter ( )
362- . enumerate ( )
363- . map ( |( x, y) | ( x % target_partition, y) )
365+ partitioned_files ( & self . schema , & self . tenant_id , manifest_files)
366+ }
367+ }
368+
369+ #[ inline( always) ]
370+ pub fn partitioned_files (
371+ schema : & SchemaRef ,
372+ tenant_id : & Option < String > ,
373+ manifest_files : Vec < File > ,
374+ ) -> ( Vec < Vec < PartitionedFile > > , datafusion:: common:: Statistics ) {
375+ let target_partition: usize = num_cpus:: get ( ) ;
376+ let mut partitioned_files = Vec :: from_iter ( ( 0 ..target_partition) . map ( |_| Vec :: new ( ) ) ) ;
377+ let mut column_statistics = HashMap :: < String , Option < TypedStatistics > > :: new ( ) ;
378+ let mut count = 0 ;
379+ let mut file_count = 0u64 ;
380+ for ( index, file) in manifest_files
381+ . into_iter ( )
382+ . enumerate ( )
383+ . map ( |( x, y) | ( x % target_partition, y) )
384+ {
385+ #[ allow( unused_mut) ]
386+ let File {
387+ mut file_path,
388+ num_rows,
389+ columns,
390+ ..
391+ } = file;
392+
393+ // Track billing metrics for files scanned in query
394+ file_count += 1 ;
395+
396+ // object_store::path::Path doesn't automatically deal with Windows path separators
397+ // to do that, we are using from_absolute_path() which takes into consideration the underlying filesystem
398+ // before sending the file path to PartitionedFile
399+ // the github issue- https://github.com/parseablehq/parseable/issues/824
400+ // For some reason, the `from_absolute_path()` doesn't work for macos, hence the ugly solution
401+ // TODO: figure out an elegant solution to this
402+ #[ cfg( windows) ]
364403 {
365- #[ allow( unused_mut) ]
366- let File {
367- mut file_path,
368- num_rows,
369- columns,
370- ..
371- } = file;
372-
373- // Track billing metrics for files scanned in query
374- file_count += 1 ;
375-
376- // object_store::path::Path doesn't automatically deal with Windows path separators
377- // to do that, we are using from_absolute_path() which takes into consideration the underlying filesystem
378- // before sending the file path to PartitionedFile
379- // the github issue- https://github.com/parseablehq/parseable/issues/824
380- // For some reason, the `from_absolute_path()` doesn't work for macos, hence the ugly solution
381- // TODO: figure out an elegant solution to this
382- #[ cfg( windows) ]
383- {
384- if PARSEABLE . storage . name ( ) == "drive" {
385- file_path = object_store:: path:: Path :: from_absolute_path ( file_path)
386- . unwrap ( )
387- . to_string ( ) ;
388- }
404+ if PARSEABLE . storage . name ( ) == "drive" {
405+ file_path = object_store:: path:: Path :: from_absolute_path ( file_path)
406+ . unwrap ( )
407+ . to_string ( ) ;
389408 }
390- let pf = PartitionedFile :: new ( file_path, file. file_size ) ;
391- partitioned_files[ index] . push ( pf) ;
392-
393- columns. into_iter ( ) . for_each ( |col| {
394- column_statistics
395- . entry ( col. name )
396- . and_modify ( |x| {
397- if let Some ( ( stats, col_stats) ) = x. as_ref ( ) . cloned ( ) . zip ( col. stats . clone ( ) )
398- {
399- // update() returns None on type mismatch (e.g. column
400- // historically written as both Utf8 and Timestamp(ms)).
401- // Dropping to None here makes the planner skip min/max
402- // pushdown for this column instead of crashing the worker.
403- * x = stats. update ( col_stats) ;
404- }
405- } )
406- . or_insert_with ( || col. stats . as_ref ( ) . cloned ( ) ) ;
407- } ) ;
408- count += num_rows;
409409 }
410- let statistics = self
411- . schema
412- . fields ( )
413- . iter ( )
414- . map ( |field| {
415- column_statistics
416- . get ( field. name ( ) )
417- . and_then ( |stats| stats. as_ref ( ) )
418- . and_then ( |stats| stats. clone ( ) . min_max_as_scalar ( field. data_type ( ) ) )
419- . map ( |( min, max) | datafusion:: common:: ColumnStatistics {
420- null_count : Precision :: Absent ,
421- max_value : Precision :: Exact ( max) ,
422- min_value : Precision :: Exact ( min) ,
423- distinct_count : Precision :: Absent ,
424- sum_value : Precision :: Absent ,
425- byte_size : Precision :: Absent ,
426- } )
427- . unwrap_or_default ( )
428- } )
429- . collect ( ) ;
410+ let pf = PartitionedFile :: new ( file_path, file. file_size ) ;
411+ partitioned_files[ index] . push ( pf) ;
412+
413+ columns. into_iter ( ) . for_each ( |col| {
414+ column_statistics
415+ . entry ( col. name )
416+ . and_modify ( |x| {
417+ if let Some ( ( stats, col_stats) ) = x. as_ref ( ) . cloned ( ) . zip ( col. stats . clone ( ) ) {
418+ // update() returns None on type mismatch (e.g. column
419+ // historically written as both Utf8 and Timestamp(ms)).
420+ // Dropping to None here makes the planner skip min/max
421+ // pushdown for this column instead of crashing the worker.
422+ * x = stats. update ( col_stats) ;
423+ }
424+ } )
425+ . or_insert_with ( || col. stats . as_ref ( ) . cloned ( ) ) ;
426+ } ) ;
427+ count += num_rows;
428+ }
429+ let statistics = schema
430+ . fields ( )
431+ . iter ( )
432+ . map ( |field| {
433+ column_statistics
434+ . get ( field. name ( ) )
435+ . and_then ( |stats| stats. as_ref ( ) )
436+ . and_then ( |stats| stats. clone ( ) . min_max_as_scalar ( field. data_type ( ) ) )
437+ . map ( |( min, max) | datafusion:: common:: ColumnStatistics {
438+ null_count : Precision :: Absent ,
439+ max_value : Precision :: Exact ( max) ,
440+ min_value : Precision :: Exact ( min) ,
441+ distinct_count : Precision :: Absent ,
442+ sum_value : Precision :: Absent ,
443+ byte_size : Precision :: Absent ,
444+ } )
445+ . unwrap_or_default ( )
446+ } )
447+ . collect ( ) ;
430448
431- let statistics = datafusion:: common:: Statistics {
432- num_rows : Precision :: Exact ( count as usize ) ,
433- total_byte_size : Precision :: Absent ,
434- column_statistics : statistics,
435- } ;
449+ let statistics = datafusion:: common:: Statistics {
450+ num_rows : Precision :: Exact ( count as usize ) ,
451+ total_byte_size : Precision :: Absent ,
452+ column_statistics : statistics,
453+ } ;
436454
437- // Track billing metrics for query scan
438- let current_date = chrono:: Utc :: now ( ) . date_naive ( ) . to_string ( ) ;
439- increment_files_scanned_in_query_by_date (
440- file_count,
441- & current_date,
442- self . tenant_id . as_deref ( ) . unwrap_or ( DEFAULT_TENANT ) ,
443- ) ;
455+ // Track billing metrics for query scan
456+ let current_date = chrono:: Utc :: now ( ) . date_naive ( ) . to_string ( ) ;
457+ increment_files_scanned_in_query_by_date (
458+ file_count,
459+ & current_date,
460+ tenant_id. as_deref ( ) . unwrap_or ( DEFAULT_TENANT ) ,
461+ ) ;
444462
445- ( partitioned_files, statistics)
446- }
463+ ( partitioned_files, statistics)
447464}
448465
449- async fn collect_from_snapshot (
466+ pub async fn collect_from_snapshot (
450467 snapshot : & Snapshot ,
451468 time_filters : & [ PartialTimeFilter ] ,
452469 filters : & [ Expr ] ,
@@ -683,7 +700,8 @@ impl TableProvider for StandardTableProvider {
683700 }
684701}
685702
686- fn reversed_mem_table (
703+ #[ inline( always) ]
704+ pub fn reversed_mem_table (
687705 mut records : Vec < RecordBatch > ,
688706 schema : Arc < Schema > ,
689707) -> Result < MemTable , DataFusionError > {
@@ -863,7 +881,7 @@ pub fn is_within_staging_window(time_filters: &[PartialTimeFilter]) -> bool {
863881 !has_upper_bound
864882}
865883
866- fn expr_in_boundary ( filter : & Expr ) -> bool {
884+ pub fn expr_in_boundary ( filter : & Expr ) -> bool {
867885 let Expr :: BinaryExpr ( binexpr) = filter else {
868886 return false ;
869887 } ;
@@ -881,7 +899,7 @@ fn expr_in_boundary(filter: &Expr) -> bool {
881899 )
882900}
883901
884- fn extract_timestamp_bound (
902+ pub fn extract_timestamp_bound (
885903 binexpr : & BinaryExpr ,
886904 time_partition : & Option < String > ,
887905) -> Option < ( Operator , NaiveDateTime ) > {
0 commit comments