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

philo-he 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 79df455ce8 [GLUTEN-13179][CORE] Fix NPE when a column has a null name 
(#13206)
79df455ce8 is described below

commit 79df455ce85ac4985ad67548254768e4f73fc167
Author: rdtr <[email protected]>
AuthorDate: Fri Oct 9 22:28:55 2026 -0700

    [GLUTEN-13179][CORE] Fix NPE when a column has a null name (#13206)
---
 .../gluten/execution/VeloxLocalTableScanSuite.scala   | 19 +++++++++++++++++++
 .../org/apache/gluten/expression/ConverterUtils.scala |  5 +++++
 .../gluten/expression/ConverterUtilsSuite.scala       | 13 ++++++++++++-
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git 
a/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxLocalTableScanSuite.scala
 
b/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxLocalTableScanSuite.scala
index cdb63cc782..068411bbd2 100644
--- 
a/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxLocalTableScanSuite.scala
+++ 
b/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxLocalTableScanSuite.scala
@@ -21,6 +21,9 @@ import org.apache.gluten.sql.shims.SparkShimLoader
 
 import org.apache.spark.SparkConf
 import org.apache.spark.sql.{DataFrame, Row}
+import org.apache.spark.sql.catalyst.expressions.AttributeReference
+import org.apache.spark.sql.catalyst.plans.logical.LocalRelation
+import org.apache.spark.sql.classic.ClassicDataset
 import org.apache.spark.sql.execution.LocalTableScanExec
 import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
 import org.apache.spark.sql.types._
@@ -71,6 +74,22 @@ class VeloxLocalTableScanSuite
     assertHasVeloxLocalTableScan(df)
   }
 
+  // Before Spark 4.2 (SPARK-57725), Spark itself fails to resolve a plan with 
a null-named
+  // attribute.
+  testWithMinSparkVersion("LocalTableScan with a null-named attribute", "4.2") 
{
+    val attrs = Seq(AttributeReference(null, IntegerType)(), 
AttributeReference("b", IntegerType)())
+    val relation = LocalRelation.fromExternalRows(attrs, Seq(Row(1, 2)))
+    // With and without offloading the LocalTableScan itself.
+    Seq("true", "false").foreach {
+      offload =>
+        withSQLConf("spark.gluten.sql.columnar.localTableScan" -> offload) {
+          val df = ClassicDataset.ofRows(spark, relation).select("b")
+          checkAnswer(df, Row(2))
+          checkGlutenPlan[ProjectExecTransformer](df)
+        }
+    }
+  }
+
   test("LocalTableScan with numeric types") {
     val schema = StructType(
       Seq(
diff --git 
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/ConverterUtils.scala
 
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/ConverterUtils.scala
index be5522721d..cf4803e37b 100644
--- 
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/ConverterUtils.scala
+++ 
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/ConverterUtils.scala
@@ -49,6 +49,11 @@ object ConverterUtils extends Logging {
   }
 
   def normalizeColName(name: String): String = {
+    // An attribute may carry a null name (e.g. built from a StructField with 
a null name,
+    // SPARK-57725). Map it to an empty name; generated column names stay 
unique via the exprId.
+    if (name == null) {
+      return ""
+    }
     val caseSensitive = SQLConf.get.caseSensitiveAnalysis
     if (caseSensitive) name else name.toLowerCase(Locale.ROOT)
   }
diff --git 
a/gluten-substrait/src/test/scala/org/apache/gluten/expression/ConverterUtilsSuite.scala
 
b/gluten-substrait/src/test/scala/org/apache/gluten/expression/ConverterUtilsSuite.scala
index 268538af15..bfa3b0ea1b 100644
--- 
a/gluten-substrait/src/test/scala/org/apache/gluten/expression/ConverterUtilsSuite.scala
+++ 
b/gluten-substrait/src/test/scala/org/apache/gluten/expression/ConverterUtilsSuite.scala
@@ -16,7 +16,8 @@
  */
 package org.apache.gluten.expression
 
-import org.apache.spark.sql.types.{DataType, TimestampType}
+import org.apache.spark.sql.catalyst.expressions.AttributeReference
+import org.apache.spark.sql.types.{DataType, IntegerType, TimestampType}
 
 import io.substrait.proto.Type
 import org.scalatest.funsuite.AnyFunSuiteLike
@@ -64,4 +65,14 @@ class ConverterUtilsSuite extends AnyFunSuiteLike {
         assert(parsedNullable === nullable)
     }
   }
+
+  test("column names for an attribute with a null name") {
+    // An attribute can have a null name, e.g. from a StructField built with a 
null name.
+    val nullNamed = AttributeReference(null, IntegerType)()
+    assert(ConverterUtils.genColumnNameWithoutExprId(nullNamed) === "")
+    assert(ConverterUtils.genColumnNameWithExprId(nullNamed) === 
s"#${nullNamed.exprId.id}")
+
+    val named = AttributeReference("B", IntegerType)()
+    assert(ConverterUtils.genColumnNameWithExprId(named) === 
s"b#${named.exprId.id}")
+  }
 }


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

Reply via email to