@@ -354,12 +354,18 @@ pub struct HashJoinExec {
354354 /// Cache holding plan properties like equivalences, output partitioning etc.
355355 cache : PlanProperties ,
356356 /// Dynamic filter for pushing down to the probe side
357- /// Set when dynamic filter pushdown is detected in handle_child_pushdown_result
358- dynamic_filter : Option < Arc < DynamicFilterPhysicalExpr > > ,
359- /// Shared bounds accumulator for coordinating dynamic filter updates across partitions
360- /// Only created when dynamic filter pushdown is enabled.
361- /// Lazily initialized at execution time to use actual runtime partition counts
362- bounds_accumulator : Option < OnceLock < Arc < SharedBoundsAccumulator > > > ,
357+ /// Set when dynamic filter pushdown is detected in handle_child_pushdown_result.
358+ /// HashJoinExec also needs to keep a shared bounds accumulator for coordinating updates.
359+ dynamic_filter : Option < HashJoinExecDynamicFilter > ,
360+ }
361+
362+ #[ derive( Clone ) ]
363+ struct HashJoinExecDynamicFilter {
364+ /// Dynamic filter that we'll update with the results of the build side once that is done.
365+ filter : Arc < DynamicFilterPhysicalExpr > ,
366+ /// Bounds accumulator to keep track of the min/max bounds on the join keys for each partition.
367+ /// It is lazily initialized during execution to make sure we use the actual execution time partition counts.
368+ bounds_accumulator : OnceLock < Arc < SharedBoundsAccumulator > > ,
363369}
364370
365371impl fmt:: Debug for HashJoinExec {
@@ -453,7 +459,6 @@ impl HashJoinExec {
453459 null_equality,
454460 cache,
455461 dynamic_filter : None ,
456- bounds_accumulator : None ,
457462 } )
458463 }
459464
@@ -852,7 +857,6 @@ impl ExecutionPlan for HashJoinExec {
852857 ) ?,
853858 // Keep the dynamic filter, bounds accumulator will be reset
854859 dynamic_filter : self . dynamic_filter . clone ( ) ,
855- bounds_accumulator : None ,
856860 } ) )
857861 }
858862
@@ -875,7 +879,6 @@ impl ExecutionPlan for HashJoinExec {
875879 cache : self . cache . clone ( ) ,
876880 // Reset dynamic filter and bounds accumulator to initial state
877881 dynamic_filter : None ,
878- bounds_accumulator : None ,
879882 } ) )
880883 }
881884
@@ -957,32 +960,28 @@ impl ExecutionPlan for HashJoinExec {
957960 let batch_size = context. session_config ( ) . batch_size ( ) ;
958961
959962 // Initialize bounds_accumulator lazily with runtime partition counts (only if enabled)
960- let bounds_accumulator = if enable_dynamic_filter_pushdown
961- && self . dynamic_filter . is_some ( )
962- {
963- if let Some ( ref bounds_accumulator_oncelock) = self . bounds_accumulator {
964- let dynamic_filter = Arc :: clone ( self . dynamic_filter . as_ref ( ) . unwrap ( ) ) ;
965- let on_right = self
966- . on
967- . iter ( )
968- . map ( |( _, right_expr) | Arc :: clone ( right_expr) )
969- . collect :: < Vec < _ > > ( ) ;
970-
971- Some ( Arc :: clone ( bounds_accumulator_oncelock. get_or_init ( || {
972- Arc :: new ( SharedBoundsAccumulator :: new_from_partition_mode (
973- self . mode ,
974- self . left . as_ref ( ) ,
975- self . right . as_ref ( ) ,
976- dynamic_filter,
977- on_right,
978- ) )
979- } ) ) )
980- } else {
981- None
982- }
983- } else {
984- None
985- } ;
963+ let bounds_accumulator = enable_dynamic_filter_pushdown
964+ . then ( || {
965+ self . dynamic_filter . as_ref ( ) . map ( |df| {
966+ let filter = Arc :: clone ( & df. filter ) ;
967+ let on_right = self
968+ . on
969+ . iter ( )
970+ . map ( |( _, right_expr) | Arc :: clone ( right_expr) )
971+ . collect :: < Vec < _ > > ( ) ;
972+ Some ( Arc :: clone ( df. bounds_accumulator . get_or_init ( || {
973+ Arc :: new ( SharedBoundsAccumulator :: new_from_partition_mode (
974+ self . mode ,
975+ self . left . as_ref ( ) ,
976+ self . right . as_ref ( ) ,
977+ filter,
978+ on_right,
979+ ) )
980+ } ) ) )
981+ } )
982+ } )
983+ . flatten ( )
984+ . flatten ( ) ;
986985
987986 // we have the batches and the hash map with their keys. We can how create a stream
988987 // over the right that uses this information to issue new batches.
@@ -1177,8 +1176,10 @@ impl ExecutionPlan for HashJoinExec {
11771176 column_indices : self . column_indices . clone ( ) ,
11781177 null_equality : self . null_equality ,
11791178 cache : self . cache . clone ( ) ,
1180- dynamic_filter : Some ( dynamic_filter) ,
1181- bounds_accumulator : Some ( OnceLock :: new ( ) ) ,
1179+ dynamic_filter : Some ( HashJoinExecDynamicFilter {
1180+ filter : dynamic_filter,
1181+ bounds_accumulator : OnceLock :: new ( ) ,
1182+ } ) ,
11821183 } ) ;
11831184 result = result. with_updated_node ( new_node as Arc < dyn ExecutionPlan > ) ;
11841185 }
0 commit comments