1616 *
1717 */
1818
19- use std:: { any:: Any , collections:: HashMap , ops:: Bound , sync:: Arc } ;
20-
2119use arrow_array:: RecordBatch ;
2220use arrow_schema:: { Schema , SchemaRef , SortOptions } ;
2321use bytes:: Bytes ;
@@ -46,6 +44,7 @@ use datafusion::{
4644use futures_util:: { stream:: FuturesOrdered , StreamExt , TryFutureExt , TryStreamExt } ;
4745use itertools:: Itertools ;
4846use object_store:: { path:: Path , ObjectStore } ;
47+ use std:: { any:: Any , collections:: HashMap , ops:: Bound , sync:: Arc } ;
4948use url:: Url ;
5049
5150use crate :: {
@@ -114,6 +113,7 @@ async fn create_parquet_physical_plan(
114113 filters : & [ Expr ] ,
115114 limit : Option < usize > ,
116115 state : & SessionState ,
116+ time_partition : Option < String > ,
117117) -> Result < Arc < dyn ExecutionPlan > , DataFusionError > {
118118 let filters = if let Some ( expr) = conjunction ( filters. to_vec ( ) ) {
119119 let table_df_schema = schema. as_ref ( ) . clone ( ) . to_dfschema ( ) ?;
@@ -125,14 +125,18 @@ async fn create_parquet_physical_plan(
125125 } ;
126126
127127 let sort_expr = PhysicalSortExpr {
128- expr : physical_plan:: expressions:: col ( DEFAULT_TIMESTAMP_KEY , & schema) ?,
128+ expr : if let Some ( time_partition) = time_partition {
129+ physical_plan:: expressions:: col ( & time_partition, & schema) ?
130+ } else {
131+ physical_plan:: expressions:: col ( DEFAULT_TIMESTAMP_KEY , & schema) ?
132+ } ,
129133 options : SortOptions {
130134 descending : true ,
131135 nulls_first : true ,
132136 } ,
133137 } ;
134-
135138 let file_format = ParquetFormat :: default ( ) . with_enable_pruning ( Some ( true ) ) ;
139+
136140 // create the execution plan
137141 let plan = file_format
138142 . create_physical_plan (
@@ -151,7 +155,6 @@ async fn create_parquet_physical_plan(
151155 filters. as_ref ( ) ,
152156 )
153157 . await ?;
154-
155158 Ok ( plan)
156159}
157160
@@ -209,7 +212,6 @@ fn partitioned_files(
209212 let mut partitioned_files = Vec :: from_iter ( ( 0 ..target_partition) . map ( |_| Vec :: new ( ) ) ) ;
210213 let mut column_statistics = HashMap :: < String , Option < catalog:: column:: TypedStatistics > > :: new ( ) ;
211214 let mut count = 0 ;
212-
213215 for ( index, file) in manifest_files
214216 . into_iter ( )
215217 . enumerate ( )
@@ -221,7 +223,6 @@ fn partitioned_files(
221223 columns,
222224 ..
223225 } = file;
224-
225226 partitioned_files[ index] . push ( PartitionedFile :: new ( file_path, file. file_size ) ) ;
226227 columns. into_iter ( ) . for_each ( |col| {
227228 column_statistics
@@ -235,7 +236,6 @@ fn partitioned_files(
235236 } ) ;
236237 count += num_rows;
237238 }
238-
239239 let statistics = table_schema
240240 . fields ( )
241241 . iter ( )
@@ -304,7 +304,7 @@ impl TableProvider for StandardTableProvider {
304304 return Err ( DataFusionError :: Plan ( "potentially unbounded query on time range. Table scanning requires atleast one time bound" . to_string ( ) ) ) ;
305305 }
306306
307- if include_now ( filters, time_partition) {
307+ if include_now ( filters, time_partition. clone ( ) ) {
308308 if let Some ( records) =
309309 event:: STREAM_WRITERS . recordbatches_cloned ( & self . stream , & self . schema )
310310 {
@@ -333,6 +333,7 @@ impl TableProvider for StandardTableProvider {
333333 projection,
334334 filters,
335335 limit,
336+ time_partition. clone ( ) ,
336337 )
337338 . await ;
338339 }
@@ -375,6 +376,7 @@ impl TableProvider for StandardTableProvider {
375376 filters,
376377 limit,
377378 state,
379+ time_partition. clone ( ) ,
378380 )
379381 . await ?;
380382
@@ -400,6 +402,7 @@ impl TableProvider for StandardTableProvider {
400402 filters,
401403 limit,
402404 state,
405+ time_partition. clone ( ) ,
403406 )
404407 . await ?;
405408
@@ -437,11 +440,16 @@ async fn legacy_listing_table(
437440 projection : Option < & Vec < usize > > ,
438441 filters : & [ Expr ] ,
439442 limit : Option < usize > ,
443+ time_partition : Option < String > ,
440444) -> Result < Arc < dyn ExecutionPlan > , DataFusionError > {
441445 let remote_table = ListingTableBuilder :: new ( stream)
442446 . populate_via_listing ( glob_storage. clone ( ) , object_store, time_filters)
443447 . and_then ( |builder| async {
444- let table = builder. build ( schema. clone ( ) , |x| glob_storage. query_prefixes ( x) ) ?;
448+ let table = builder. build (
449+ schema. clone ( ) ,
450+ |x| glob_storage. query_prefixes ( x) ,
451+ time_partition,
452+ ) ?;
445453 let res = match table {
446454 Some ( table) => Some ( table. scan ( state, projection, filters, limit) . await ?) ,
447455 _ => None ,
@@ -459,6 +467,7 @@ fn final_plan(
459467 schema : Arc < Schema > ,
460468) -> Result < Arc < dyn ExecutionPlan > , DataFusionError > {
461469 let mut execution_plans = execution_plans. into_iter ( ) . flatten ( ) . collect_vec ( ) ;
470+
462471 let exec: Arc < dyn ExecutionPlan > = if execution_plans. is_empty ( ) {
463472 let schema = match projection {
464473 Some ( projection) => Arc :: new ( schema. project ( projection) ?) ,
@@ -470,7 +479,6 @@ fn final_plan(
470479 } else {
471480 Arc :: new ( UnionExec :: new ( execution_plans) )
472481 } ;
473-
474482 Ok ( exec)
475483}
476484
@@ -557,34 +565,33 @@ impl PartialTimeFilter {
557565 ) ) ) ) ) ,
558566 ) )
559567 }
560-
561- fn is_greater_than ( & self , other : & NaiveDateTime ) -> bool {
562- match self {
563- PartialTimeFilter :: Low ( Bound :: Excluded ( time) ) => time >= other,
564- PartialTimeFilter :: Low ( Bound :: Included ( time) )
565- | PartialTimeFilter :: High ( Bound :: Excluded ( time) )
566- | PartialTimeFilter :: High ( Bound :: Included ( time) ) => time > other,
567- PartialTimeFilter :: Eq ( time) => time > other,
568- _ => unimplemented ! ( ) ,
569- }
570- }
571568}
572569
573570fn is_overlapping_query (
574571 manifest_list : & [ ManifestItem ] ,
575572 time_filters : & [ PartialTimeFilter ] ,
576573) -> bool {
577574 // This is for backwards compatiblity. Older table format relies on listing.
578- // if the time is lower than upper bound of first file then we consider it overlapping
579- let Some ( first_entry_upper_bound ) =
580- manifest_list. iter ( ) . map ( |file| file. time_upper_bound ) . min ( )
575+ // if the start time is lower than lower bound of first file then we consider it overlapping
576+ let Some ( first_entry_lower_bound ) =
577+ manifest_list. iter ( ) . map ( |file| file. time_lower_bound ) . min ( )
581578 else {
582579 return true ;
583580 } ;
584581
585- !time_filters
586- . iter ( )
587- . all ( |filter| filter. is_greater_than ( & first_entry_upper_bound. naive_utc ( ) ) )
582+ for filter in time_filters {
583+ match filter {
584+ PartialTimeFilter :: Low ( Bound :: Excluded ( time) )
585+ | PartialTimeFilter :: Low ( Bound :: Included ( time) ) => {
586+ if time < & first_entry_lower_bound. naive_utc ( ) {
587+ return true ;
588+ }
589+ }
590+ _ => { }
591+ }
592+ }
593+
594+ false
588595}
589596
590597fn include_now ( filters : & [ Expr ] , time_partition : Option < String > ) -> bool {
@@ -862,7 +869,7 @@ mod tests {
862869 let res = is_overlapping_query (
863870 & manifest_items ( ) ,
864871 & [ PartialTimeFilter :: Low ( std:: ops:: Bound :: Included (
865- datetime_min ( 2023 , 12 , 15 ) . naive_utc ( ) ,
872+ datetime_min ( 2023 , 12 , 14 ) . naive_utc ( ) ,
866873 ) ) ] ,
867874 ) ;
868875
@@ -874,7 +881,7 @@ mod tests {
874881 let res = is_overlapping_query (
875882 & manifest_items ( ) ,
876883 & [ PartialTimeFilter :: Low ( std:: ops:: Bound :: Included (
877- datetime_min ( 2023 , 12 , 15 )
884+ datetime_min ( 2023 , 12 , 14 )
878885 . naive_utc ( )
879886 . add ( Duration :: hours ( 3 ) ) ,
880887 ) ) ] ,
0 commit comments