sunchao commented on code in PR #57363:
URL: https://github.com/apache/spark/pull/57363#discussion_r3616717627


##########
sql/hive/src/test/scala/org/apache/spark/sql/hive/execution/HiveUDAFSuite.scala:
##########
@@ -102,28 +102,36 @@ class HiveUDAFSuite extends QueryTest
   test("SPARK-24935: customized Hive UDAF with two aggregation buffers") {
     withTempView("v") {
       spark.range(100).createTempView("v")
-      val df = sql("SELECT id % 2, mock2(id) FROM v GROUP BY id % 2")
-
-      val aggs = collect(df.queryExecution.executedPlan) {
-        case agg: ObjectHashAggregateExec => agg
-      }
-
-      // There should be two aggregate operators, one for partial aggregation, 
and the other for
-      // global aggregation.
-      assert(aggs.length == 2)
-
-      withSQLConf(SQLConf.OBJECT_AGG_SORT_BASED_FALLBACK_THRESHOLD.key -> "1") 
{
-        checkAnswer(df, Seq(
-          Row(0, Row(50, 0)),
-          Row(1, Row(50, 0))
-        ))
-      }
-
-      withSQLConf(SQLConf.OBJECT_AGG_SORT_BASED_FALLBACK_THRESHOLD.key -> 
"100") {
-        checkAnswer(df, Seq(
-          Row(0, Row(50, 0)),
-          Row(1, Row(50, 0))
-        ))
+      // `range(100)` is a single partition, so the partial and final 
aggregate would otherwise be
+      // adjacent and merged by `CombineAdjacentAggregation`. `MockUDAF2` 
deliberately uses distinct
+      // aggregation-buffer classes per aggregation mode (one for consuming 
original input, another
+      // for merging partial buffers), so it does not support a single 
`Complete`-mode aggregate.
+      // Keep the partial/final pair uncombined so the sort-based fallback 
path below is exercised
+      // in the mode this UDAF is designed for.
+      withSQLConf(SQLConf.COMBINE_ADJACENT_AGGREGATION_ENABLED.key -> "false") 
{

Review Comment:
   [P1] Please do not disable this regression test to accommodate the new 
default. A Complete object aggregate calls `HiveUDAFFunction.update`, which 
creates a PARTIAL1-mode buffer, and then `eval`, which passes that buffer to 
the separately initialized FINAL evaluator. Mode-aware Hive UDAFs may legally 
use different buffer classes in those modes, so the default rewrite can turn a 
previously supported query into a `ClassCastException`. I reproduced this and 
also verified that a variant which correctly handles Hive's native COMPLETE 
lifecycle still fails through Spark's mixed-evaluator path. Please make the 
Hive wrapper Complete-safe or exclude these UDAFs before enabling the rule by 
default, and keep this test exercising the default path.



##########
docs/sql-migration-guide.md:
##########
@@ -28,6 +28,7 @@ license: |
 - Since Spark 4.3, the configuration key 
`spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled` has 
been renamed to 
`spark.sql.sources.v2.bucketing.allowKeysSubsetOfPartitionKeys.enabled` to 
reflect that it now applies to storage-partitioned joins, aggregates, and 
windows. The old key continues to work as an alias.
 - Since Spark 4.3, the Spark Thrift Server rejects setting JVM system 
properties through the `set:system:` session configuration overlay (for 
example, in a JDBC connection string). To restore the previous behavior, set 
`spark.sql.legacy.hive.thriftServer.allowSettingSystemProperties` to `true`.
 - Since Spark 4.3, the adaptive execution rule 
`org.apache.spark.sql.execution.adaptive.DynamicJoinSelection` has been renamed 
to `DemoteBroadcastHashJoin`, which now only demotes broadcast hash joins 
(emitting `NO_BROADCAST_HASH`). Its selection of shuffled hash join over sort 
merge join has moved to a new physical rule gated by 
`spark.sql.adaptive.convertSortMergeJoinToShuffledHashJoin.enabled` (default 
`true`). If you previously disabled the shuffled-hash-join preference by 
listing `org.apache.spark.sql.execution.adaptive.DynamicJoinSelection` in 
`spark.sql.adaptive.optimizer.excludedRules`, that name no longer matches any 
rule (unknown names are silently ignored); set 
`spark.sql.adaptive.convertSortMergeJoinToShuffledHashJoin.enabled` to `false` 
instead.
+- Since Spark 4.3, `spark.sql.execution.replaceHashWithSortAgg` defaults to 
`true`. Spark now replaces a hash-based aggregate with a sort aggregate when 
the aggregate's child is already sorted on the grouping keys. To restore the 
previous behavior, set `spark.sql.execution.replaceHashWithSortAgg` to `false`.

Review Comment:
   [P2] Please document `spark.sql.execution.combineAdjacentAggregation` here 
as well. Although the config itself is introduced during 4.3 development, its 
default-on execution behavior is new for users upgrading from 4.2. The two 
settings are now independent, so following this rollback instruction and 
disabling only `replaceHashWithSortAgg` still leaves adjacent Partial/Final 
aggregation combined. Please restore the removed entry and state that both 
settings may need to be false to recover the previous staging.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala:
##########
@@ -3006,18 +3006,17 @@ object SQLConf {
     .doc("Whether to replace hash aggregate node with sort aggregate based on 
children's ordering")
     .version("3.3.0")
     .booleanConf
-    .createWithDefault(false)
+    .createWithDefault(true)
 
   val COMBINE_ADJACENT_AGGREGATION_ENABLED =
     buildConf("spark.sql.execution.combineAdjacentAggregation")
       .internal()
       .doc("When true, combine adjacent aggregation with `Partial` and `Final` 
to `Complete` " +
-        "mode. This defaults to the value of 
`spark.sql.execution.replaceHashWithSortAgg` since " +
-        "combining adjacent aggregation subsumes the partial-and-final merge 
that " +
-        "`replaceHashWithSortAgg` used to perform on its own.")
+        "mode.")
       .version("4.3.0")
       .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
-      .fallbackConf(REPLACE_HASH_WITH_SORT_AGG_ENABLED)
+      .booleanConf
+      .createWithDefault(true)

Review Comment:
   [P2] Enabling this rewrite changes built-in aggregate results, not only the 
physical plan. With AQE off and a single-partition group containing two finite 
`1e155` values, disabling combining returns `NaN` for `var_pop`, `covar_pop`, 
and `regr_sxy`, while Complete returns `0.0`. The old NaN comes from 
overflowing the first merge into the empty Final buffer, so 0.0 is 
mathematically preferable, but a default physical optimization should not 
silently introduce config-dependent results. Please fix and test the 
empty-buffer merge as a deliberate correctness change, or keep these aggregates 
out of combining until the modes are equivalent.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to