@@ -63,7 +63,8 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
6363 typeCoercionRules ++
6464 extendedRules : _* ),
6565 Batch (" Check Analysis" , Once ,
66- CheckResolution ),
66+ CheckResolution ,
67+ CheckAggregation ),
6768 Batch (" AnalysisOperators" , fixedPoint,
6869 EliminateAnalysisOperators )
6970 )
@@ -88,6 +89,32 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
8889 }
8990 }
9091
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+
91118 /**
92119 * Replaces [[UnresolvedRelation ]]s with concrete relations from the catalog.
93120 */
@@ -204,18 +231,17 @@ class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Bool
204231 */
205232 object UnresolvedHavingClauseAttributes extends Rule [LogicalPlan ] {
206233 def apply (plan : LogicalPlan ): LogicalPlan = plan transformUp {
207- case filter @ Filter (havingCondition, aggregate @ Aggregate (_, originalAggExprs, _))
234+ case filter @ Filter (havingCondition, aggregate @ Aggregate (_, originalAggExprs, _))
208235 if aggregate.resolved && containsAggregate(havingCondition) => {
209236 val evaluatedCondition = Alias (havingCondition, " havingCondition" )()
210237 val aggExprsWithHaving = evaluatedCondition +: originalAggExprs
211-
238+
212239 Project (aggregate.output,
213240 Filter (evaluatedCondition.toAttribute,
214241 aggregate.copy(aggregateExpressions = aggExprsWithHaving)))
215242 }
216-
217243 }
218-
244+
219245 protected def containsAggregate (condition : Expression ): Boolean =
220246 condition
221247 .collect { case ae : AggregateExpression => ae }
0 commit comments