kosiew commented on code in PR #23813:
URL: https://github.com/apache/datafusion/pull/23813#discussion_r3637182849


##########
datafusion/expr/src/utils.rs:
##########
@@ -652,6 +652,106 @@ pub fn find_aggregate_exprs<'a>(exprs: impl 
IntoIterator<Item = &'a Expr>) -> Ve
     })
 }
 
+/// Returns an error if any of `exprs` nests aggregate or window function calls
+/// in a way that has no physical equivalent: an aggregate call may not contain
+/// another aggregate call (`sum(sum(x))`) or a window call
+/// (`sum(sum(x) OVER ())`), and a window call may not contain another window
+/// call (`sum(sum(x) OVER ()) OVER ()`). The reverse nesting, an aggregate 
used
+/// as the argument of a window call (`sum(sum(x)) OVER ()`), is legal: there 
the
+/// aggregate is evaluated by the `Aggregate` node and the window function is
+/// evaluated on top of its result.
+///
+/// Such expressions are not valid SQL either, so they are rejected while the
+/// logical plan is built rather than failing later with an error that does not
+/// point back at the original SQL.
+///
+/// Callers do not need to invoke this directly: [`Aggregate::try_new`] and

Review Comment:
   Nice improvement. One small thing: I think "every way of building those 
nodes" is a bit stronger than what the code guarantees today. 
`Window::try_new_with_schema` and `Aggregate::try_new_with_schema` don't 
perform this validation, and `Window` can also be constructed directly from its 
public fields.
   
   The SQL and `LogicalPlanBuilder` paths introduced in this PR are protected, 
so this isn't blocking. I'd suggest narrowing the wording here (and in the PR 
description) to those validated construction paths. If the intent is to enforce 
this invariant across all public construction paths, it would be good to either 
add the checks there as well or document that those constructors expect already 
validated expressions.



##########
datafusion/sqllogictest/test_files/aggregate.slt:
##########
@@ -9607,3 +9607,69 @@ SET datafusion.execution.target_partitions = 4;
 
 statement ok
 DROP TABLE hits_raw;
+
+# Nested aggregate function calls are rejected during planning
+# issue: https://github.com/apache/datafusion/issues/23812
+statement ok
+CREATE TABLE nested_agg_t AS VALUES (1, 10.0), (1, 20.0), (2, 30.0);
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested: 'sum\(nested_agg_t\.column2\)' is nested inside 
'sum\(sum\(nested_agg_t\.column2\)\)'
+SELECT column1, sum(sum(column2)) FROM nested_agg_t GROUP BY column1;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested: 'count\(nested_agg_t\.column2\)' is nested inside 
'sum\(count\(nested_agg_t\.column2\)\)'
+SELECT column1, sum(count(column2)) FROM nested_agg_t GROUP BY column1;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested
+SELECT sum(sum(column2)) FROM nested_agg_t;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested
+SELECT column1 FROM nested_agg_t GROUP BY column1 HAVING sum(sum(column2)) > 0;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested
+SELECT column1, sum(column2 + sum(column2)) FROM nested_agg_t GROUP BY column1;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested
+SELECT sum(column2) FILTER (WHERE sum(column2) > 0) FROM nested_agg_t;
+
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot be nested
+SELECT array_agg(column2 ORDER BY sum(column2)) FROM nested_agg_t;
+
+# A window function nested inside an aggregate is rejected as well
+statement error DataFusion error: Error during planning: Aggregate function 
calls cannot contain window function calls
+SELECT sum(sum(column2) OVER ()) FROM nested_agg_t;
+
+# ... as are nested window functions
+statement error DataFusion error: Error during planning: Window function calls 
cannot be nested
+SELECT sum(sum(column2) OVER ()) OVER () FROM nested_agg_t;
+
+statement error DataFusion error: Error during planning: Window function calls 
cannot be nested

Review Comment:
   The coverage here looks great. One additional case that might be worth 
adding is a nested window function in `PARTITION BY`, for example:
   
   ```sql
   row_number() OVER (PARTITION BY row_number() OVER ())
   ```
   
   `Expr::apply_children` traverses `PARTITION BY`, but the current SQL tests 
exercise window arguments and `ORDER BY`. Adding a regression test for 
`PARTITION BY` would help protect that traversal path too.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to