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

zml1206 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 cd37d7aef9 [VL] Fall back to vanilla Delta write for unsupported 
schemas (#12444)
cd37d7aef9 is described below

commit cd37d7aef914350f66f0faebf34bbd071f5cfbea
Author: Felipe Pessoto <[email protected]>
AuthorDate: Sun Jul 12 18:32:22 2026 -0700

    [VL] Fall back to vanilla Delta write for unsupported schemas (#12444)
    
    Velox cannot write every Spark type. When native Delta write is enabled, 
Gluten
    offloads certain Delta write commands to the native writer via 
OffloadDeltaCommand
    -- DataFrameWriter.save (append/overwrite), CREATE/REPLACE TABLE AS SELECT,
    UPDATE, DELETE and OPTIMIZE. The native write path inserts a 
RowToVeloxColumnarExec
    transition whose SparkArrowUtil.toArrowSchema call throws
    `UnsupportedOperationException: Unsupported data type: <type>` at runtime 
for any
    type it has no Arrow mapping for (VariantType is the motivating example). 
(Plain
    SQL INSERT INTO and MERGE are not offloaded and were unaffected.)
    
    Guard GlutenOptimisticTransaction.writeFiles with the backend's existing 
schema
    validator: if VeloxValidatorApi.validateSchema reports the input schema is 
not
    supported, delegate to super.writeFiles (the vanilla Delta write path) 
instead of
    offloading. This reuses the same check Velox already applies to scan 
schemas, so
    unsupported types fall back consistently before Arrow schema conversion 
rather
    than adding a one-off VariantType guard. Supported writes are unaffected, 
and the
    validator's reason is logged when it falls back.
    
    Adds DeltaVariantWriteSuite, which exercises offloaded writes: top-level
    and struct-nested variant columns via DataFrameWriter.save, plus an UPDATE.
---
 .../sql/delta/GlutenOptimisticTransaction.scala    | 15 +++-
 .../spark/sql/delta/DeltaVariantWriteSuite.scala   | 94 ++++++++++++++++++++++
 2 files changed, 108 insertions(+), 1 deletion(-)

diff --git 
a/backends-velox/src-delta40/main/scala/org/apache/spark/sql/delta/GlutenOptimisticTransaction.scala
 
b/backends-velox/src-delta40/main/scala/org/apache/spark/sql/delta/GlutenOptimisticTransaction.scala
index 7c78eea59a..a93e929f12 100644
--- 
a/backends-velox/src-delta40/main/scala/org/apache/spark/sql/delta/GlutenOptimisticTransaction.scala
+++ 
b/backends-velox/src-delta40/main/scala/org/apache/spark/sql/delta/GlutenOptimisticTransaction.scala
@@ -16,7 +16,7 @@
  */
 package org.apache.spark.sql.delta
 
-import org.apache.gluten.backendsapi.velox.VeloxBatchType
+import org.apache.gluten.backendsapi.velox.{VeloxBatchType, VeloxValidatorApi}
 import org.apache.gluten.extension.columnar.transition.Transitions
 
 import org.apache.spark.sql.{AnalysisException, Dataset}
@@ -48,6 +48,19 @@ class GlutenOptimisticTransaction(delegate: 
OptimisticTransaction)
       writeOptions: Option[DeltaOptions],
       isOptimize: Boolean,
       additionalConstraints: Seq[Constraint]): Seq[FileAction] = {
+    // Velox cannot write every Spark type: the native write path inserts a 
RowToVeloxColumnarExec
+    // transition whose SparkArrowUtil.toArrowSchema call throws (e.g. 
`Unsupported data type:
+    // variant`) for any type it has no Arrow mapping for. Reuse the backend's 
schema validator so
+    // such writes fall back to the vanilla Delta write path instead of 
failing at runtime.
+    val unsupportedReason = VeloxValidatorApi.validateSchema(inputData.schema)
+    unsupportedReason match {
+      case Some(reason) =>
+        logInfo(
+          s"Schema is not supported by Velox ($reason); falling back to the " +
+            "vanilla Delta write path.")
+        return super.writeFiles(inputData, writeOptions, isOptimize, 
additionalConstraints)
+      case None =>
+    }
     hasWritten = true
 
     val spark = inputData.sparkSession
diff --git 
a/backends-velox/src-delta40/test/scala/org/apache/spark/sql/delta/DeltaVariantWriteSuite.scala
 
b/backends-velox/src-delta40/test/scala/org/apache/spark/sql/delta/DeltaVariantWriteSuite.scala
new file mode 100644
index 0000000000..d7dcb3f7f3
--- /dev/null
+++ 
b/backends-velox/src-delta40/test/scala/org/apache/spark/sql/delta/DeltaVariantWriteSuite.scala
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.spark.sql.delta
+
+import org.apache.spark.sql.QueryTest
+import org.apache.spark.sql.Row
+import org.apache.spark.sql.delta.test.DeltaSQLCommandTest
+import org.apache.spark.sql.test.SharedSparkSession
+
+/**
+ * Velox has no Arrow representation for VariantType, so a Delta write that 
Gluten offloads to the
+ * native columnar writer (DataFrameWriter.save, UPDATE, ...) used to throw
+ * `UnsupportedOperationException: Unsupported data type: variant` at runtime.
+ * GlutenOptimisticTransaction now detects a schema Velox cannot write (such 
as one containing a
+ * variant) and delegates such writes to the vanilla Delta write path.
+ *
+ * These tests use write commands that Gluten targets for native offload 
(DataFrameWriter.save and
+ * UPDATE): without the fix they reach the native path and fail; with the fix 
they fall back to
+ * vanilla Delta and succeed. Plain INSERT INTO is never offloaded, so it 
would pass regardless of
+ * the fix and is intentionally not used here.
+ */
+class DeltaVariantWriteSuite
+  extends QueryTest
+  with SharedSparkSession
+  with DeltaSQLCommandTest {
+
+  test("write and read a top-level variant column") {
+    withTempDir {
+      dir =>
+        val path = dir.getCanonicalPath
+        spark
+          .range(3)
+          .selectExpr("'foo' AS s", "parse_json(cast(id + 99 AS string)) AS v")
+          .write
+          .format("delta")
+          .mode("overwrite")
+          .save(path)
+        checkAnswer(
+          spark.read.format("delta").load(path).selectExpr("s", "to_json(v)"),
+          Seq(Row("foo", "99"), Row("foo", "100"), Row("foo", "101")))
+    }
+  }
+
+  test("write and read a variant nested in a struct") {
+    withTempDir {
+      dir =>
+        val path = dir.getCanonicalPath
+        spark
+          .range(2)
+          .selectExpr(
+            "'foo' AS s",
+            "named_struct('inner', parse_json(cast(id + 99 AS string))) AS v")
+          .write
+          .format("delta")
+          .mode("overwrite")
+          .save(path)
+        checkAnswer(
+          spark.read.format("delta").load(path).selectExpr("s", 
"to_json(v.inner)"),
+          Seq(Row("foo", "99"), Row("foo", "100")))
+    }
+  }
+
+  test("update a table with a variant column") {
+    withTempDir {
+      dir =>
+        val path = dir.getCanonicalPath
+        spark
+          .range(3)
+          .selectExpr("cast(id AS int) AS id", "parse_json(cast(id AS string)) 
AS v")
+          .write
+          .format("delta")
+          .mode("overwrite")
+          .save(path)
+        sql(s"UPDATE delta.`$path` SET v = parse_json('123') WHERE id = 1")
+        checkAnswer(
+          spark.read.format("delta").load(path).selectExpr("id", "to_json(v)"),
+          Seq(Row(0, "0"), Row(1, "123"), Row(2, "2")))
+    }
+  }
+}


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

Reply via email to