@@ -595,8 +595,50 @@ class Analyzer(
595595 }
596596 }
597597
598+ private def tryResolveHavingCondition (
599+ a : AggregateWithHaving , havingCond : Expression , agg : LogicalPlan ): LogicalPlan = {
600+ val aggForResolver = agg match {
601+ case a : Aggregate =>
602+ // For CUBE/ROLLUP expressions, since they don't have their own logical plan, we need
603+ // to delete them from groupingExpressions for condition resolving.
604+ a.copy(groupingExpressions = Seq .empty)
605+ case g : GroupingSets =>
606+ Aggregate (g.groupByExprs, g.aggregations, g.child)
607+ }
608+ // Try resolving the condition of the filter as though it is in the aggregate clause
609+ val (aggregateExpressions, transformedAggregateFilter) =
610+ ResolveAggregateFunctions .resolveFilterCondInAggregate(
611+ havingCond, aggForResolver, true )
612+
613+ // Push the aggregate expressions into the aggregate (if any).
614+ if (aggregateExpressions.nonEmpty) {
615+ val newChild = agg match {
616+ case a : Aggregate =>
617+ a.copy(aggregateExpressions = a.aggregateExpressions ++ aggregateExpressions)
618+ case g : GroupingSets =>
619+ g.copy(aggregations = g.aggregations ++ aggregateExpressions)
620+ }
621+ Project (agg.output,
622+ Filter (transformedAggregateFilter.get, newChild))
623+ } else {
624+ a
625+ }
626+ }
627+
598628 // This require transformUp to replace grouping()/grouping_id() in resolved Filter/Sort
599- def apply (plan : LogicalPlan ): LogicalPlan = plan resolveOperatorsUp {
629+ def apply (plan : LogicalPlan ): LogicalPlan = plan resolveOperatorsDown {
630+ case a @ AggregateWithHaving (
631+ havingCondition, agg @ Aggregate (Seq (c @ Cube (groupByExprs)), aggregateExpressions, _))
632+ if (groupByExprs ++ aggregateExpressions).forall(_.resolved) =>
633+ tryResolveHavingCondition(a, havingCondition, agg)
634+ case a @ AggregateWithHaving (
635+ havingCondition, agg @ Aggregate (Seq (r @ Rollup (groupByExprs)), aggregateExpressions, _))
636+ if (groupByExprs ++ aggregateExpressions).forall(_.resolved) =>
637+ tryResolveHavingCondition(a, havingCondition, agg)
638+ case a @ AggregateWithHaving (
639+ havingCondition, g : GroupingSets ) if g.expressions.forall(_.resolved) =>
640+ tryResolveHavingCondition(a, havingCondition, g)
641+
600642 case a if ! a.childrenResolved => a // be sure all of the children are resolved.
601643
602644 // Ensure group by expressions and aggregate expressions have been resolved.
@@ -2125,13 +2167,17 @@ class Analyzer(
21252167 condition.find(_.isInstanceOf [AggregateExpression ]).isDefined
21262168 }
21272169
2128- def resolveHaving (filter : Filter , agg : Aggregate ): LogicalPlan = {
2129- // Try resolving the condition of the filter as though it is in the aggregate clause
2170+ def resolveFilterCondInAggregate (
2171+ filterCond : Expression ,
2172+ agg : Aggregate ,
2173+ resolveFilterNotInAggOutput : Boolean = false )
2174+ : (Seq [NamedExpression ], Option [Expression ]) = {
2175+ val aggregateExpressions = ArrayBuffer .empty[NamedExpression ]
21302176 try {
21312177 val aggregatedCondition =
21322178 Aggregate (
21332179 agg.groupingExpressions,
2134- Alias (filter.condition , " havingCondition" )() :: Nil ,
2180+ Alias (filterCond , " havingCondition" )() :: Nil ,
21352181 agg.child)
21362182 val resolvedOperator = executeSameContext(aggregatedCondition)
21372183 def resolvedAggregateFilter =
@@ -2144,13 +2190,18 @@ class Analyzer(
21442190 if (resolvedOperator.resolved) {
21452191 // Try to replace all aggregate expressions in the filter by an alias.
21462192 val aggregateExpressions = ArrayBuffer .empty[NamedExpression ]
2193+ val groupingExpressions = if (resolveFilterNotInAggOutput) {
2194+ agg.groupingExpressions :+ resolvedAggregateFilter
2195+ } else {
2196+ agg.groupingExpressions
2197+ }
21472198 val transformedAggregateFilter = resolvedAggregateFilter.transform {
21482199 case ae : AggregateExpression =>
21492200 val alias = Alias (ae, ae.toString)()
21502201 aggregateExpressions += alias
21512202 alias.toAttribute
21522203 // Grouping functions are handled in the rule [[ResolveGroupingAnalytics]].
2153- case e : Expression if agg. groupingExpressions.exists(_.semanticEquals(e)) &&
2204+ case e : Expression if groupingExpressions.exists(_.semanticEquals(e)) &&
21542205 ! ResolveGroupingAnalytics .hasGroupingFunction(e) &&
21552206 ! agg.output.exists(_.semanticEquals(e)) =>
21562207 e match {
@@ -2163,22 +2214,29 @@ class Analyzer(
21632214 alias.toAttribute
21642215 }
21652216 }
2166-
2167- // Push the aggregate expressions into the aggregate (if any).
2168- if (aggregateExpressions.nonEmpty) {
2169- Project (agg.output,
2170- Filter (transformedAggregateFilter,
2171- agg.copy(aggregateExpressions = agg.aggregateExpressions ++ aggregateExpressions)))
2172- } else {
2173- filter
2174- }
2217+ (aggregateExpressions, Some (transformedAggregateFilter))
21752218 } else {
2176- filter
2219+ (aggregateExpressions, None )
21772220 }
21782221 } catch {
21792222 // Attempting to resolve in the aggregate can result in ambiguity. When this happens,
21802223 // just return the original plan.
2181- case ae : AnalysisException => filter
2224+ case ae : AnalysisException => (aggregateExpressions, None )
2225+ }
2226+ }
2227+
2228+ def resolveHaving (filter : Filter , agg : Aggregate ): LogicalPlan = {
2229+ // Try resolving the condition of the filter as though it is in the aggregate clause
2230+ val (aggregateExpressions, transformedAggregateFilter) =
2231+ resolveFilterCondInAggregate(filter.condition, agg)
2232+
2233+ // Push the aggregate expressions into the aggregate (if any).
2234+ if (aggregateExpressions.nonEmpty) {
2235+ Project (agg.output,
2236+ Filter (transformedAggregateFilter.get,
2237+ agg.copy(aggregateExpressions = agg.aggregateExpressions ++ aggregateExpressions)))
2238+ } else {
2239+ filter
21822240 }
21832241 }
21842242 }
0 commit comments