@@ -206,6 +206,7 @@ use datafusion_physical_expr_common::sort_expr::{
206206use datafusion_expr:: utils:: AggregateOrderSensitivity ;
207207use datafusion_physical_expr_common:: utils:: evaluate_expressions_to_arrays;
208208use itertools:: Itertools ;
209+ use order:: GroupCompletionMode ;
209210use topk:: hash_table:: is_supported_hash_key_type;
210211use topk:: heap:: is_supported_heap_type;
211212
@@ -895,6 +896,12 @@ pub struct AggregateExec {
895896 /// input order". When that is not possible, the constructor overwrites it
896897 /// with the unordered variant [`InputOrderMode::Linear`].
897898 input_order_mode : InputOrderMode ,
899+ /// Describes when the executor can determine that groups are complete.
900+ ///
901+ /// Input ordering describes a subset of the cases in which groups can be
902+ /// safely emitted before the input ends. Full group completion requires only
903+ /// that rows for each complete grouping tuple are contiguous.
904+ group_completion_mode : GroupCompletionMode ,
898905 cache : Arc < PlanProperties > ,
899906 /// During initialization, if the plan supports dynamic filtering (see [`AggrDynFilter`]),
900907 /// it is set to `Some(..)` regardless of whether it can be pushed down to a child node.
@@ -919,6 +926,7 @@ impl AggregateExec {
919926 required_input_ordering : self . required_input_ordering . clone ( ) ,
920927 metrics : ExecutionPlanMetricsSet :: new ( ) ,
921928 input_order_mode : self . input_order_mode . clone ( ) ,
929+ group_completion_mode : self . group_completion_mode . clone ( ) ,
922930 cache : Arc :: clone ( & self . cache ) ,
923931 mode : self . mode ,
924932 group_by : Arc :: clone ( & self . group_by ) ,
@@ -939,6 +947,7 @@ impl AggregateExec {
939947 required_input_ordering : self . required_input_ordering . clone ( ) ,
940948 metrics : ExecutionPlanMetricsSet :: new ( ) ,
941949 input_order_mode : self . input_order_mode . clone ( ) ,
950+ group_completion_mode : self . group_completion_mode . clone ( ) ,
942951 cache : Arc :: clone ( & self . cache ) ,
943952 mode : self . mode ,
944953 group_by : Arc :: clone ( & self . group_by ) ,
@@ -1072,6 +1081,8 @@ impl AggregateExec {
10721081 input_order_mode = InputOrderMode :: Linear ;
10731082 }
10741083
1084+ let group_completion_mode = GroupCompletionMode :: from ( & input_order_mode) ;
1085+
10751086 // construct a map from the input expression to the output expression of the Aggregation group by
10761087 let group_expr_mapping =
10771088 ProjectionMapping :: try_new ( group_by. expr . clone ( ) , & input. schema ( ) ) ?;
@@ -1102,6 +1113,7 @@ impl AggregateExec {
11021113 required_input_ordering,
11031114 limit_options : None ,
11041115 input_order_mode,
1116+ group_completion_mode,
11051117 cache : Arc :: new ( cache) ,
11061118 dynamic_filter : None ,
11071119 } ;
@@ -1284,47 +1296,51 @@ impl AggregateExec {
12841296 ) ;
12851297 }
12861298
1287- // Choose the execution path based on (aggregation mode, ordering).
1288- //
1289- // Note that `self.input_order_mode` represents both input ordering and output
1290- // order promise. See its comment for details.
1299+ // Choose the execution path based on aggregation mode and when groups
1300+ // are known to be complete.
12911301 use AggregateMode :: * ;
1292- use InputOrderMode :: * ;
1293- let stream = match ( self . mode , & self . input_order_mode ) {
1294- ( Partial , Linear ) => StreamType :: PartialHash (
1302+ let stream = match ( self . mode , & self . group_completion_mode ) {
1303+ ( Partial , GroupCompletionMode :: None ) => StreamType :: PartialHash (
12951304 PartialHashAggregateStream :: new ( self , context, partition) ?,
12961305 ) ,
1297- ( Partial , Sorted | PartiallySorted ( _) ) => {
1306+ ( Partial , GroupCompletionMode :: Partial ( _) | GroupCompletionMode :: Full ) => {
12981307 StreamType :: OrderedPartialAggregate ( OrderedPartialAggregateStream :: new (
12991308 self , context, partition,
13001309 ) ?)
13011310 }
1302- ( PartialReduce , Linear ) => StreamType :: PartialReduceHash (
1311+ ( PartialReduce , GroupCompletionMode :: None ) => StreamType :: PartialReduceHash (
13031312 PartialReduceHashAggregateStream :: new ( self , context, partition) ?,
13041313 ) ,
1305- ( PartialReduce , Sorted | PartiallySorted ( _) ) => {
1306- // See the comment above: the builder enforces `Linear` order for
1307- // `PartialReduce` mode.
1314+ (
1315+ PartialReduce ,
1316+ GroupCompletionMode :: Partial ( _) | GroupCompletionMode :: Full ,
1317+ ) => {
13081318 return internal_err ! (
1309- "PartialReduce aggregation must use InputOrderMode::Linear "
1319+ "PartialReduce aggregation must use GroupCompletionMode::None "
13101320 ) ;
13111321 }
1312- ( Final | FinalPartitioned , Linear ) => StreamType :: FinalHash (
1313- FinalHashAggregateStream :: new ( self , context, partition) ?,
1314- ) ,
1315- ( Final | FinalPartitioned , Sorted | PartiallySorted ( _) ) => {
1316- StreamType :: OrderedFinalAggregate ( OrderedFinalAggregateStream :: new (
1322+ ( Final | FinalPartitioned , GroupCompletionMode :: None ) => {
1323+ StreamType :: FinalHash ( FinalHashAggregateStream :: new (
13171324 self , context, partition,
13181325 ) ?)
13191326 }
1320- ( Single | SinglePartitioned , Linear ) => StreamType :: SingleHash (
1321- SingleHashAggregateStream :: new ( self , context, partition) ?,
1322- ) ,
1323- ( Single | SinglePartitioned , Sorted | PartiallySorted ( _) ) => {
1324- StreamType :: OrderedSingleAggregate ( OrderedSingleAggregateStream :: new (
1327+ (
1328+ Final | FinalPartitioned ,
1329+ GroupCompletionMode :: Partial ( _) | GroupCompletionMode :: Full ,
1330+ ) => StreamType :: OrderedFinalAggregate ( OrderedFinalAggregateStream :: new (
1331+ self , context, partition,
1332+ ) ?) ,
1333+ ( Single | SinglePartitioned , GroupCompletionMode :: None ) => {
1334+ StreamType :: SingleHash ( SingleHashAggregateStream :: new (
13251335 self , context, partition,
13261336 ) ?)
13271337 }
1338+ (
1339+ Single | SinglePartitioned ,
1340+ GroupCompletionMode :: Partial ( _) | GroupCompletionMode :: Full ,
1341+ ) => StreamType :: OrderedSingleAggregate ( OrderedSingleAggregateStream :: new (
1342+ self , context, partition,
1343+ ) ?) ,
13281344 } ;
13291345 Ok ( stream)
13301346 }
@@ -2352,6 +2368,8 @@ impl ExecutionPlan for AggregateExec {
23522368 required_input_ordering : _,
23532369 // Derived at construction from the input ordering and `group_by`.
23542370 input_order_mode : _,
2371+ // Derived at construction from `input_order_mode`.
2372+ group_completion_mode : _,
23552373 // Derived at construction by `Self::compute_properties`.
23562374 cache : _,
23572375 dynamic_filter,
@@ -5042,6 +5060,10 @@ mod tests {
50425060 aggregate. input_order_mode( ) ,
50435061 InputOrderMode :: PartiallySorted ( _)
50445062 ) ) ;
5063+ assert_eq ! (
5064+ aggregate. group_completion_mode,
5065+ GroupCompletionMode :: Partial ( vec![ 0 ] )
5066+ ) ;
50455067
50465068 let task_ctx = new_migrated_hash_ctx ( 2 ) ;
50475069 let stream = aggregate. execute_typed ( 0 , & task_ctx) ?;
@@ -5491,6 +5513,7 @@ mod tests {
54915513 ) ?;
54925514
54935515 assert_eq ! ( aggregate. input_order_mode( ) , & InputOrderMode :: Linear ) ;
5516+ assert_eq ! ( aggregate. group_completion_mode, GroupCompletionMode :: None ) ;
54945517 // This captures the behavior before #24438. When the source can declare
54955518 // `(key, time_bin)` group-contiguous, the corresponding case can use
54965519 // `EmissionType::Incremental`.
@@ -5651,6 +5674,10 @@ mod tests {
56515674 Arc :: clone ( & schema) ,
56525675 ) ?;
56535676 assert_eq ! ( final_aggregate. input_order_mode( ) , & InputOrderMode :: Sorted ) ;
5677+ assert_eq ! (
5678+ final_aggregate. group_completion_mode,
5679+ GroupCompletionMode :: Full
5680+ ) ;
56545681
56555682 let task_ctx = new_migrated_hash_ctx ( 2 ) ;
56565683 let stream = final_aggregate. execute_typed ( 0 , & task_ctx) ?;
0 commit comments