This is an automated email from the ASF dual-hosted git repository.

zhouyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new e5c5dfec81 [GLUTEN-12837][TEST] Migrate Spark 3.3-only test coverage 
to Spark 3.4+ (#12840)
e5c5dfec81 is described below

commit e5c5dfec812555e697442137fcddc1ae1f571ab6
Author: YangJie <[email protected]>
AuthorDate: Mon Aug 24 16:21:55 2026 +0800

    [GLUTEN-12837][TEST] Migrate Spark 3.3-only test coverage to Spark 3.4+ 
(#12840)
---
 .../utils/clickhouse/ClickHouseTestSettings.scala  |  4 --
 .../gluten/utils/velox/VeloxTestSettings.scala     |  1 -
 .../gluten/utils/velox/VeloxTestSettings.scala     |  4 ++
 .../spark/sql/GlutenDataFrameFunctionsSuite.scala  | 82 ++++++++++++++++++++++
 .../spark/sql/GlutenDateFunctionsSuite.scala       | 18 +++++
 .../sql/GlutenDynamicPartitionPruningSuite.scala   | 35 +++++++++
 .../expressions/GlutenLiteralExpressionSuite.scala | 38 +++++++++-
 .../expressions/GlutenMathExpressionsSuite.scala   | 20 ++++++
 .../ClickHouseAdaptiveQueryExecSuite.scala         | 55 +++++++++++++++
 .../hive/execution/GlutenHiveSQLQuerySuite.scala   | 20 ++++++
 .../gluten/utils/velox/VeloxTestSettings.scala     |  4 ++
 .../spark/sql/GlutenDataFrameFunctionsSuite.scala  | 82 ++++++++++++++++++++++
 .../spark/sql/GlutenDateFunctionsSuite.scala       | 18 +++++
 .../sql/GlutenDynamicPartitionPruningSuite.scala   | 35 +++++++++
 .../expressions/GlutenLiteralExpressionSuite.scala | 38 +++++++++-
 .../expressions/GlutenMathExpressionsSuite.scala   | 20 ++++++
 .../ClickHouseAdaptiveQueryExecSuite.scala         | 55 +++++++++++++++
 .../hive/execution/GlutenHiveSQLQuerySuite.scala   | 20 ++++++
 .../gluten/utils/velox/VeloxTestSettings.scala     |  4 ++
 .../spark/sql/GlutenDataFrameFunctionsSuite.scala  | 81 +++++++++++++++++++++
 .../spark/sql/GlutenDateFunctionsSuite.scala       | 18 +++++
 .../sql/GlutenDynamicPartitionPruningSuite.scala   | 35 +++++++++
 .../expressions/GlutenLiteralExpressionSuite.scala | 38 +++++++++-
 .../expressions/GlutenMathExpressionsSuite.scala   | 20 ++++++
 .../ClickHouseAdaptiveQueryExecSuite.scala         | 55 +++++++++++++++
 .../hive/execution/GlutenHiveSQLQuerySuite.scala   | 20 ++++++
 .../gluten/utils/velox/VeloxTestSettings.scala     |  4 ++
 .../spark/sql/GlutenDataFrameFunctionsSuite.scala  | 81 +++++++++++++++++++++
 .../spark/sql/GlutenDateFunctionsSuite.scala       | 18 +++++
 .../sql/GlutenDynamicPartitionPruningSuite.scala   | 35 +++++++++
 .../expressions/GlutenLiteralExpressionSuite.scala | 38 +++++++++-
 .../expressions/GlutenMathExpressionsSuite.scala   | 20 ++++++
 .../ClickHouseAdaptiveQueryExecSuite.scala         | 55 +++++++++++++++
 .../hive/execution/GlutenHiveSQLQuerySuite.scala   | 20 ++++++
 .../org/apache/spark/sql/GlutenImplicitsTest.scala | 74 ++++++++++++++-----
 35 files changed, 1140 insertions(+), 25 deletions(-)

diff --git 
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
 
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
index b0b767cd91..d9dcdde5b7 100644
--- 
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
+++ 
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/clickhouse/ClickHouseTestSettings.scala
@@ -1875,10 +1875,6 @@ class ClickHouseTestSettings extends BackendTestSettings 
{
     .exclude(
       "SELECT structFieldSimple.key, arrayFieldSimple[1] FROM tableWithSchema 
a where int_Field=1")
     .exclude("SELECT structFieldComplex.Value.`value_(2)` FROM 
tableWithSchema")
-  enableSuite[GlutenImplicitsTest]
-    .excludeGlutenTest("fallbackSummary with shuffle")
-    .excludeGlutenTest("fallbackSummary with cache")
-    .excludeGlutenTest("fallbackSummary with cached data and shuffle")
   enableSuite[GlutenSparkSessionExtensionSuite]
     .includeGlutenTest("customColumnarOp")
   enableSuite[GlutenHiveSQLQueryCHSuite]
diff --git 
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
 
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 0ba7d7796c..fb42198e2d 100644
--- 
a/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++ 
b/gluten-ut/spark33/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -907,7 +907,6 @@ class VeloxTestSettings extends BackendTestSettings {
   enableSuite[GlutenHashUDAQueryWithControlledFallbackSuite]
   enableSuite[GlutenSQLQuerySuiteAE]
   enableSuite[GlutenWindowQuerySuite]
-  enableSuite[GlutenImplicitsTest]
   enableSuite[GlutenCollapseProjectExecTransformerSuite]
   enableSuite[GlutenSparkSessionExtensionSuite]
     .includeGlutenTest("customColumnarOp")
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index e7cda00f71..e0d2e3ee5e 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -826,10 +826,14 @@ class VeloxTestSettings extends BackendTestSettings {
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOff]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenExpressionsSchemaSuite]
   enableSuite[GlutenExtraStrategiesSuite]
   enableSuite[GlutenFileBasedDataSourceSuite]
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
index 1725b98e75..ebe1fc50fc 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
@@ -111,4 +111,86 @@ class GlutenDataFrameFunctionsSuite extends 
DataFrameFunctionsSuite with GlutenS
       checkAnswer(df1.selectExpr("array_insert(a, -6, c)"), Seq(Row(Seq(3, 
null, 3, 2, 5, 1, 2))))
     }
   }
+
+  testGluten("flatten function") {
+    // Test cases with a primitive type
+    val intDF = Seq(
+      (Seq(Seq(1, 2, 3), Seq(4, 5), Seq(6))),
+      (Seq(Seq(1, 2))),
+      (Seq(Seq(1), Seq.empty)),
+      (Seq(Seq.empty, Seq(1)))
+    ).toDF("i")
+
+    val intDFResult = Seq(Row(Seq(1, 2, 3, 4, 5, 6)), Row(Seq(1, 2)), 
Row(Seq(1)), Row(Seq(1)))
+
+    def testInt(): Unit = {
+      checkAnswer(intDF.select(flatten($"i")), intDFResult)
+      checkAnswer(intDF.selectExpr("flatten(i)"), intDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testInt()
+    // Test with cached relation, the Project will be evaluated with codegen
+    intDF.cache()
+    testInt()
+
+    // Test cases with non-primitive types
+    val strDF = Seq(
+      (Seq(Seq("a", "b"), Seq("c"), Seq("d", "e", "f"))),
+      (Seq(Seq("a", "b"))),
+      (Seq(Seq("a", null), Seq(null, "b"), Seq(null, null))),
+      (Seq(Seq("a"), Seq.empty)),
+      (Seq(Seq.empty, Seq("a")))
+    ).toDF("s")
+
+    val strDFResult = Seq(
+      Row(Seq("a", "b", "c", "d", "e", "f")),
+      Row(Seq("a", "b")),
+      Row(Seq("a", null, null, "b", null, null)),
+      Row(Seq("a")),
+      Row(Seq("a")))
+
+    def testString(): Unit = {
+      checkAnswer(strDF.select(flatten($"s")), strDFResult)
+      checkAnswer(strDF.selectExpr("flatten(s)"), strDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testString()
+    // Test with cached relation, the Project will be evaluated with codegen
+    strDF.cache()
+    testString()
+
+    val arrDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+
+    def testArray(): Unit = {
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(arr, array(null, 5), array(6, 
null)))"),
+        Seq(Row(Seq(1, 2, 3, null, 5, 6, null))))
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(array(arr, arr), array(arr)))"),
+        Seq(Row(Seq(Seq(1, 2, 3), Seq(1, 2, 3), Seq(1, 2, 3)))))
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testArray()
+    // Test with cached relation, the Project will be evaluated with codegen
+    arrDF.cache()
+    testArray()
+
+    // Error test cases
+    val oneRowDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"arr"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"i"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"s"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.selectExpr("flatten(null)")
+    }
+  }
 }
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
index d66f26d7c0..37c3afef66 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
@@ -282,4 +282,22 @@ class GlutenDateFunctionsSuite extends DateFunctionsSuite 
with GlutenSQLTestsTra
     val df1 = Seq(x1, x2).toDF("x")
     checkAnswer(df1.select(to_date(col("x"))), Row(Date.valueOf("2016-02-29")) 
:: Row(null) :: Nil)
   }
+
+  testGluten("date_from_unix_date") {
+    // -100000 and 200000 are outside ClickHouse's native Date32 range
+    // [1900-01-01, 2299-12-31]. They guard against implementations that clamp 
to that
+    // range (e.g. mapping to CH toDate32), which would silently diverge from 
Spark.
+    val df = Seq(Some(0), Some(1000), Some(-100000), Some(200000), 
None).toDF("unix_date")
+    val expected = Seq(
+      Row(Date.valueOf("1970-01-01")),
+      Row(Date.valueOf("1972-09-27")),
+      Row(Date.valueOf("1696-03-17")),
+      Row(Date.valueOf("2517-08-01")),
+      Row(null))
+
+    // Go through expr() so the same case works on every supported version: 
the Scala
+    // functions API does not expose date_from_unix_date before Spark 3.5.
+    checkAnswer(df.select(expr("date_from_unix_date(unix_date)")), expected)
+    checkAnswer(df.selectExpr("date_from_unix_date(unix_date)"), expected)
+  }
 }
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
index 740d52d0b1..3b2be3c72e 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
@@ -828,3 +828,38 @@ class GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan
     super.sparkConf.set(GlutenConfig.COLUMNAR_BATCHSCAN_ENABLED.key, "false")
   }
 }
+
+// Test DPP with project disabled by user for some reason, which can also mock 
the situation
+// that project is not transformable.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+// Same as above except whole-stage codegen is off.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
index 556d185af0..f81ef0b6ff 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
@@ -17,5 +17,41 @@
 package org.apache.spark.sql.catalyst.expressions
 
 import org.apache.spark.sql.GlutenTestsTrait
+import org.apache.spark.sql.Row
+import org.apache.spark.sql.catalyst.util.DateTimeUtils
+import org.apache.spark.sql.internal.SQLConf
+import org.apache.spark.sql.types._
+import org.apache.spark.unsafe.types.CalendarInterval
 
-class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {}
+import java.nio.charset.StandardCharsets
+import java.time.{Instant, LocalDate}
+
+class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {
+  testGluten("default") {
+    checkEvaluation(Literal.default(BooleanType), false)
+    checkEvaluation(Literal.default(ByteType), 0.toByte)
+    checkEvaluation(Literal.default(ShortType), 0.toShort)
+    checkEvaluation(Literal.default(IntegerType), 0)
+    checkEvaluation(Literal.default(LongType), 0L)
+    checkEvaluation(Literal.default(FloatType), 0.0f)
+    checkEvaluation(Literal.default(DoubleType), 0.0)
+    checkEvaluation(Literal.default(StringType), "")
+    checkEvaluation(Literal.default(BinaryType), 
"".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Literal.default(DecimalType.USER_DEFAULT), Decimal(0))
+    checkEvaluation(Literal.default(DecimalType.SYSTEM_DEFAULT), Decimal(0))
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "false") {
+      checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
+      checkEvaluation(Literal.default(TimestampType), 
DateTimeUtils.toJavaTimestamp(0L))
+    }
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "true") {
+      checkEvaluation(Literal.default(DateType), LocalDate.ofEpochDay(0))
+      checkEvaluation(Literal.default(TimestampType), Instant.ofEpochSecond(0))
+    }
+    checkEvaluation(Literal.default(CalendarIntervalType), new 
CalendarInterval(0, 0, 0L))
+    checkEvaluation(Literal.default(YearMonthIntervalType()), 0)
+    checkEvaluation(Literal.default(DayTimeIntervalType()), 0L)
+    checkEvaluation(Literal.default(ArrayType(StringType)), Array())
+    checkEvaluation(Literal.default(MapType(IntegerType, StringType)), Map())
+    checkEvaluation(Literal.default(StructType(StructField("a", StringType) :: 
Nil)), Row(""))
+  }
+}
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
index c755a475ed..b793896de2 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
@@ -20,6 +20,8 @@ import org.apache.spark.sql.GlutenTestsTrait
 import org.apache.spark.sql.catalyst.dsl.expressions._
 import org.apache.spark.sql.types._
 
+import java.nio.charset.StandardCharsets
+
 class GlutenMathExpressionsSuite extends MathExpressionsSuite with 
GlutenTestsTrait {
   testGluten("round/bround/floor/ceil") {
     val scales = -6 to 6
@@ -274,4 +276,22 @@ class GlutenMathExpressionsSuite extends 
MathExpressionsSuite with GlutenTestsTr
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(3.1411), 
Literal(-3))), Decimal(1000))
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(135.135), 
Literal(-2))), Decimal(200))
   }
+
+  testGluten("unhex") {
+    checkEvaluation(Unhex(Literal.create(null, StringType)), null)
+    checkEvaluation(Unhex(Literal("737472696E67")), 
"string".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Unhex(Literal("")), new Array[Byte](0))
+    checkEvaluation(Unhex(Literal("F")), Array[Byte](15))
+    checkEvaluation(Unhex(Literal("ff")), Array[Byte](-1))
+
+//    checkEvaluation(Unhex(Literal("GG")), null)
+    checkEvaluation(Unhex(Literal("123")), Array[Byte](1, 35))
+    checkEvaluation(Unhex(Literal("12345")), Array[Byte](1, 35, 69))
+    // scalastyle:off
+    // Turn off scala style for non-ascii chars
+    checkEvaluation(Unhex(Literal("E4B889E9878DE79A84")), 
"三重的".getBytes(StandardCharsets.UTF_8))
+//    checkEvaluation(Unhex(Literal("三重的")), null)
+    // scalastyle:on
+    checkConsistencyBetweenInterpretedAndCodegen((e: Expression) => Unhex(e), 
StringType)
+  }
 }
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
index 3c4e7bc5e1..35729ac030 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
@@ -1549,4 +1549,59 @@ class ClickHouseAdaptiveQueryExecSuite extends 
AdaptiveQueryExecSuite with Glute
         }
     }
   }
+
+  // TODO: re-enable this test after #12845. 
findTopLevelSortMergeJoinTransform matches the Velox
+  // SortMergeJoinExecTransformer, which ClickHouse never produces, so it 
collects nothing here and
+  // the case fails on CH-3.5. The expected counts are also still unverified 
on ClickHouse.
+  ignoreGluten("SPARK-37652: optimize skewed join through union") {
+    withSQLConf(
+      SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "true",
+      SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "-1",
+      SQLConf.SKEW_JOIN_SKEWED_PARTITION_THRESHOLD.key -> "100",
+      SQLConf.ADVISORY_PARTITION_SIZE_IN_BYTES.key -> "100"
+    ) {
+      withTempView("skewData1", "skewData2") {
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 3 as key1", "id as value1")
+          .createOrReplaceTempView("skewData1")
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 1 as key2", "id as value2")
+          .createOrReplaceTempView("skewData2")
+
+        def checkSkewJoin(query: String, joinNums: Int, optimizeSkewJoinNums: 
Int): Unit = {
+          val (_, innerAdaptivePlan) = runAdaptiveAndVerifyResult(query)
+          val joins = findTopLevelSortMergeJoinTransform(innerAdaptivePlan)
+          val optimizeSkewJoins = joins.filter(_.isSkewJoin)
+          assert(joins.size == joinNums && optimizeSkewJoins.size == 
optimizeSkewJoinNums)
+        }
+
+        // skewJoin union skewJoin
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData1 JOIN skewData2 ON key1 = 
key2",
+          2,
+          2)
+
+        // skewJoin union aggregate
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData2 GROUP BY key2",
+          1,
+          1)
+
+        // skewJoin1 union (skewJoin2 join aggregate)
+        // skewJoin2 will lead to extra shuffles, but skew1 cannot be optimized
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 UNION ALL 
" +
+            "SELECT key1 from (SELECT key1 FROM skewData1 JOIN skewData2 ON 
key1 = key2) tmp1 " +
+            "JOIN (SELECT key2 FROM skewData2 GROUP BY key2) tmp2 ON key1 = 
key2",
+          3,
+          0
+        )
+      }
+    }
+  }
+
 }
diff --git 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
index 010e55c346..8be19c216a 100644
--- 
a/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
+++ 
b/gluten-ut/spark34/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
@@ -171,4 +171,24 @@ class GlutenHiveSQLQuerySuite extends 
GlutenHiveSQLQuerySuiteBase {
       }
     }
   }
+
+  testGluten("avoid unnecessary filter binding for subfield during scan") {
+    withSQLConf(
+      "spark.sql.hive.convertMetastoreParquet" -> "false") {
+      sql("DROP TABLE IF EXISTS test_subfield")
+      sql(
+        "CREATE TABLE test_subfield (name STRING, favorite_color STRING," +
+          " label STRUCT<label_1:STRING, label_2:STRING>) USING hive 
OPTIONS(fileFormat 'parquet')")
+      sql(
+        "INSERT INTO test_subfield VALUES('test_1', 'red', 
named_struct('label_1', 'label-a'," +
+          "'label_2', 'label-b'))")
+      val df = spark.sql("select * from test_subfield where name='test_1'")
+      checkAnswer(df, Seq(Row("test_1", "red", Row("label-a", "label-b"))))
+      checkOperatorMatch[HiveTableScanExecTransformer](df)
+    }
+    spark.sessionState.catalog.dropTable(
+      TableIdentifier("test_subfield"),
+      ignoreIfNotExists = true,
+      purge = false)
+  }
 }
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 7fa7fa8dd6..c753c17ed0 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -791,10 +791,14 @@ class VeloxTestSettings extends BackendTestSettings {
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOff]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenExpressionsSchemaSuite]
   enableSuite[GlutenExtraStrategiesSuite]
   enableSuite[GlutenFileBasedDataSourceSuite]
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
index 2b0b40790a..e64f760ab5 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
@@ -49,4 +49,86 @@ class GlutenDataFrameFunctionsSuite extends 
DataFrameFunctionsSuite with GlutenS
       false
     )
   }
+
+  testGluten("flatten function") {
+    // Test cases with a primitive type
+    val intDF = Seq(
+      (Seq(Seq(1, 2, 3), Seq(4, 5), Seq(6))),
+      (Seq(Seq(1, 2))),
+      (Seq(Seq(1), Seq.empty)),
+      (Seq(Seq.empty, Seq(1)))
+    ).toDF("i")
+
+    val intDFResult = Seq(Row(Seq(1, 2, 3, 4, 5, 6)), Row(Seq(1, 2)), 
Row(Seq(1)), Row(Seq(1)))
+
+    def testInt(): Unit = {
+      checkAnswer(intDF.select(flatten($"i")), intDFResult)
+      checkAnswer(intDF.selectExpr("flatten(i)"), intDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testInt()
+    // Test with cached relation, the Project will be evaluated with codegen
+    intDF.cache()
+    testInt()
+
+    // Test cases with non-primitive types
+    val strDF = Seq(
+      (Seq(Seq("a", "b"), Seq("c"), Seq("d", "e", "f"))),
+      (Seq(Seq("a", "b"))),
+      (Seq(Seq("a", null), Seq(null, "b"), Seq(null, null))),
+      (Seq(Seq("a"), Seq.empty)),
+      (Seq(Seq.empty, Seq("a")))
+    ).toDF("s")
+
+    val strDFResult = Seq(
+      Row(Seq("a", "b", "c", "d", "e", "f")),
+      Row(Seq("a", "b")),
+      Row(Seq("a", null, null, "b", null, null)),
+      Row(Seq("a")),
+      Row(Seq("a")))
+
+    def testString(): Unit = {
+      checkAnswer(strDF.select(flatten($"s")), strDFResult)
+      checkAnswer(strDF.selectExpr("flatten(s)"), strDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testString()
+    // Test with cached relation, the Project will be evaluated with codegen
+    strDF.cache()
+    testString()
+
+    val arrDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+
+    def testArray(): Unit = {
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(arr, array(null, 5), array(6, 
null)))"),
+        Seq(Row(Seq(1, 2, 3, null, 5, 6, null))))
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(array(arr, arr), array(arr)))"),
+        Seq(Row(Seq(Seq(1, 2, 3), Seq(1, 2, 3), Seq(1, 2, 3)))))
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testArray()
+    // Test with cached relation, the Project will be evaluated with codegen
+    arrDF.cache()
+    testArray()
+
+    // Error test cases
+    val oneRowDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"arr"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"i"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"s"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.selectExpr("flatten(null)")
+    }
+  }
 }
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
index 082f06641b..7390d3ff5a 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
@@ -297,4 +297,22 @@ class GlutenDateFunctionsSuite extends DateFunctionsSuite 
with GlutenSQLTestsTra
     val df1 = Seq(x1, x2).toDF("x")
     checkAnswer(df1.select(to_date(col("x"))), Row(Date.valueOf("2016-02-29")) 
:: Row(null) :: Nil)
   }
+
+  testGluten("date_from_unix_date") {
+    // -100000 and 200000 are outside ClickHouse's native Date32 range
+    // [1900-01-01, 2299-12-31]. They guard against implementations that clamp 
to that
+    // range (e.g. mapping to CH toDate32), which would silently diverge from 
Spark.
+    val df = Seq(Some(0), Some(1000), Some(-100000), Some(200000), 
None).toDF("unix_date")
+    val expected = Seq(
+      Row(Date.valueOf("1970-01-01")),
+      Row(Date.valueOf("1972-09-27")),
+      Row(Date.valueOf("1696-03-17")),
+      Row(Date.valueOf("2517-08-01")),
+      Row(null))
+
+    // Go through expr() so the same case works on every supported version: 
the Scala
+    // functions API does not expose date_from_unix_date before Spark 3.5.
+    checkAnswer(df.select(expr("date_from_unix_date(unix_date)")), expected)
+    checkAnswer(df.selectExpr("date_from_unix_date(unix_date)"), expected)
+  }
 }
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
index 4c1dcd2a09..ab34d46f67 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
@@ -768,3 +768,38 @@ class GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan
     super.sparkConf.set(GlutenConfig.COLUMNAR_BATCHSCAN_ENABLED.key, "false")
   }
 }
+
+// Test DPP with project disabled by user for some reason, which can also mock 
the situation
+// that project is not transformable.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+// Same as above except whole-stage codegen is off.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
index 556d185af0..f81ef0b6ff 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
@@ -17,5 +17,41 @@
 package org.apache.spark.sql.catalyst.expressions
 
 import org.apache.spark.sql.GlutenTestsTrait
+import org.apache.spark.sql.Row
+import org.apache.spark.sql.catalyst.util.DateTimeUtils
+import org.apache.spark.sql.internal.SQLConf
+import org.apache.spark.sql.types._
+import org.apache.spark.unsafe.types.CalendarInterval
 
-class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {}
+import java.nio.charset.StandardCharsets
+import java.time.{Instant, LocalDate}
+
+class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {
+  testGluten("default") {
+    checkEvaluation(Literal.default(BooleanType), false)
+    checkEvaluation(Literal.default(ByteType), 0.toByte)
+    checkEvaluation(Literal.default(ShortType), 0.toShort)
+    checkEvaluation(Literal.default(IntegerType), 0)
+    checkEvaluation(Literal.default(LongType), 0L)
+    checkEvaluation(Literal.default(FloatType), 0.0f)
+    checkEvaluation(Literal.default(DoubleType), 0.0)
+    checkEvaluation(Literal.default(StringType), "")
+    checkEvaluation(Literal.default(BinaryType), 
"".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Literal.default(DecimalType.USER_DEFAULT), Decimal(0))
+    checkEvaluation(Literal.default(DecimalType.SYSTEM_DEFAULT), Decimal(0))
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "false") {
+      checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
+      checkEvaluation(Literal.default(TimestampType), 
DateTimeUtils.toJavaTimestamp(0L))
+    }
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "true") {
+      checkEvaluation(Literal.default(DateType), LocalDate.ofEpochDay(0))
+      checkEvaluation(Literal.default(TimestampType), Instant.ofEpochSecond(0))
+    }
+    checkEvaluation(Literal.default(CalendarIntervalType), new 
CalendarInterval(0, 0, 0L))
+    checkEvaluation(Literal.default(YearMonthIntervalType()), 0)
+    checkEvaluation(Literal.default(DayTimeIntervalType()), 0L)
+    checkEvaluation(Literal.default(ArrayType(StringType)), Array())
+    checkEvaluation(Literal.default(MapType(IntegerType, StringType)), Map())
+    checkEvaluation(Literal.default(StructType(StructField("a", StringType) :: 
Nil)), Row(""))
+  }
+}
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
index b4459df420..2e329264d8 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
@@ -20,6 +20,8 @@ import org.apache.spark.sql.GlutenTestsTrait
 import org.apache.spark.sql.catalyst.dsl.expressions._
 import org.apache.spark.sql.types._
 
+import java.nio.charset.StandardCharsets
+
 class GlutenMathExpressionsSuite extends MathExpressionsSuite with 
GlutenTestsTrait {
   testGluten("round/bround/floor/ceil") {
     val scales = -6 to 6
@@ -277,4 +279,22 @@ class GlutenMathExpressionsSuite extends 
MathExpressionsSuite with GlutenTestsTr
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(3.1411), 
Literal(-3))), Decimal(1000))
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(135.135), 
Literal(-2))), Decimal(200))
   }
+
+  testGluten("unhex") {
+    checkEvaluation(Unhex(Literal.create(null, StringType)), null)
+    checkEvaluation(Unhex(Literal("737472696E67")), 
"string".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Unhex(Literal("")), new Array[Byte](0))
+    checkEvaluation(Unhex(Literal("F")), Array[Byte](15))
+    checkEvaluation(Unhex(Literal("ff")), Array[Byte](-1))
+
+//    checkEvaluation(Unhex(Literal("GG")), null)
+    checkEvaluation(Unhex(Literal("123")), Array[Byte](1, 35))
+    checkEvaluation(Unhex(Literal("12345")), Array[Byte](1, 35, 69))
+    // scalastyle:off
+    // Turn off scala style for non-ascii chars
+    checkEvaluation(Unhex(Literal("E4B889E9878DE79A84")), 
"三重的".getBytes(StandardCharsets.UTF_8))
+//    checkEvaluation(Unhex(Literal("三重的")), null)
+    // scalastyle:on
+    checkConsistencyBetweenInterpretedAndCodegen((e: Expression) => Unhex(e), 
StringType)
+  }
 }
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
index 9fcc6677b9..a682f65546 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
@@ -1572,4 +1572,59 @@ class ClickHouseAdaptiveQueryExecSuite extends 
AdaptiveQueryExecSuite with Glute
         }
     }
   }
+
+  // TODO: re-enable this test after #12845. 
findTopLevelSortMergeJoinTransform matches the Velox
+  // SortMergeJoinExecTransformer, which ClickHouse never produces, so it 
collects nothing here and
+  // the case fails on CH-3.5. The expected counts are also still unverified 
on ClickHouse.
+  ignoreGluten("SPARK-37652: optimize skewed join through union") {
+    withSQLConf(
+      SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "true",
+      SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "-1",
+      SQLConf.SKEW_JOIN_SKEWED_PARTITION_THRESHOLD.key -> "100",
+      SQLConf.ADVISORY_PARTITION_SIZE_IN_BYTES.key -> "100"
+    ) {
+      withTempView("skewData1", "skewData2") {
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 3 as key1", "id as value1")
+          .createOrReplaceTempView("skewData1")
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 1 as key2", "id as value2")
+          .createOrReplaceTempView("skewData2")
+
+        def checkSkewJoin(query: String, joinNums: Int, optimizeSkewJoinNums: 
Int): Unit = {
+          val (_, innerAdaptivePlan) = runAdaptiveAndVerifyResult(query)
+          val joins = findTopLevelSortMergeJoinTransform(innerAdaptivePlan)
+          val optimizeSkewJoins = joins.filter(_.isSkewJoin)
+          assert(joins.size == joinNums && optimizeSkewJoins.size == 
optimizeSkewJoinNums)
+        }
+
+        // skewJoin union skewJoin
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData1 JOIN skewData2 ON key1 = 
key2",
+          2,
+          2)
+
+        // skewJoin union aggregate
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData2 GROUP BY key2",
+          1,
+          1)
+
+        // skewJoin1 union (skewJoin2 join aggregate)
+        // skewJoin2 will lead to extra shuffles, but skew1 cannot be optimized
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 UNION ALL 
" +
+            "SELECT key1 from (SELECT key1 FROM skewData1 JOIN skewData2 ON 
key1 = key2) tmp1 " +
+            "JOIN (SELECT key2 FROM skewData2 GROUP BY key2) tmp2 ON key1 = 
key2",
+          3,
+          0
+        )
+      }
+    }
+  }
+
 }
diff --git 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
index 5308ee7ede..ddd61613ee 100644
--- 
a/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
+++ 
b/gluten-ut/spark35/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
@@ -241,4 +241,24 @@ class GlutenHiveSQLQuerySuite extends 
GlutenHiveSQLQuerySuiteBase {
       }
     }
   }
+
+  testGluten("avoid unnecessary filter binding for subfield during scan") {
+    withSQLConf(
+      "spark.sql.hive.convertMetastoreParquet" -> "false") {
+      sql("DROP TABLE IF EXISTS test_subfield")
+      sql(
+        "CREATE TABLE test_subfield (name STRING, favorite_color STRING," +
+          " label STRUCT<label_1:STRING, label_2:STRING>) USING hive 
OPTIONS(fileFormat 'parquet')")
+      sql(
+        "INSERT INTO test_subfield VALUES('test_1', 'red', 
named_struct('label_1', 'label-a'," +
+          "'label_2', 'label-b'))")
+      val df = spark.sql("select * from test_subfield where name='test_1'")
+      checkAnswer(df, Seq(Row("test_1", "red", Row("label-a", "label-b"))))
+      checkOperatorMatch[HiveTableScanExecTransformer](df)
+    }
+    spark.sessionState.catalog.dropTable(
+      TableIdentifier("test_subfield"),
+      ignoreIfNotExists = true,
+      purge = false)
+  }
 }
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 7cbe7fea4e..c0b965d6e8 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -1071,10 +1071,14 @@ class VeloxTestSettings extends BackendTestSettings {
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOff]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenExpressionsSchemaSuite]
   enableSuite[GlutenExtraStrategiesSuite]
   enableSuite[GlutenFileBasedDataSourceSuite]
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
index 49f6052b20..0d2caf6100 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
@@ -278,4 +278,85 @@ class GlutenDataFrameFunctionsSuite extends 
DataFrameFunctionsSuite with GlutenS
     testMapOfPrimitiveTypesCombination()
   }
 
+  testGluten("flatten function") {
+    // Test cases with a primitive type
+    val intDF = Seq(
+      (Seq(Seq(1, 2, 3), Seq(4, 5), Seq(6))),
+      (Seq(Seq(1, 2))),
+      (Seq(Seq(1), Seq.empty)),
+      (Seq(Seq.empty, Seq(1)))
+    ).toDF("i")
+
+    val intDFResult = Seq(Row(Seq(1, 2, 3, 4, 5, 6)), Row(Seq(1, 2)), 
Row(Seq(1)), Row(Seq(1)))
+
+    def testInt(): Unit = {
+      checkAnswer(intDF.select(flatten($"i")), intDFResult)
+      checkAnswer(intDF.selectExpr("flatten(i)"), intDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testInt()
+    // Test with cached relation, the Project will be evaluated with codegen
+    intDF.cache()
+    testInt()
+
+    // Test cases with non-primitive types
+    val strDF = Seq(
+      (Seq(Seq("a", "b"), Seq("c"), Seq("d", "e", "f"))),
+      (Seq(Seq("a", "b"))),
+      (Seq(Seq("a", null), Seq(null, "b"), Seq(null, null))),
+      (Seq(Seq("a"), Seq.empty)),
+      (Seq(Seq.empty, Seq("a")))
+    ).toDF("s")
+
+    val strDFResult = Seq(
+      Row(Seq("a", "b", "c", "d", "e", "f")),
+      Row(Seq("a", "b")),
+      Row(Seq("a", null, null, "b", null, null)),
+      Row(Seq("a")),
+      Row(Seq("a")))
+
+    def testString(): Unit = {
+      checkAnswer(strDF.select(flatten($"s")), strDFResult)
+      checkAnswer(strDF.selectExpr("flatten(s)"), strDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testString()
+    // Test with cached relation, the Project will be evaluated with codegen
+    strDF.cache()
+    testString()
+
+    val arrDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+
+    def testArray(): Unit = {
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(arr, array(null, 5), array(6, 
null)))"),
+        Seq(Row(Seq(1, 2, 3, null, 5, 6, null))))
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(array(arr, arr), array(arr)))"),
+        Seq(Row(Seq(Seq(1, 2, 3), Seq(1, 2, 3), Seq(1, 2, 3)))))
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testArray()
+    // Test with cached relation, the Project will be evaluated with codegen
+    arrDF.cache()
+    testArray()
+
+    // Error test cases
+    val oneRowDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"arr"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"i"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"s"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.selectExpr("flatten(null)")
+    }
+  }
 }
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
index 082f06641b..7390d3ff5a 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
@@ -297,4 +297,22 @@ class GlutenDateFunctionsSuite extends DateFunctionsSuite 
with GlutenSQLTestsTra
     val df1 = Seq(x1, x2).toDF("x")
     checkAnswer(df1.select(to_date(col("x"))), Row(Date.valueOf("2016-02-29")) 
:: Row(null) :: Nil)
   }
+
+  testGluten("date_from_unix_date") {
+    // -100000 and 200000 are outside ClickHouse's native Date32 range
+    // [1900-01-01, 2299-12-31]. They guard against implementations that clamp 
to that
+    // range (e.g. mapping to CH toDate32), which would silently diverge from 
Spark.
+    val df = Seq(Some(0), Some(1000), Some(-100000), Some(200000), 
None).toDF("unix_date")
+    val expected = Seq(
+      Row(Date.valueOf("1970-01-01")),
+      Row(Date.valueOf("1972-09-27")),
+      Row(Date.valueOf("1696-03-17")),
+      Row(Date.valueOf("2517-08-01")),
+      Row(null))
+
+    // Go through expr() so the same case works on every supported version: 
the Scala
+    // functions API does not expose date_from_unix_date before Spark 3.5.
+    checkAnswer(df.select(expr("date_from_unix_date(unix_date)")), expected)
+    checkAnswer(df.selectExpr("date_from_unix_date(unix_date)"), expected)
+  }
 }
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
index dc96c09bc2..9f4e18fb82 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
@@ -709,3 +709,38 @@ class GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan
     super.sparkConf.set(GlutenConfig.COLUMNAR_BATCHSCAN_ENABLED.key, "false")
   }
 }
+
+// Test DPP with project disabled by user for some reason, which can also mock 
the situation
+// that project is not transformable.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+// Same as above except whole-stage codegen is off.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
index 556d185af0..f81ef0b6ff 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
@@ -17,5 +17,41 @@
 package org.apache.spark.sql.catalyst.expressions
 
 import org.apache.spark.sql.GlutenTestsTrait
+import org.apache.spark.sql.Row
+import org.apache.spark.sql.catalyst.util.DateTimeUtils
+import org.apache.spark.sql.internal.SQLConf
+import org.apache.spark.sql.types._
+import org.apache.spark.unsafe.types.CalendarInterval
 
-class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {}
+import java.nio.charset.StandardCharsets
+import java.time.{Instant, LocalDate}
+
+class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {
+  testGluten("default") {
+    checkEvaluation(Literal.default(BooleanType), false)
+    checkEvaluation(Literal.default(ByteType), 0.toByte)
+    checkEvaluation(Literal.default(ShortType), 0.toShort)
+    checkEvaluation(Literal.default(IntegerType), 0)
+    checkEvaluation(Literal.default(LongType), 0L)
+    checkEvaluation(Literal.default(FloatType), 0.0f)
+    checkEvaluation(Literal.default(DoubleType), 0.0)
+    checkEvaluation(Literal.default(StringType), "")
+    checkEvaluation(Literal.default(BinaryType), 
"".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Literal.default(DecimalType.USER_DEFAULT), Decimal(0))
+    checkEvaluation(Literal.default(DecimalType.SYSTEM_DEFAULT), Decimal(0))
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "false") {
+      checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
+      checkEvaluation(Literal.default(TimestampType), 
DateTimeUtils.toJavaTimestamp(0L))
+    }
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "true") {
+      checkEvaluation(Literal.default(DateType), LocalDate.ofEpochDay(0))
+      checkEvaluation(Literal.default(TimestampType), Instant.ofEpochSecond(0))
+    }
+    checkEvaluation(Literal.default(CalendarIntervalType), new 
CalendarInterval(0, 0, 0L))
+    checkEvaluation(Literal.default(YearMonthIntervalType()), 0)
+    checkEvaluation(Literal.default(DayTimeIntervalType()), 0L)
+    checkEvaluation(Literal.default(ArrayType(StringType)), Array())
+    checkEvaluation(Literal.default(MapType(IntegerType, StringType)), Map())
+    checkEvaluation(Literal.default(StructType(StructField("a", StringType) :: 
Nil)), Row(""))
+  }
+}
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
index 1c47e1b2f3..db1be819ad 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
@@ -20,6 +20,8 @@ import org.apache.spark.sql.GlutenExpressionOffloadTracker
 import org.apache.spark.sql.catalyst.dsl.expressions._
 import org.apache.spark.sql.types._
 
+import java.nio.charset.StandardCharsets
+
 class GlutenMathExpressionsSuite extends MathExpressionsSuite with 
GlutenExpressionOffloadTracker {
   override protected def offloadCategory: String = "math"
   testGluten("round/bround/floor/ceil") {
@@ -278,4 +280,22 @@ class GlutenMathExpressionsSuite extends 
MathExpressionsSuite with GlutenExpress
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(3.1411), 
Literal(-3))), Decimal(1000))
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(135.135), 
Literal(-2))), Decimal(200))
   }
+
+  testGluten("unhex") {
+    checkEvaluation(Unhex(Literal.create(null, StringType)), null)
+    checkEvaluation(Unhex(Literal("737472696E67")), 
"string".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Unhex(Literal("")), new Array[Byte](0))
+    checkEvaluation(Unhex(Literal("F")), Array[Byte](15))
+    checkEvaluation(Unhex(Literal("ff")), Array[Byte](-1))
+
+//    checkEvaluation(Unhex(Literal("GG")), null)
+    checkEvaluation(Unhex(Literal("123")), Array[Byte](1, 35))
+    checkEvaluation(Unhex(Literal("12345")), Array[Byte](1, 35, 69))
+    // scalastyle:off
+    // Turn off scala style for non-ascii chars
+    checkEvaluation(Unhex(Literal("E4B889E9878DE79A84")), 
"三重的".getBytes(StandardCharsets.UTF_8))
+//    checkEvaluation(Unhex(Literal("三重的")), null)
+    // scalastyle:on
+    checkConsistencyBetweenInterpretedAndCodegen((e: Expression) => Unhex(e), 
StringType)
+  }
 }
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
index 903c7ccd13..babab4b660 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
@@ -1566,4 +1566,59 @@ class ClickHouseAdaptiveQueryExecSuite extends 
AdaptiveQueryExecSuite with Glute
         }
     }
   }
+
+  // TODO: re-enable this test after #12845. 
findTopLevelSortMergeJoinTransform matches the Velox
+  // SortMergeJoinExecTransformer, which ClickHouse never produces, so it 
collects nothing here and
+  // the case fails on CH-3.5. The expected counts are also still unverified 
on ClickHouse.
+  ignoreGluten("SPARK-37652: optimize skewed join through union") {
+    withSQLConf(
+      SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "true",
+      SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "-1",
+      SQLConf.SKEW_JOIN_SKEWED_PARTITION_THRESHOLD.key -> "100",
+      SQLConf.ADVISORY_PARTITION_SIZE_IN_BYTES.key -> "100"
+    ) {
+      withTempView("skewData1", "skewData2") {
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 3 as key1", "id as value1")
+          .createOrReplaceTempView("skewData1")
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 1 as key2", "id as value2")
+          .createOrReplaceTempView("skewData2")
+
+        def checkSkewJoin(query: String, joinNums: Int, optimizeSkewJoinNums: 
Int): Unit = {
+          val (_, innerAdaptivePlan) = runAdaptiveAndVerifyResult(query)
+          val joins = findTopLevelSortMergeJoinTransform(innerAdaptivePlan)
+          val optimizeSkewJoins = joins.filter(_.isSkewJoin)
+          assert(joins.size == joinNums && optimizeSkewJoins.size == 
optimizeSkewJoinNums)
+        }
+
+        // skewJoin union skewJoin
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData1 JOIN skewData2 ON key1 = 
key2",
+          2,
+          2)
+
+        // skewJoin union aggregate
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData2 GROUP BY key2",
+          1,
+          1)
+
+        // skewJoin1 union (skewJoin2 join aggregate)
+        // skewJoin2 will lead to extra shuffles, but skew1 cannot be optimized
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 UNION ALL 
" +
+            "SELECT key1 from (SELECT key1 FROM skewData1 JOIN skewData2 ON 
key1 = key2) tmp1 " +
+            "JOIN (SELECT key2 FROM skewData2 GROUP BY key2) tmp2 ON key1 = 
key2",
+          3,
+          0
+        )
+      }
+    }
+  }
+
 }
diff --git 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
index 5308ee7ede..ddd61613ee 100644
--- 
a/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
+++ 
b/gluten-ut/spark40/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
@@ -241,4 +241,24 @@ class GlutenHiveSQLQuerySuite extends 
GlutenHiveSQLQuerySuiteBase {
       }
     }
   }
+
+  testGluten("avoid unnecessary filter binding for subfield during scan") {
+    withSQLConf(
+      "spark.sql.hive.convertMetastoreParquet" -> "false") {
+      sql("DROP TABLE IF EXISTS test_subfield")
+      sql(
+        "CREATE TABLE test_subfield (name STRING, favorite_color STRING," +
+          " label STRUCT<label_1:STRING, label_2:STRING>) USING hive 
OPTIONS(fileFormat 'parquet')")
+      sql(
+        "INSERT INTO test_subfield VALUES('test_1', 'red', 
named_struct('label_1', 'label-a'," +
+          "'label_2', 'label-b'))")
+      val df = spark.sql("select * from test_subfield where name='test_1'")
+      checkAnswer(df, Seq(Row("test_1", "red", Row("label-a", "label-b"))))
+      checkOperatorMatch[HiveTableScanExecTransformer](df)
+    }
+    spark.sessionState.catalog.dropTable(
+      TableIdentifier("test_subfield"),
+      ignoreIfNotExists = true,
+      purge = false)
+  }
 }
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
index 61bd44f554..ee88614222 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/gluten/utils/velox/VeloxTestSettings.scala
@@ -1059,10 +1059,14 @@ class VeloxTestSettings extends BackendTestSettings {
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOff]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOn]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOnDisableScan]
   enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject]
+  enableSuite[GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject]
   enableSuite[GlutenExpressionsSchemaSuite]
   enableSuite[GlutenExtraStrategiesSuite]
   enableSuite[GlutenFileBasedDataSourceSuite]
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
index 49f6052b20..0d2caf6100 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDataFrameFunctionsSuite.scala
@@ -278,4 +278,85 @@ class GlutenDataFrameFunctionsSuite extends 
DataFrameFunctionsSuite with GlutenS
     testMapOfPrimitiveTypesCombination()
   }
 
+  testGluten("flatten function") {
+    // Test cases with a primitive type
+    val intDF = Seq(
+      (Seq(Seq(1, 2, 3), Seq(4, 5), Seq(6))),
+      (Seq(Seq(1, 2))),
+      (Seq(Seq(1), Seq.empty)),
+      (Seq(Seq.empty, Seq(1)))
+    ).toDF("i")
+
+    val intDFResult = Seq(Row(Seq(1, 2, 3, 4, 5, 6)), Row(Seq(1, 2)), 
Row(Seq(1)), Row(Seq(1)))
+
+    def testInt(): Unit = {
+      checkAnswer(intDF.select(flatten($"i")), intDFResult)
+      checkAnswer(intDF.selectExpr("flatten(i)"), intDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testInt()
+    // Test with cached relation, the Project will be evaluated with codegen
+    intDF.cache()
+    testInt()
+
+    // Test cases with non-primitive types
+    val strDF = Seq(
+      (Seq(Seq("a", "b"), Seq("c"), Seq("d", "e", "f"))),
+      (Seq(Seq("a", "b"))),
+      (Seq(Seq("a", null), Seq(null, "b"), Seq(null, null))),
+      (Seq(Seq("a"), Seq.empty)),
+      (Seq(Seq.empty, Seq("a")))
+    ).toDF("s")
+
+    val strDFResult = Seq(
+      Row(Seq("a", "b", "c", "d", "e", "f")),
+      Row(Seq("a", "b")),
+      Row(Seq("a", null, null, "b", null, null)),
+      Row(Seq("a")),
+      Row(Seq("a")))
+
+    def testString(): Unit = {
+      checkAnswer(strDF.select(flatten($"s")), strDFResult)
+      checkAnswer(strDF.selectExpr("flatten(s)"), strDFResult)
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testString()
+    // Test with cached relation, the Project will be evaluated with codegen
+    strDF.cache()
+    testString()
+
+    val arrDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+
+    def testArray(): Unit = {
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(arr, array(null, 5), array(6, 
null)))"),
+        Seq(Row(Seq(1, 2, 3, null, 5, 6, null))))
+      checkAnswer(
+        arrDF.selectExpr("flatten(array(array(arr, arr), array(arr)))"),
+        Seq(Row(Seq(Seq(1, 2, 3), Seq(1, 2, 3), Seq(1, 2, 3)))))
+    }
+
+    // Test with local relation, the Project will be evaluated without codegen
+    testArray()
+    // Test with cached relation, the Project will be evaluated with codegen
+    arrDF.cache()
+    testArray()
+
+    // Error test cases
+    val oneRowDF = Seq((1, "a", Seq(1, 2, 3))).toDF("i", "s", "arr")
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"arr"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"i"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.select(flatten($"s"))
+    }
+    intercept[AnalysisException] {
+      oneRowDF.selectExpr("flatten(null)")
+    }
+  }
 }
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
index 082f06641b..7390d3ff5a 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDateFunctionsSuite.scala
@@ -297,4 +297,22 @@ class GlutenDateFunctionsSuite extends DateFunctionsSuite 
with GlutenSQLTestsTra
     val df1 = Seq(x1, x2).toDF("x")
     checkAnswer(df1.select(to_date(col("x"))), Row(Date.valueOf("2016-02-29")) 
:: Row(null) :: Nil)
   }
+
+  testGluten("date_from_unix_date") {
+    // -100000 and 200000 are outside ClickHouse's native Date32 range
+    // [1900-01-01, 2299-12-31]. They guard against implementations that clamp 
to that
+    // range (e.g. mapping to CH toDate32), which would silently diverge from 
Spark.
+    val df = Seq(Some(0), Some(1000), Some(-100000), Some(200000), 
None).toDF("unix_date")
+    val expected = Seq(
+      Row(Date.valueOf("1970-01-01")),
+      Row(Date.valueOf("1972-09-27")),
+      Row(Date.valueOf("1696-03-17")),
+      Row(Date.valueOf("2517-08-01")),
+      Row(null))
+
+    // Go through expr() so the same case works on every supported version: 
the Scala
+    // functions API does not expose date_from_unix_date before Spark 3.5.
+    checkAnswer(df.select(expr("date_from_unix_date(unix_date)")), expected)
+    checkAnswer(df.selectExpr("date_from_unix_date(unix_date)"), expected)
+  }
 }
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
index d2222aa8a6..2a1a2c55fe 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/GlutenDynamicPartitionPruningSuite.scala
@@ -711,3 +711,38 @@ class GlutenDynamicPartitionPruningV2SuiteAEOffDisableScan
     super.sparkConf.set(GlutenConfig.COLUMNAR_BATCHSCAN_ENABLED.key, "false")
   }
 }
+
+// Test DPP with project disabled by user for some reason, which can also mock 
the situation
+// that project is not transformable.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+// Same as above except whole-stage codegen is off.
+class GlutenDynamicPartitionPruningV1SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOnDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf.set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+  }
+}
+
+class GlutenDynamicPartitionPruningV2SuiteAEOffWSCGOffDisableProject
+  extends GlutenDynamicPartitionPruningV2SuiteAEOff {
+  override def sparkConf: SparkConf = {
+    super.sparkConf
+      .set(GlutenConfig.COLUMNAR_PROJECT_ENABLED.key, "false")
+      .set(SQLConf.WHOLESTAGE_CODEGEN_ENABLED.key, "false")
+  }
+}
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
index 556d185af0..f81ef0b6ff 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenLiteralExpressionSuite.scala
@@ -17,5 +17,41 @@
 package org.apache.spark.sql.catalyst.expressions
 
 import org.apache.spark.sql.GlutenTestsTrait
+import org.apache.spark.sql.Row
+import org.apache.spark.sql.catalyst.util.DateTimeUtils
+import org.apache.spark.sql.internal.SQLConf
+import org.apache.spark.sql.types._
+import org.apache.spark.unsafe.types.CalendarInterval
 
-class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {}
+import java.nio.charset.StandardCharsets
+import java.time.{Instant, LocalDate}
+
+class GlutenLiteralExpressionSuite extends LiteralExpressionSuite with 
GlutenTestsTrait {
+  testGluten("default") {
+    checkEvaluation(Literal.default(BooleanType), false)
+    checkEvaluation(Literal.default(ByteType), 0.toByte)
+    checkEvaluation(Literal.default(ShortType), 0.toShort)
+    checkEvaluation(Literal.default(IntegerType), 0)
+    checkEvaluation(Literal.default(LongType), 0L)
+    checkEvaluation(Literal.default(FloatType), 0.0f)
+    checkEvaluation(Literal.default(DoubleType), 0.0)
+    checkEvaluation(Literal.default(StringType), "")
+    checkEvaluation(Literal.default(BinaryType), 
"".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Literal.default(DecimalType.USER_DEFAULT), Decimal(0))
+    checkEvaluation(Literal.default(DecimalType.SYSTEM_DEFAULT), Decimal(0))
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "false") {
+      checkEvaluation(Literal.default(DateType), DateTimeUtils.toJavaDate(0))
+      checkEvaluation(Literal.default(TimestampType), 
DateTimeUtils.toJavaTimestamp(0L))
+    }
+    withSQLConf(SQLConf.DATETIME_JAVA8API_ENABLED.key -> "true") {
+      checkEvaluation(Literal.default(DateType), LocalDate.ofEpochDay(0))
+      checkEvaluation(Literal.default(TimestampType), Instant.ofEpochSecond(0))
+    }
+    checkEvaluation(Literal.default(CalendarIntervalType), new 
CalendarInterval(0, 0, 0L))
+    checkEvaluation(Literal.default(YearMonthIntervalType()), 0)
+    checkEvaluation(Literal.default(DayTimeIntervalType()), 0L)
+    checkEvaluation(Literal.default(ArrayType(StringType)), Array())
+    checkEvaluation(Literal.default(MapType(IntegerType, StringType)), Map())
+    checkEvaluation(Literal.default(StructType(StructField("a", StringType) :: 
Nil)), Row(""))
+  }
+}
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
index ac0296c3f0..9d4d1fe848 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/catalyst/expressions/GlutenMathExpressionsSuite.scala
@@ -21,6 +21,8 @@ import org.apache.spark.sql.catalyst.dsl.expressions._
 import org.apache.spark.sql.shim.GlutenTestsTrait
 import org.apache.spark.sql.types._
 
+import java.nio.charset.StandardCharsets
+
 class GlutenMathExpressionsSuite
   extends MathExpressionsSuite
   with GlutenExpressionOffloadTracker
@@ -282,4 +284,22 @@ class GlutenMathExpressionsSuite
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(3.1411), 
Literal(-3))), Decimal(1000))
     checkEvaluation(checkDataTypeAndCast(RoundCeil(Literal(135.135), 
Literal(-2))), Decimal(200))
   }
+
+  testGluten("unhex") {
+    checkEvaluation(Unhex(Literal.create(null, StringType)), null)
+    checkEvaluation(Unhex(Literal("737472696E67")), 
"string".getBytes(StandardCharsets.UTF_8))
+    checkEvaluation(Unhex(Literal("")), new Array[Byte](0))
+    checkEvaluation(Unhex(Literal("F")), Array[Byte](15))
+    checkEvaluation(Unhex(Literal("ff")), Array[Byte](-1))
+
+//    checkEvaluation(Unhex(Literal("GG")), null)
+    checkEvaluation(Unhex(Literal("123")), Array[Byte](1, 35))
+    checkEvaluation(Unhex(Literal("12345")), Array[Byte](1, 35, 69))
+    // scalastyle:off
+    // Turn off scala style for non-ascii chars
+    checkEvaluation(Unhex(Literal("E4B889E9878DE79A84")), 
"三重的".getBytes(StandardCharsets.UTF_8))
+//    checkEvaluation(Unhex(Literal("三重的")), null)
+    // scalastyle:on
+    checkConsistencyBetweenInterpretedAndCodegen((e: Expression) => Unhex(e), 
StringType)
+  }
 }
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
index 903c7ccd13..babab4b660 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/execution/adaptive/clickhouse/ClickHouseAdaptiveQueryExecSuite.scala
@@ -1566,4 +1566,59 @@ class ClickHouseAdaptiveQueryExecSuite extends 
AdaptiveQueryExecSuite with Glute
         }
     }
   }
+
+  // TODO: re-enable this test after #12845. 
findTopLevelSortMergeJoinTransform matches the Velox
+  // SortMergeJoinExecTransformer, which ClickHouse never produces, so it 
collects nothing here and
+  // the case fails on CH-3.5. The expected counts are also still unverified 
on ClickHouse.
+  ignoreGluten("SPARK-37652: optimize skewed join through union") {
+    withSQLConf(
+      SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "true",
+      SQLConf.AUTO_BROADCASTJOIN_THRESHOLD.key -> "-1",
+      SQLConf.SKEW_JOIN_SKEWED_PARTITION_THRESHOLD.key -> "100",
+      SQLConf.ADVISORY_PARTITION_SIZE_IN_BYTES.key -> "100"
+    ) {
+      withTempView("skewData1", "skewData2") {
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 3 as key1", "id as value1")
+          .createOrReplaceTempView("skewData1")
+        spark
+          .range(0, 1000, 1, 10)
+          .selectExpr("id % 1 as key2", "id as value2")
+          .createOrReplaceTempView("skewData2")
+
+        def checkSkewJoin(query: String, joinNums: Int, optimizeSkewJoinNums: 
Int): Unit = {
+          val (_, innerAdaptivePlan) = runAdaptiveAndVerifyResult(query)
+          val joins = findTopLevelSortMergeJoinTransform(innerAdaptivePlan)
+          val optimizeSkewJoins = joins.filter(_.isSkewJoin)
+          assert(joins.size == joinNums && optimizeSkewJoins.size == 
optimizeSkewJoinNums)
+        }
+
+        // skewJoin union skewJoin
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData1 JOIN skewData2 ON key1 = 
key2",
+          2,
+          2)
+
+        // skewJoin union aggregate
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 " +
+            "UNION ALL SELECT key2 FROM skewData2 GROUP BY key2",
+          1,
+          1)
+
+        // skewJoin1 union (skewJoin2 join aggregate)
+        // skewJoin2 will lead to extra shuffles, but skew1 cannot be optimized
+        checkSkewJoin(
+          "SELECT key1 FROM skewData1 JOIN skewData2 ON key1 = key2 UNION ALL 
" +
+            "SELECT key1 from (SELECT key1 FROM skewData1 JOIN skewData2 ON 
key1 = key2) tmp1 " +
+            "JOIN (SELECT key2 FROM skewData2 GROUP BY key2) tmp2 ON key1 = 
key2",
+          3,
+          0
+        )
+      }
+    }
+  }
+
 }
diff --git 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
index 5308ee7ede..ddd61613ee 100644
--- 
a/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
+++ 
b/gluten-ut/spark41/src/test/scala/org/apache/spark/sql/hive/execution/GlutenHiveSQLQuerySuite.scala
@@ -241,4 +241,24 @@ class GlutenHiveSQLQuerySuite extends 
GlutenHiveSQLQuerySuiteBase {
       }
     }
   }
+
+  testGluten("avoid unnecessary filter binding for subfield during scan") {
+    withSQLConf(
+      "spark.sql.hive.convertMetastoreParquet" -> "false") {
+      sql("DROP TABLE IF EXISTS test_subfield")
+      sql(
+        "CREATE TABLE test_subfield (name STRING, favorite_color STRING," +
+          " label STRUCT<label_1:STRING, label_2:STRING>) USING hive 
OPTIONS(fileFormat 'parquet')")
+      sql(
+        "INSERT INTO test_subfield VALUES('test_1', 'red', 
named_struct('label_1', 'label-a'," +
+          "'label_2', 'label-b'))")
+      val df = spark.sql("select * from test_subfield where name='test_1'")
+      checkAnswer(df, Seq(Row("test_1", "red", Row("label-a", "label-b"))))
+      checkOperatorMatch[HiveTableScanExecTransformer](df)
+    }
+    spark.sessionState.catalog.dropTable(
+      TableIdentifier("test_subfield"),
+      ignoreIfNotExists = true,
+      purge = false)
+  }
 }
diff --git 
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
 b/gluten-ut/test/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
similarity index 55%
rename from 
gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
rename to 
gluten-ut/test/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
index 6de15f434a..07f6e48b3e 100644
--- 
a/gluten-ut/spark33/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
+++ 
b/gluten-ut/test/src/test/scala/org/apache/spark/sql/GlutenImplicitsTest.scala
@@ -17,13 +17,39 @@
 package org.apache.spark.sql
 
 import org.apache.gluten.config.GlutenConfig
+import org.apache.gluten.utils.BackendTestUtils
 
 import org.apache.spark.SparkConf
 import org.apache.spark.sql.execution.GlutenImplicits._
 import org.apache.spark.sql.internal.SQLConf
+import org.apache.spark.sql.test.SharedSparkSession
+import org.apache.spark.util.Utils
 
-class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
-  sys.props.put(GlutenConfig.COLUMNAR_TABLE_CACHE_ENABLED.key, "true")
+class GlutenImplicitsTest extends GlutenQueryTest with SharedSparkSession {
+
+  // Keep the warehouse under target/ so that `mvn clean` removes it. Left to 
itself,
+  // SharedSparkSession resolves StaticSQLConf.WAREHOUSE_PATH against the 
fork's working
+  // directory, which is the module basedir, and a run interrupted between 
beforeAll and afterAll
+  // would leave t1 behind in a place no build step cleans up. Appending the 
class name keeps
+  // this suite's warehouse separate from the other suites in this module. The 
path mirrors what
+  // GlutenTestsBaseTrait hands to the per-version suites, inlined rather than 
inherited because
+  // mixing that trait in would route every case through BackendTestSettings, 
where this suite is
+  // no longer registered.
+  private val warehouse: String =
+    getClass.getResource("/").getPath + 
"unit-tests-working-home/spark-warehouse/" +
+      getClass.getCanonicalName
+
+  override protected def sparkConf: SparkConf = {
+    // Reuse the session conf every other Gluten SQL suite runs with, since 
the node counts
+    // asserted below depend on it.
+    GlutenSQLTestsBaseTrait
+      .nativeSparkConf(super.sparkConf, warehouse)
+      .set("spark.sql.shuffle.partitions", "5")
+      // Three cases below assert node counts for a cached relation, which 
needs the table cache
+      // offloaded. That is the default, but set it explicitly so the 
expectations do not change
+      // silently if the default ever flips.
+      .set(GlutenConfig.COLUMNAR_TABLE_CACHE_ENABLED.key, "true")
+  }
 
   override protected def beforeAll(): Unit = {
     super.beforeAll()
@@ -36,8 +62,16 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
   }
 
   override protected def afterAll(): Unit = {
-    spark.sql("drop table t1")
-    super.afterAll()
+    // super.afterAll() stops the session, so it has to run even if the drop 
fails: this module
+    // runs every suite in one fork, and a leaked SparkContext would take the 
later suites with
+    // it. tryWithSafeFinally rather than a bare finally, because both halves 
throw for the same
+    // reason (a stopped SparkContext fails the drop and catalog.reset() 
alike) and a bare finally
+    // would replace the original exception with the one from super.afterAll().
+    Utils.tryWithSafeFinally {
+      spark.sql("DROP TABLE IF EXISTS t1")
+    } {
+      super.afterAll()
+    }
   }
 
   override protected def afterEach(): Unit = {
@@ -45,11 +79,6 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     super.afterEach()
   }
 
-  override def sparkConf: SparkConf = {
-    super.sparkConf
-      .set("spark.sql.shuffle.partitions", "5")
-  }
-
   private def withAQEEnabledAndDisabled(f: => Unit): Unit = {
     withSQLConf(
       SQLConf.ADAPTIVE_EXECUTION_ENABLED.key -> "true",
@@ -67,7 +96,13 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     }
   }
 
-  testGluten("fallbackSummary with query") {
+  // ClickHouse reports different Gluten node counts for the cases that 
involve a shuffle or a
+  // cached relation. spark33's ClickHouseTestSettings excluded exactly these 
three without
+  // recording what CH actually reports, so they stay Velox-only until someone 
reads the real
+  // counts off a ClickHouse run. Follows GlutenCheckOverflowTransformerSuite 
in this module.
+  private def assumeVeloxOnly(): Unit = 
assume(BackendTestUtils.isVeloxBackendLoaded())
+
+  test("fallbackSummary with query") {
     withAQEEnabledAndDisabled {
       val df = spark.table("t1").filter(_.getLong(0) > 0)
       assert(df.fallbackSummary().numGlutenNodes == 1, df.fallbackSummary())
@@ -78,7 +113,8 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     }
   }
 
-  testGluten("fallbackSummary with shuffle") {
+  test("fallbackSummary with shuffle") {
+    assumeVeloxOnly()
     withAQEEnabledAndDisabled {
       val df = spark.sql("SELECT c2 FROM t1 group by c2").filter(_.getLong(0) 
> 0)
       assert(df.fallbackSummary().numGlutenNodes == 6, df.fallbackSummary())
@@ -89,7 +125,7 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     }
   }
 
-  testGluten("fallbackSummary with set command") {
+  test("fallbackSummary with set command") {
     withAQEEnabledAndDisabled {
       val df = spark.sql("set k=v")
       assert(df.fallbackSummary().numGlutenNodes == 0, df.fallbackSummary())
@@ -97,17 +133,22 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     }
   }
 
-  testGluten("fallbackSummary with data write command") {
+  test("fallbackSummary with data write command") {
     withAQEEnabledAndDisabled {
       withTable("tmp") {
         val df = spark.sql("create table tmp using parquet as select * from 
t1")
-        assert(df.fallbackSummary().numGlutenNodes == 1, df.fallbackSummary())
+        // Spark 3.3 counts one Gluten node here. Since 3.4 the CTAS is 
executed as an
+        // ExecutedCommandExec, which collectFallbackNodes walks past without 
counting anything,
+        // so the summary reports neither a Gluten node nor a fallback node.
+        val expectedGlutenNodes = if (isSparkVersionGE("3.4")) 0 else 1
+        assert(df.fallbackSummary().numGlutenNodes == expectedGlutenNodes, 
df.fallbackSummary())
         assert(df.fallbackSummary().numFallbackNodes == 0, 
df.fallbackSummary())
       }
     }
   }
 
-  testGluten("fallbackSummary with cache") {
+  test("fallbackSummary with cache") {
+    assumeVeloxOnly()
     withAQEEnabledAndDisabled {
       val df = spark.table("t1").cache().filter(_.getLong(0) > 0)
       assert(df.fallbackSummary().numGlutenNodes == 2, df.fallbackSummary())
@@ -118,7 +159,8 @@ class GlutenImplicitsTest extends GlutenSQLTestsBaseTrait {
     }
   }
 
-  testGluten("fallbackSummary with cached data and shuffle") {
+  test("fallbackSummary with cached data and shuffle") {
+    assumeVeloxOnly()
     withAQEEnabledAndDisabled {
       val df = spark.sql("select * from t1").filter(_.getLong(0) > 
0).cache.repartition()
       assert(df.fallbackSummary().numGlutenNodes == 7, df.fallbackSummary())


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

Reply via email to