Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
27 changes: 23 additions & 4 deletions dev/diffs/3.4.3.diff
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,14 @@ index cf40e944c09..bdd5be4f462 100644

test("A cached table preserves the partitioning and ordering of its cached SparkPlan") {
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
index 1cc09c3d7fc..a84939c045b 100644
index 1cc09c3d7fc..908693e9c12 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
@@ -26,8 +26,9 @@ import org.scalatest.matchers.must.Matchers.the
@@ -24,10 +24,12 @@ import scala.util.Random
import org.scalatest.matchers.must.Matchers.the

import org.apache.spark.SparkException
+import org.apache.spark.sql.comet.CometHashAggregateExec
import org.apache.spark.sql.execution.WholeStageCodegenExec
import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
+import org.apache.spark.sql.comet.CometHashAggregateExec
Expand All @@ -274,7 +277,7 @@ index 1cc09c3d7fc..a84939c045b 100644
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
import org.apache.spark.sql.internal.SQLConf
@@ -691,7 +692,9 @@ class DataFrameAggregateSuite extends QueryTest
@@ -691,7 +693,9 @@ class DataFrameAggregateSuite extends QueryTest
case _ => false
}.isDefined)
} else {
Expand All @@ -285,7 +288,23 @@ index 1cc09c3d7fc..a84939c045b 100644
}

// test case for ObjectHashAggregate and SortAggregate
@@ -755,7 +758,7 @@ class DataFrameAggregateSuite extends QueryTest
@@ -700,7 +704,8 @@ class DataFrameAggregateSuite extends QueryTest
val objHashAggOrSortAggPlan =
stripAQEPlan(objHashAggOrSortAggDF.queryExecution.executedPlan)
if (useObjectHashAgg) {
- assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec])
+ assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec] ||
+ objHashAggOrSortAggPlan.isInstanceOf[CometHashAggregateExec])
} else {
assert(objHashAggOrSortAggPlan.isInstanceOf[SortAggregateExec])
}
@@ -750,12 +755,12 @@ class DataFrameAggregateSuite extends QueryTest
assert(sortAggPlans.isEmpty)

val objHashAggPlans = collect(aggPlan) {
- case objHashAgg: ObjectHashAggregateExec => objHashAgg
+ case agg @ (_: ObjectHashAggregateExec | _: CometHashAggregateExec) => agg
}
assert(objHashAggPlans.nonEmpty)

val exchangePlans = collect(aggPlan) {
Expand Down
27 changes: 23 additions & 4 deletions dev/diffs/3.5.8.diff
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,14 @@ index e5494726695..00937f025c2 100644

test("A cached table preserves the partitioning and ordering of its cached SparkPlan") {
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
index 6f3090d8908..a0e9309888d 100644
index 6f3090d8908..5db376a3754 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
@@ -27,8 +27,9 @@ import org.apache.spark.{SparkException, SparkThrowable}
@@ -25,10 +25,12 @@ import org.scalatest.matchers.must.Matchers.the

import org.apache.spark.{SparkException, SparkThrowable}
import org.apache.spark.sql.catalyst.plans.logical.Expand
+import org.apache.spark.sql.comet.CometHashAggregateExec
import org.apache.spark.sql.execution.WholeStageCodegenExec
import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
+import org.apache.spark.sql.comet.CometHashAggregateExec
Expand All @@ -255,7 +258,7 @@ index 6f3090d8908..a0e9309888d 100644
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
import org.apache.spark.sql.internal.SQLConf
@@ -729,7 +730,9 @@ class DataFrameAggregateSuite extends QueryTest
@@ -729,7 +731,9 @@ class DataFrameAggregateSuite extends QueryTest
case _ => false
}.isDefined)
} else {
Expand All @@ -266,7 +269,23 @@ index 6f3090d8908..a0e9309888d 100644
}

// test case for ObjectHashAggregate and SortAggregate
@@ -793,7 +796,7 @@ class DataFrameAggregateSuite extends QueryTest
@@ -738,7 +742,8 @@ class DataFrameAggregateSuite extends QueryTest
val objHashAggOrSortAggPlan =
stripAQEPlan(objHashAggOrSortAggDF.queryExecution.executedPlan)
if (useObjectHashAgg) {
- assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec])
+ assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec] ||
+ objHashAggOrSortAggPlan.isInstanceOf[CometHashAggregateExec])
} else {
assert(objHashAggOrSortAggPlan.isInstanceOf[SortAggregateExec])
}
@@ -788,12 +793,12 @@ class DataFrameAggregateSuite extends QueryTest
assert(sortAggPlans.isEmpty)

val objHashAggPlans = collect(aggPlan) {
- case objHashAgg: ObjectHashAggregateExec => objHashAgg
+ case agg @ (_: ObjectHashAggregateExec | _: CometHashAggregateExec) => agg
}
assert(objHashAggPlans.nonEmpty)

val exchangePlans = collect(aggPlan) {
Expand Down
27 changes: 23 additions & 4 deletions dev/diffs/4.0.2.diff
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,14 @@ index 0f42502f1d9..e9ff802141f 100644

withTempView("t0", "t1", "t2") {
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
index 9db406ff12f..19b4bb8e39d 100644
index 9db406ff12f..d08b5302103 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
@@ -29,8 +29,9 @@ import org.apache.spark.sql.catalyst.util.AUTO_GENERATED_ALIAS
@@ -27,10 +27,12 @@ import org.apache.spark.{SparkArithmeticException, SparkRuntimeException}
import org.apache.spark.sql.catalyst.plans.logical.Expand
import org.apache.spark.sql.catalyst.util.AUTO_GENERATED_ALIAS
import org.apache.spark.sql.errors.DataTypeErrors.toSQLId
+import org.apache.spark.sql.comet.CometHashAggregateExec
import org.apache.spark.sql.execution.WholeStageCodegenExec
import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
+import org.apache.spark.sql.comet.CometHashAggregateExec
Expand All @@ -392,7 +395,7 @@ index 9db406ff12f..19b4bb8e39d 100644
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
import org.apache.spark.sql.internal.SQLConf
@@ -791,7 +792,9 @@ class DataFrameAggregateSuite extends QueryTest
@@ -791,7 +793,9 @@ class DataFrameAggregateSuite extends QueryTest
case _ => false
}.isDefined)
} else {
Expand All @@ -403,7 +406,23 @@ index 9db406ff12f..19b4bb8e39d 100644
}

// test case for ObjectHashAggregate and SortAggregate
@@ -855,7 +858,7 @@ class DataFrameAggregateSuite extends QueryTest
@@ -800,7 +804,8 @@ class DataFrameAggregateSuite extends QueryTest
val objHashAggOrSortAggPlan =
stripAQEPlan(objHashAggOrSortAggDF.queryExecution.executedPlan)
if (useObjectHashAgg) {
- assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec])
+ assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec] ||
+ objHashAggOrSortAggPlan.isInstanceOf[CometHashAggregateExec])
} else {
assert(objHashAggOrSortAggPlan.isInstanceOf[SortAggregateExec])
}
@@ -850,12 +855,12 @@ class DataFrameAggregateSuite extends QueryTest
assert(sortAggPlans.isEmpty)

val objHashAggPlans = collect(aggPlan) {
- case objHashAgg: ObjectHashAggregateExec => objHashAgg
+ case agg @ (_: ObjectHashAggregateExec | _: CometHashAggregateExec) => agg
}
assert(objHashAggPlans.nonEmpty)

val exchangePlans = collect(aggPlan) {
Expand Down
27 changes: 23 additions & 4 deletions dev/diffs/4.1.2.diff
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,14 @@ index 0d807aeae4d..6d7744e771b 100644

withTempView("t0", "t1", "t2") {
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
index bfe15b33768..31f8ba4b876 100644
index bfe15b33768..550549f1c7c 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/DataFrameAggregateSuite.scala
@@ -30,8 +30,9 @@ import org.apache.spark.sql.catalyst.util.AUTO_GENERATED_ALIAS
@@ -28,10 +28,12 @@ import org.apache.spark.{SparkArithmeticException, SparkRuntimeException}
import org.apache.spark.sql.catalyst.plans.logical.Expand
import org.apache.spark.sql.catalyst.util.AUTO_GENERATED_ALIAS
import org.apache.spark.sql.errors.DataTypeErrors.toSQLId
+import org.apache.spark.sql.comet.CometHashAggregateExec
import org.apache.spark.sql.execution.WholeStageCodegenExec
import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
+import org.apache.spark.sql.comet.CometHashAggregateExec
Expand All @@ -406,7 +409,7 @@ index bfe15b33768..31f8ba4b876 100644
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
import org.apache.spark.sql.internal.SQLConf
@@ -792,7 +793,9 @@ class DataFrameAggregateSuite extends QueryTest
@@ -792,7 +794,9 @@ class DataFrameAggregateSuite extends QueryTest
case _ => false
}.isDefined)
} else {
Expand All @@ -417,7 +420,23 @@ index bfe15b33768..31f8ba4b876 100644
}

// test case for ObjectHashAggregate and SortAggregate
@@ -856,7 +859,7 @@ class DataFrameAggregateSuite extends QueryTest
@@ -801,7 +805,8 @@ class DataFrameAggregateSuite extends QueryTest
val objHashAggOrSortAggPlan =
stripAQEPlan(objHashAggOrSortAggDF.queryExecution.executedPlan)
if (useObjectHashAgg) {
- assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec])
+ assert(objHashAggOrSortAggPlan.isInstanceOf[ObjectHashAggregateExec] ||
+ objHashAggOrSortAggPlan.isInstanceOf[CometHashAggregateExec])
} else {
assert(objHashAggOrSortAggPlan.isInstanceOf[SortAggregateExec])
}
@@ -851,12 +856,12 @@ class DataFrameAggregateSuite extends QueryTest
assert(sortAggPlans.isEmpty)

val objHashAggPlans = collect(aggPlan) {
- case objHashAgg: ObjectHashAggregateExec => objHashAgg
+ case agg @ (_: ObjectHashAggregateExec | _: CometHashAggregateExec) => agg
}
assert(objHashAggPlans.nonEmpty)

val exchangePlans = collect(aggPlan) {
Expand Down
8 changes: 8 additions & 0 deletions docs/source/contributor-guide/expression-audits/agg_funcs.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@
- Spark 3.5.8 (2026-05-26)
- Spark 4.0.1 (2026-05-26)

## collect_list

- Spark 3.4.3 (audited 2026-06-24): `CollectList` extends `Collect[ArrayBuffer[Any]]`, returns `ArrayType(child.dataType, containsNull = false)`, ignores NULL inputs in `update()` (Hive-compatible semantics), and yields an empty array as `defaultResult`. `nullable = false`. No `checkInputDataTypes` override, so any input type accepted by Spark's analyzer is accepted. Registered as both `collect_list` and `array_agg` aliases in `FunctionRegistry`.
- Spark 3.5.8 (audited 2026-06-24): identical to 3.4.3.
- Spark 4.0.1 (audited 2026-06-24): only structural change is adding `with UnaryLike[Expression]` to the case class; no behavior change.
- Spark 4.1.1 (audited 2026-06-24): identical to 4.0.1.
- Comet implementation: native side delegates to `datafusion_spark::function::aggregate::collect::SparkCollectList`, which wraps `ArrayAggAccumulator` with `ignore_nulls = true` and converts a final NULL accumulator state to an empty array. Native intermediate state is `ArrayType(child.dataType, containsNull = true)`, so Comet rewrites intermediate `Partial`/`PartialMerge` object-aggregate buffer attributes from Spark's serialized `BinaryType` to that native state type.

## median

- Spark 3.4.3 (audited 2026-06-24): `Median(child)` is a `RuntimeReplaceableAggregate` with `replacement = Percentile(child, Literal(0.5))`. Catalyst rewrites `median(x)` to `percentile(x, 0.5)` before Comet sees the plan, so it is served by `CometPercentile`.
Expand Down
4 changes: 2 additions & 2 deletions docs/source/user-guide/latest/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ The tables below list every Spark built-in expression with its current status.
| `any_value` | ✅ | |
| `approx_count_distinct` | 🔜 | tracking [#4098](https://github.com/apache/datafusion-comet/issues/4098) |
| `approx_percentile` | ✅ | Byte, short, int, long, float, and double input; other input types fall back to Spark |
| `array_agg` | 🔜 | Array aggregate (related to `collect_list`, [#2524](https://github.com/apache/datafusion-comet/issues/2524)) |
| `array_agg` | | Alias for `collect_list` |
| `avg` | ✅ | Interval types fall back |
| `bit_and` | ✅ | |
| `bit_or` | ✅ | |
| `bit_xor` | ✅ | |
| `bool_and` | ✅ | |
| `bool_or` | ✅ | |
| `collect_list` | 🔜 | [#2524](https://github.com/apache/datafusion-comet/issues/2524) |
| `collect_list` | | |
| `collect_set` | ✅ | |
| `corr` | ✅ | |
| `count` | ✅ | |
Expand Down
Loading
Loading