@@ -63,7 +63,8 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
63
63
typeCoercionRules ++
64
64
extendedRules : _* ),
65
65
Batch (" Check Analysis" , Once ,
66
- CheckResolution ),
66
+ CheckResolution ,
67
+ CheckAggregation ),
67
68
Batch (" AnalysisOperators" , fixedPoint,
68
69
EliminateAnalysisOperators )
69
70
)
@@ -88,6 +89,32 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
88
89
}
89
90
}
90
91
92
+ /**
93
+ * Checks for non-aggregated attributes with aggregation
94
+ */
95
+ object CheckAggregation extends Rule [LogicalPlan ] {
96
+ def apply (plan : LogicalPlan ): LogicalPlan = {
97
+ plan.transform {
98
+ case aggregatePlan @ Aggregate (groupingExprs, aggregateExprs, child) =>
99
+ def isValidAggregateExpression (expr : Expression ): Boolean = expr match {
100
+ case _ : AggregateExpression => true
101
+ case e : Attribute if groupingExprs.contains(e) => true
102
+ case e if groupingExprs.contains(e) => true
103
+ case e if e.references.isEmpty => true
104
+ case e => e.children.forall(isValidAggregateExpression)
105
+ }
106
+
107
+ aggregateExprs.foreach { e =>
108
+ if (! isValidAggregateExpression(e)) {
109
+ throw new TreeNodeException (plan, s " Expression not in GROUP BY: $e" )
110
+ }
111
+ }
112
+
113
+ aggregatePlan
114
+ }
115
+ }
116
+ }
117
+
91
118
/**
92
119
* Replaces [[UnresolvedRelation ]]s with concrete relations from the catalog.
93
120
*/
@@ -204,18 +231,17 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
204
231
*/
205
232
object UnresolvedHavingClauseAttributes extends Rule [LogicalPlan ] {
206
233
def apply (plan : LogicalPlan ): LogicalPlan = plan transformUp {
207
- case filter @ Filter (havingCondition, aggregate @ Aggregate (_, originalAggExprs, _))
234
+ case filter @ Filter (havingCondition, aggregate @ Aggregate (_, originalAggExprs, _))
208
235
if aggregate.resolved && containsAggregate(havingCondition) => {
209
236
val evaluatedCondition = Alias (havingCondition, " havingCondition" )()
210
237
val aggExprsWithHaving = evaluatedCondition +: originalAggExprs
211
-
238
+
212
239
Project (aggregate.output,
213
240
Filter (evaluatedCondition.toAttribute,
214
241
aggregate.copy(aggregateExpressions = aggExprsWithHaving)))
215
242
}
216
-
217
243
}
218
-
244
+
219
245
protected def containsAggregate (condition : Expression ): Boolean =
220
246
condition
221
247
.collect { case ae : AggregateExpression => ae }
0 commit comments