Skip to content

Commit 037c8cf

Browse files
committed
fix: ignore constant aggregate completion boundaries
1 parent 46bbf0d commit 037c8cf

9 files changed

Lines changed: 138 additions & 22 deletions

File tree

‎datafusion/core/tests/dataframe/mod.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ async fn test_aggregate_with_pk2() -> Result<()> {
910910
assert_snapshot!(
911911
physical_plan_to_string(&df).await,
912912
@r"
913-
AggregateExec: mode=Single, gby=[id@0 as id, name@1 as name], aggr=[], ordering_mode=Sorted
913+
AggregateExec: mode=Single, gby=[id@0 as id, name@1 as name], aggr=[]
914914
FilterExec: id@0 = 1 AND name@1 = a
915915
DataSourceExec: partitions=1, partition_sizes=[1]
916916
"
@@ -958,7 +958,7 @@ async fn test_aggregate_with_pk3() -> Result<()> {
958958
assert_snapshot!(
959959
physical_plan_to_string(&df).await,
960960
@r"
961-
AggregateExec: mode=Single, gby=[id@0 as id, name@1 as name], aggr=[], ordering_mode=PartiallySorted([0])
961+
AggregateExec: mode=Single, gby=[id@0 as id, name@1 as name], aggr=[]
962962
FilterExec: id@0 = 1
963963
DataSourceExec: partitions=1, partition_sizes=[1]
964964
"
@@ -1008,7 +1008,7 @@ async fn test_aggregate_with_pk4() -> Result<()> {
10081008
assert_snapshot!(
10091009
physical_plan_to_string(&df).await,
10101010
@r"
1011-
AggregateExec: mode=Single, gby=[id@0 as id], aggr=[], ordering_mode=Sorted
1011+
AggregateExec: mode=Single, gby=[id@0 as id], aggr=[]
10121012
FilterExec: id@0 = 1
10131013
DataSourceExec: partitions=1, partition_sizes=[1]
10141014
"

‎datafusion/core/tests/physical_optimizer/filter_pushdown.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -740,12 +740,12 @@ fn test_pushdown_through_aggregates_on_grouping_columns() {
740740
OptimizationTest:
741741
input:
742742
- FilterExec: b@1 = bar
743-
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt], ordering_mode=PartiallySorted([0])
743+
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt]
744744
- FilterExec: a@0 = foo
745745
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true
746746
output:
747747
Ok:
748-
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt], ordering_mode=Sorted
748+
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt]
749749
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true, predicate=a@0 = foo AND b@1 = bar
750750
"
751751
);
@@ -804,7 +804,7 @@ fn test_pushdown_through_aggregates_preserves_parent_filter_order() {
804804
output:
805805
Ok:
806806
- FilterExec: cnt@2 = 1
807-
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt], ordering_mode=PartiallySorted([1])
807+
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt]
808808
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true, predicate=b@1 = bar
809809
"
810810
);
@@ -2099,7 +2099,7 @@ fn test_pushdown_filter_on_non_first_grouping_column() {
20992099
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true
21002100
output:
21012101
Ok:
2102-
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt], ordering_mode=PartiallySorted([1])
2102+
- AggregateExec: mode=Final, gby=[a@0 as a, b@1 as b], aggr=[cnt]
21032103
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true, predicate=b@1 = bar
21042104
"
21052105
);
@@ -2435,7 +2435,7 @@ fn test_pushdown_through_aggregate_with_reordered_input_columns() {
24352435
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true
24362436
output:
24372437
Ok:
2438-
- AggregateExec: mode=Final, gby=[a@1 as a, b@2 as b], aggr=[cnt], ordering_mode=PartiallySorted([1])
2438+
- AggregateExec: mode=Final, gby=[a@1 as a, b@2 as b], aggr=[cnt]
24392439
- ProjectionExec: expr=[c@2 as c, a@0 as a, b@1 as b]
24402440
- DataSourceExec: file_groups={1 group: [[test.parquet]]}, projection=[a, b, c], file_type=test, pushdown_supported=true, predicate=b@1 = bar
24412441
"

‎datafusion/core/tests/physical_optimizer/sanity_checker.rs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,27 @@ async fn test_aggregate() -> Result<()> {
282282
Ok(())
283283
}
284284

285+
#[tokio::test]
286+
async fn test_constant_grouping_expr_on_unbounded_input() -> Result<()> {
287+
let case = QueryCase {
288+
sql: "SELECT c1, c2, MIN(c4) FROM test WHERE c2 = 1 GROUP BY c1, c2".to_string(),
289+
cases: vec![
290+
Arc::new(UnaryTestCase {
291+
source_type: SourceType::Bounded,
292+
expect_fail: false,
293+
}),
294+
Arc::new(UnaryTestCase {
295+
source_type: SourceType::Unbounded,
296+
expect_fail: true,
297+
}),
298+
],
299+
error_operator: "operator: AggregateExec".to_string(),
300+
};
301+
302+
case.run().await?;
303+
Ok(())
304+
}
305+
285306
#[tokio::test]
286307
async fn test_window_agg_hash_partition() -> Result<()> {
287308
let test1 = UnaryTestCase {

‎datafusion/physical-plan/src/aggregates/mod.rs‎

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,8 @@ impl AggregateExec {
10281028
let required_input_ordering =
10291029
LexRequirement::new(new_requirements).map(OrderingRequirements::new_soft);
10301030

1031+
// Constant expressions never change, so they cannot mark a completed group.
1032+
// Exclude them from both the ordering indices and the group expression count.
10311033
// If our aggregation has grouping sets then our base grouping exprs will
10321034
// be expanded based on the flags in `group_by.groups` where for each
10331035
// group we swap the grouping expr for `null` if the flag is `true`
@@ -1036,9 +1038,18 @@ impl AggregateExec {
10361038
let indices: Vec<usize> = indices
10371039
.into_iter()
10381040
.filter(|idx| group_by.groups.iter().all(|group| !group[*idx]))
1041+
.filter(|idx| {
1042+
input_eq_properties
1043+
.is_expr_constant(&groupby_exprs[*idx])
1044+
.is_none()
1045+
})
10391046
.collect();
10401047

1041-
let mut input_order_mode = if indices.len() == groupby_exprs.len()
1048+
let num_non_constant_groupby_exprs = groupby_exprs
1049+
.iter()
1050+
.filter(|expr| input_eq_properties.is_expr_constant(expr).is_none())
1051+
.count();
1052+
let mut input_order_mode = if indices.len() == num_non_constant_groupby_exprs
10421053
&& !indices.is_empty()
10431054
&& group_by.groups.len() == 1
10441055
{
@@ -3249,7 +3260,7 @@ mod tests {
32493260

32503261
use arrow::array::{
32513262
BooleanArray, DictionaryArray, Float32Array, Float64Array, Int32Array,
3252-
Int64Array, NullArray, StructArray, UInt32Array, UInt64Array,
3263+
Int64Array, NullArray, StringArray, StructArray, UInt32Array, UInt64Array,
32533264
};
32543265
use arrow::compute::{SortOptions, concat_batches};
32553266
use arrow::datatypes::Int32Type;
@@ -3260,7 +3271,7 @@ mod tests {
32603271
use datafusion_execution::runtime_env::RuntimeEnvBuilder;
32613272
use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
32623273
use datafusion_expr::{
3263-
Accumulator, AggregateUDF, AggregateUDFImpl, EmitTo, GroupsAccumulator,
3274+
Accumulator, AggregateUDF, AggregateUDFImpl, EmitTo, GroupsAccumulator, Operator,
32643275
Signature, Volatility,
32653276
};
32663277
use datafusion_functions_aggregate::approx_percentile_cont::approx_percentile_cont_udaf;
@@ -3274,7 +3285,7 @@ mod tests {
32743285
use datafusion_physical_expr::Partitioning;
32753286
use datafusion_physical_expr::PhysicalSortExpr;
32763287
use datafusion_physical_expr::aggregate::AggregateExprBuilder;
3277-
use datafusion_physical_expr::expressions::{Literal, NotExpr};
3288+
use datafusion_physical_expr::expressions::{Literal, NotExpr, binary};
32783289

32793290
use crate::projection::ProjectionExec;
32803291
use crate::repartition::RepartitionExec;
@@ -4595,6 +4606,87 @@ mod tests {
45954606
Ok(())
45964607
}
45974608

4609+
#[tokio::test]
4610+
async fn constant_grouping_expr_is_not_a_completion_boundary() -> Result<()> {
4611+
let schema = Arc::new(Schema::new(vec![
4612+
Field::new("key", DataType::Int32, true),
4613+
Field::new("market", DataType::Utf8, true),
4614+
Field::new("value", DataType::Int64, false),
4615+
]));
4616+
let batch = RecordBatch::try_new(
4617+
Arc::clone(&schema),
4618+
vec![
4619+
Arc::new(Int32Array::from(vec![None, Some(10), Some(10)])),
4620+
Arc::new(StringArray::from(vec![Some("US"), Some("US"), Some("US")])),
4621+
Arc::new(Int64Array::from(vec![3, 1, 2])),
4622+
],
4623+
)?;
4624+
4625+
let build_aggregate = |input: Arc<dyn ExecutionPlan>| -> Result<AggregateExec> {
4626+
let predicate =
4627+
binary(col("market", &schema)?, Operator::Eq, lit("US"), &schema)?;
4628+
let input = Arc::new(FilterExecBuilder::new(predicate, input).build()?);
4629+
AggregateExec::try_new(
4630+
AggregateMode::Single,
4631+
PhysicalGroupBy::new_single(vec![
4632+
(col("key", &schema)?, "key".to_string()),
4633+
(col("market", &schema)?, "market".to_string()),
4634+
]),
4635+
vec![Arc::new(
4636+
AggregateExprBuilder::new(count_udaf(), vec![col("value", &schema)?])
4637+
.schema(Arc::clone(&schema))
4638+
.alias("COUNT(value)")
4639+
.build()?,
4640+
)],
4641+
vec![None],
4642+
input,
4643+
Arc::clone(&schema),
4644+
)
4645+
};
4646+
4647+
let unordered_input = TestMemoryExec::try_new_exec(
4648+
&[vec![batch.clone()]],
4649+
Arc::clone(&schema),
4650+
None,
4651+
)?;
4652+
let aggregate = build_aggregate(unordered_input)?;
4653+
assert_eq!(aggregate.input_order_mode(), &InputOrderMode::Linear);
4654+
assert_eq!(
4655+
aggregate.schema().as_ref(),
4656+
&Schema::new(vec![
4657+
Field::new("key", DataType::Int32, true),
4658+
Field::new("market", DataType::Utf8, true),
4659+
Field::new("COUNT(value)", DataType::Int64, false),
4660+
])
4661+
);
4662+
4663+
let output =
4664+
collect(aggregate.execute(0, Arc::new(TaskContext::default()))?).await?;
4665+
assert_eq!(output.iter().map(RecordBatch::num_rows).sum::<usize>(), 2);
4666+
assert_snapshot!(batches_to_sort_string(&output), @r"
4667+
+-----+--------+--------------+
4668+
| key | market | COUNT(value) |
4669+
+-----+--------+--------------+
4670+
| | US | 1 |
4671+
| 10 | US | 2 |
4672+
+-----+--------+--------------+
4673+
");
4674+
4675+
let ordering = LexOrdering::new([PhysicalSortExpr::new_default(Arc::new(
4676+
Column::new("key", 0),
4677+
))])
4678+
.unwrap();
4679+
let ordered_input =
4680+
TestMemoryExec::try_new(&[vec![batch]], Arc::clone(&schema), None)?
4681+
.try_with_sort_information(vec![ordering])?;
4682+
let ordered_input =
4683+
Arc::new(TestMemoryExec::update_cache(&Arc::new(ordered_input)));
4684+
let aggregate = build_aggregate(ordered_input)?;
4685+
assert_eq!(aggregate.input_order_mode(), &InputOrderMode::Sorted);
4686+
4687+
Ok(())
4688+
}
4689+
45984690
fn partial_reduce_test_aggregate() -> Result<AggregateExec> {
45994691
partial_reduce_test_aggregate_with_batches(1)
46004692
}
@@ -6472,7 +6564,10 @@ mod tests {
64726564
");
64736565
}
64746566
}
6475-
Err(e) => assert!(matches!(e, DataFusionError::ResourcesExhausted(_))),
6567+
Err(e) => assert!(
6568+
matches!(e.find_root(), DataFusionError::ResourcesExhausted(_)),
6569+
"unexpected error: {e}"
6570+
),
64766571
}
64776572

64786573
Ok(())

‎datafusion/sqllogictest/test_files/aggregate.slt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8722,9 +8722,9 @@ logical_plan
87228722
15)------------EmptyRelation: rows=1
87238723
physical_plan
87248724
01)ProjectionExec: expr=[last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST]@1 as last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST], sum(DISTINCT Int64(1))@2 as sum(DISTINCT Int64(1))]
8725-
02)--AggregateExec: mode=FinalPartitioned, gby=[id@0 as id], aggr=[last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST], sum(DISTINCT Int64(1))], ordering_mode=Sorted
8725+
02)--AggregateExec: mode=FinalPartitioned, gby=[id@0 as id], aggr=[last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST], sum(DISTINCT Int64(1))]
87268726
03)----RepartitionExec: partitioning=Hash([id@0], 4), input_partitions=5
8727-
04)------AggregateExec: mode=Partial, gby=[id@0 as id], aggr=[last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST], sum(DISTINCT Int64(1))], ordering_mode=Sorted
8727+
04)------AggregateExec: mode=Partial, gby=[id@0 as id], aggr=[last_value(a.foo) ORDER BY [a.foo ASC NULLS LAST], sum(DISTINCT Int64(1))]
87288728
05)--------UnionExec
87298729
06)----------ProjectionExec: expr=[1 as id, 2 as foo]
87308730
07)------------PlaceholderRowExec

‎datafusion/sqllogictest/test_files/array_agg.slt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,9 +530,9 @@ logical_plan
530530
15)------------EmptyRelation: rows=1
531531
physical_plan
532532
01)ProjectionExec: expr=[array_length(array_agg(DISTINCT a.foo)@1) as array_length(array_agg(DISTINCT a.foo)), sum(DISTINCT Int64(1))@2 as sum(DISTINCT Int64(1))]
533-
02)--AggregateExec: mode=FinalPartitioned, gby=[id@0 as id], aggr=[array_agg(DISTINCT a.foo), sum(DISTINCT Int64(1))], ordering_mode=Sorted
533+
02)--AggregateExec: mode=FinalPartitioned, gby=[id@0 as id], aggr=[array_agg(DISTINCT a.foo), sum(DISTINCT Int64(1))]
534534
03)----RepartitionExec: partitioning=Hash([id@0], 4), input_partitions=5
535-
04)------AggregateExec: mode=Partial, gby=[id@0 as id], aggr=[array_agg(DISTINCT a.foo), sum(DISTINCT Int64(1))], ordering_mode=Sorted
535+
04)------AggregateExec: mode=Partial, gby=[id@0 as id], aggr=[array_agg(DISTINCT a.foo), sum(DISTINCT Int64(1))]
536536
05)--------UnionExec
537537
06)----------ProjectionExec: expr=[1 as id, CAST(2 AS Int64) as foo]
538538
07)------------PlaceholderRowExec

‎datafusion/sqllogictest/test_files/push_down_filter_regression.slt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,9 +497,9 @@ query TT
497497
EXPLAIN SELECT a, count(b) FROM agg_filter_pushdown WHERE a = 'x' GROUP BY a;
498498
----
499499
physical_plan
500-
01)AggregateExec: mode=FinalPartitioned, gby=[a@0 as a], aggr=[count(agg_filter_pushdown.b)], ordering_mode=Sorted
500+
01)AggregateExec: mode=FinalPartitioned, gby=[a@0 as a], aggr=[count(agg_filter_pushdown.b)]
501501
02)--RepartitionExec: partitioning=Hash([a@0], 4), input_partitions=4
502-
03)----AggregateExec: mode=Partial, gby=[a@0 as a], aggr=[count(agg_filter_pushdown.b)], ordering_mode=Sorted
502+
03)----AggregateExec: mode=Partial, gby=[a@0 as a], aggr=[count(agg_filter_pushdown.b)]
503503
04)------RepartitionExec: partitioning=RoundRobinBatch(4), input_partitions=1
504504
05)--------DataSourceExec: file_groups={1 group: [[WORKSPACE_ROOT/datafusion/sqllogictest/test_files/scratch/push_down_filter_regression/agg_filter_pushdown.parquet]]}, projection=[a, b], file_type=parquet, predicate=a@0 = x, pruning_predicate=a_null_count@2 != row_count@3 AND a_min@0 <= x AND x <= a_max@1, required_guarantees=[a in (x)]
505505

‎datafusion/sqllogictest/test_files/union.slt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ logical_plan
694694
physical_plan
695695
01)UnionExec
696696
02)--ProjectionExec: expr=[Int64(1)@0 as a]
697-
03)----AggregateExec: mode=SinglePartitioned, gby=[1 as Int64(1)], aggr=[], ordering_mode=Sorted
697+
03)----AggregateExec: mode=SinglePartitioned, gby=[1 as Int64(1)], aggr=[]
698698
04)------PlaceholderRowExec
699699
05)--ProjectionExec: expr=[2 as a]
700700
06)----PlaceholderRowExec
@@ -722,7 +722,7 @@ logical_plan
722722
physical_plan
723723
01)UnionExec
724724
02)--ProjectionExec: expr=[count(Int64(1))@1 as count, CAST(n@0 AS Int64) as n]
725-
03)----AggregateExec: mode=SinglePartitioned, gby=[n@0 as n], aggr=[count(Int64(1))], ordering_mode=Sorted
725+
03)----AggregateExec: mode=SinglePartitioned, gby=[n@0 as n], aggr=[count(Int64(1))]
726726
04)------ProjectionExec: expr=[5 as n]
727727
05)--------PlaceholderRowExec
728728
06)--ProjectionExec: expr=[1 as count, max(Int64(10))@0 as n]

‎datafusion/sqllogictest/test_files/window.slt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ physical_plan
276276
03)----SortExec: expr=[b@0 ASC NULLS LAST], preserve_partitioning=[true]
277277
04)------AggregateExec: mode=FinalPartitioned, gby=[b@0 as b], aggr=[max(d.a)]
278278
05)--------RepartitionExec: partitioning=Hash([b@0], 4), input_partitions=4
279-
06)----------AggregateExec: mode=Partial, gby=[b@1 as b], aggr=[max(d.a)], ordering_mode=Sorted
279+
06)----------AggregateExec: mode=Partial, gby=[b@1 as b], aggr=[max(d.a)]
280280
07)------------UnionExec
281281
08)--------------ProjectionExec: expr=[1 as a, aa as b]
282282
09)----------------PlaceholderRowExec

0 commit comments

Comments
 (0)