Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,20 @@ abstract class Optimizer(catalogManager: CatalogManager)
*/
object EliminateDistinct extends Rule[LogicalPlan] {
override def apply(plan: LogicalPlan): LogicalPlan = plan transformExpressions {
case ae: AggregateExpression if ae.isDistinct =>
ae.aggregateFunction match {
case _: Max | _: Min => ae.copy(isDistinct = false)
case _ => ae
}
case ae: AggregateExpression if ae.isDistinct && isDuplicateAgnostic(ae.aggregateFunction) =>
ae.copy(isDistinct = false)
}

private def isDuplicateAgnostic(af: AggregateFunction): Boolean = af match {
case _: Max => true
case _: Min => true
case _: BitAndAgg => true
case _: BitOrAgg => true
case _: First => true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First and Last are non-deterministic, does this matter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In another PR I try to argue that they should be deterministic: #29810
TLDR: An analogous aggregator would be sum on float and double datatype - its result does depend on the order of its inputs, but is deterministic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we finally make First and Last deterministic, then I guess they need to be removed from EliminateDistinct?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's exclude First/Last here before #29810 is merged

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

case _: Last => true
case _: HyperLogLogPlusPlus => true
Comment thread
tanelk marked this conversation as resolved.
Outdated
case _: CollectSet => true
case _ => false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package org.apache.spark.sql.catalyst.optimizer

import org.apache.spark.sql.catalyst.dsl.expressions._
import org.apache.spark.sql.catalyst.dsl.plans._
import org.apache.spark.sql.catalyst.expressions.Expression
import org.apache.spark.sql.catalyst.expressions.aggregate._
import org.apache.spark.sql.catalyst.plans.PlanTest
import org.apache.spark.sql.catalyst.plans.logical.{LocalRelation, LogicalPlan}
import org.apache.spark.sql.catalyst.rules.RuleExecutor
Expand All @@ -32,25 +34,26 @@ class EliminateDistinctSuite extends PlanTest {

val testRelation = LocalRelation('a.int)

test("Eliminate Distinct in Max") {
val query = testRelation
.select(maxDistinct('a).as('result))
.analyze
val answer = testRelation
.select(max('a).as('result))
.analyze
assert(query != answer)
comparePlans(Optimize.execute(query), answer)
}

test("Eliminate Distinct in Min") {
val query = testRelation
.select(minDistinct('a).as('result))
.analyze
val answer = testRelation
.select(min('a).as('result))
.analyze
assert(query != answer)
comparePlans(Optimize.execute(query), answer)
Seq(
("max", Max(_)),
("min", Min(_)),
Comment thread
tanelk marked this conversation as resolved.
Outdated
("approx_count_distinct", HyperLogLogPlusPlus(_: Expression)),
("first", First(_, ignoreNulls = true)),
("last", Last(_, ignoreNulls = true)),
("bit_and", BitAndAgg(_)),
("bit_or", BitOrAgg(_)),
("collect_set", CollectSet(_: Expression))
).foreach {
case (name, agg) =>
test(s"Eliminate Distinct in $name") {
val query = testRelation
.select(agg('a).toAggregateExpression(isDistinct = true).as('result))
.analyze
val answer = testRelation
.select(agg('a).toAggregateExpression(isDistinct = false).as('result))
.analyze
assert(query != answer)
comparePlans(Optimize.execute(query), answer)
}
}
}