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

exmy 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 3817c1dab2 [GLUTEN-12834][CH] Fix nullable filtering after Expand with 
constant columns (#12818)
3817c1dab2 is described below

commit 3817c1dab26477c62857823d87745562b8749b05
Author: exmy <[email protected]>
AuthorDate: Thu Aug 20 16:43:04 2026 +0800

    [GLUTEN-12834][CH] Fix nullable filtering after Expand with constant 
columns (#12818)
    
    * [CH] Fix nullable filtering after Expand with constant columns
    
    Materialize constant selection columns in regular and advanced Expand to 
prevent stale constant filter state from leaking null rows. Preserve nullable 
types for null literals and add regression coverage for both Expand paths.
    
    * [CH] Simplify nullable handling and Expand test configuration
    
    Use the ClickHouse makeNullable helper for null literals and configure the 
lazy aggregate Expand tests only through SparkEnv, which is the actual 
configuration source.
---
 .../GlutenClickHouseTPCHSaltNullParquetSuite.scala | 76 +++++++++++++++++++++-
 .../local-engine/Operator/AdvancedExpandStep.cpp   |  2 +-
 cpp-ch/local-engine/Operator/ExpandTransform.cpp   |  2 +-
 .../Parser/RelParsers/ExpandRelParser.cpp          |  7 ++
 4 files changed, 84 insertions(+), 3 deletions(-)

diff --git 
a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
 
b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
index 4766c2fd24..f6277c214f 100644
--- 
a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
+++ 
b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
@@ -21,7 +21,7 @@ import org.apache.gluten.config.GlutenConfig
 import org.apache.gluten.execution._
 import org.apache.gluten.execution.GlutenPlan
 
-import org.apache.spark.{SparkConf, SparkException}
+import org.apache.spark.{SparkConf, SparkEnv, SparkException}
 import org.apache.spark.sql.DataFrame
 import org.apache.spark.sql.catalyst.optimizer.{BuildLeft, ConstantFolding, 
NullPropagation}
 import org.apache.spark.sql.execution.{ColumnarToRowExec, ReusedSubqueryExec, 
SubqueryExec}
@@ -878,6 +878,80 @@ class GlutenClickHouseTPCHSaltNullParquetSuite
     compareResultsAgainstVanillaSpark(sql, true, { _ => })
   }
 
+  test("grouping sets preserves nullable columns across union") {
+    val sql =
+      """
+        |select msg_type, os, count(*) as cnt
+        |from (
+        |  select 'file' as msg_type, 'Android' as os, id from range(10)
+        |  union all
+        |  select 'file' as msg_type, 'iOS' as os, id from range(10)
+        |) t
+        |group by grouping sets ((msg_type), (os))
+        |having msg_type is not null
+        |order by msg_type, os, cnt
+        |""".stripMargin
+    withSparkEnvConf(CHConfig.runtimeConfig("enable_lazy_aggregate_expand"), 
"false") {
+      compareResultsAgainstVanillaSpark(
+        sql,
+        true,
+        {
+          df =>
+            val expands = 
collectWithSubqueries(df.queryExecution.executedPlan) {
+              case e: ExpandExecTransformer
+                  if !e.child.isInstanceOf[HashAggregateExecBaseTransformer] =>
+                e
+            }
+            assert(expands.size == 1)
+        }
+      )
+    }
+  }
+
+  test("lazy aggregate expand preserves nullable columns across union") {
+    val sql =
+      """
+        |select msg_type, os, count(*) as cnt
+        |from (
+        |  select 'file' as msg_type, 'Android' as os, id from range(10)
+        |  union all
+        |  select 'file' as msg_type, 'iOS' as os, id from range(10)
+        |) t
+        |group by grouping sets ((msg_type), (os))
+        |having msg_type is not null
+        |order by msg_type, os, cnt
+        |""".stripMargin
+    withSparkEnvConf(CHConfig.runtimeConfig("enable_lazy_aggregate_expand"), 
"true") {
+      compareResultsAgainstVanillaSpark(
+        sql,
+        true,
+        {
+          df =>
+            val expands = 
collectWithSubqueries(df.queryExecution.executedPlan) {
+              case e: ExpandExecTransformer
+                  if e.child.isInstanceOf[HashAggregateExecBaseTransformer] =>
+                e
+            }
+            assert(expands.size == 1)
+        }
+      )
+    }
+  }
+
+  private def withSparkEnvConf(key: String, value: String)(f: => Unit): Unit = 
{
+    val sparkConf = SparkEnv.get.conf
+    val previousValue = sparkConf.getOption(key)
+    sparkConf.set(key, value)
+    try {
+      f
+    } finally {
+      previousValue match {
+        case Some(previous) => sparkConf.set(key, previous)
+        case None => sparkConf.remove(key)
+      }
+    }
+  }
+
   test("expand with nullable type not match") {
     val sql =
       """
diff --git a/cpp-ch/local-engine/Operator/AdvancedExpandStep.cpp 
b/cpp-ch/local-engine/Operator/AdvancedExpandStep.cpp
index c7729c26c5..e81e39ce29 100644
--- a/cpp-ch/local-engine/Operator/AdvancedExpandStep.cpp
+++ b/cpp-ch/local-engine/Operator/AdvancedExpandStep.cpp
@@ -248,7 +248,7 @@ void AdvancedExpandTransform::expandInputChunk()
             input_arg.column = input_column;
             input_arg.type = input_header->getByPosition(index).type;
             /// input_column maybe non-Nullable
-            columns[col_i] = DB::castColumn(input_arg, type);
+            columns[col_i] = DB::castColumn(input_arg, 
type)->convertToFullColumnIfConst();
         }
         else if (kind == EXPAND_FIELD_KIND_LITERAL)
         {
diff --git a/cpp-ch/local-engine/Operator/ExpandTransform.cpp 
b/cpp-ch/local-engine/Operator/ExpandTransform.cpp
index afc6596d55..e79076a762 100644
--- a/cpp-ch/local-engine/Operator/ExpandTransform.cpp
+++ b/cpp-ch/local-engine/Operator/ExpandTransform.cpp
@@ -114,7 +114,7 @@ void ExpandTransform::work()
             input_arg.column = input_column;
             input_arg.type = input_header.getByPosition(index).type;
             /// input_column maybe non-Nullable
-            columns[col_i] = DB::castColumn(input_arg, type);
+            columns[col_i] = DB::castColumn(input_arg, 
type)->convertToFullColumnIfConst();
         }
         else if (kind == EXPAND_FIELD_KIND_LITERAL)
         {
diff --git a/cpp-ch/local-engine/Parser/RelParsers/ExpandRelParser.cpp 
b/cpp-ch/local-engine/Parser/RelParsers/ExpandRelParser.cpp
index 4bbdba6f90..3515060a22 100644
--- a/cpp-ch/local-engine/Parser/RelParsers/ExpandRelParser.cpp
+++ b/cpp-ch/local-engine/Parser/RelParsers/ExpandRelParser.cpp
@@ -20,6 +20,7 @@
 #include <Columns/ColumnAggregateFunction.h>
 #include <Core/Block.h>
 #include <Core/ColumnWithTypeAndName.h>
+#include <DataTypes/DataTypeNullable.h>
 #include <Operator/AdvancedExpandStep.h>
 #include <Operator/ExpandStep.h>
 #include <Parser/RelParsers/RelParser.h>
@@ -113,6 +114,12 @@ ExpandField ExpandRelParser::buildExpandField(const 
DB::Block & header, const su
             else if (project_expr.has_literal())
             {
                 auto [type, field] = parseLiteral(project_expr.literal());
+                // A NULL literal is nullable even when the type carried by the
+                // Substrait literal does not explicitly encode nullability.
+                // Keep that information in the Expand output type so the
+                // generated column contains a real null map.
+                if (field.isNull() && type && !type->isNullable())
+                    type = DB::makeNullable(type);
                 kinds.push_back(ExpandFieldKind::EXPAND_FIELD_KIND_LITERAL);
                 fields.push_back(field);
                 updateType(types[i], type);


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

Reply via email to