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 95f280f6da [GLUTEN-10134][VL] Preserve store assignment cast modes 
(`STORE_ASSIGNMENT_POLICY` defaults to ANSI) (#12051)
95f280f6da is described below

commit 95f280f6da1e7d2467ed5fa52218215e5dce2213
Author: Reema <[email protected]>
AuthorDate: Mon Aug 17 23:36:16 2026 +0200

    [GLUTEN-10134][VL] Preserve store assignment cast modes 
(`STORE_ASSIGNMENT_POLICY` defaults to ANSI) (#12051)
---
 .../apache/gluten/execution/VeloxInsertSuite.scala | 142 +++++++++++++++++++++
 .../functions/RegistrationAllFunctions.cc          |   2 +
 cpp/velox/substrait/SubstraitToVeloxExpr.cc        |  33 ++++-
 cpp/velox/tests/SparkFunctionTest.cc               |  33 +++++
 .../tests/Substrait2VeloxPlanConversionTest.cc     |   2 +-
 .../gluten/substrait/expression/CastNode.java      |  34 +++--
 .../substrait/expression/ExpressionBuilder.java    |   5 +
 .../expression/UnaryExpressionTransformer.scala    |  13 +-
 .../gluten/sql/shims/spark33/Spark33Shims.scala    |   9 ++
 .../gluten/sql/shims/spark34/Spark34Shims.scala    |   1 +
 .../gluten/sql/shims/spark35/Spark35Shims.scala    |   1 +
 11 files changed, 259 insertions(+), 16 deletions(-)

diff --git 
a/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxInsertSuite.scala
 
b/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxInsertSuite.scala
new file mode 100644
index 0000000000..55411b2e70
--- /dev/null
+++ 
b/backends-velox/src/test/scala/org/apache/gluten/execution/VeloxInsertSuite.scala
@@ -0,0 +1,142 @@
+/*
+ * 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.gluten.execution
+
+import org.apache.gluten.config.GlutenConfig
+
+import org.apache.spark.SparkConf
+import org.apache.spark.sql.{AnalysisException, Row}
+import org.apache.spark.sql.internal.SQLConf
+
+class VeloxInsertSuite extends VeloxWholeStageTransformerSuite {
+  override protected val resourcePath: String = "placeholder"
+  override protected val fileFormat: String = "parquet"
+
+  override protected def sparkConf: SparkConf = {
+    super.sparkConf
+      .set("spark.shuffle.manager", 
"org.apache.spark.shuffle.sort.ColumnarShuffleManager")
+      .set("spark.sql.shuffle.partitions", "1")
+      .set("spark.memory.offHeap.size", "2g")
+      .set("spark.unsafe.exceptionOnMemoryLeak", "true")
+  }
+
+  test("storeAssignmentPolicy default ANSI is independent from ANSI mode") {
+    withTable("store_assignment_ansi_src", "store_assignment_ansi") {
+      withSQLConf(SQLConf.ANSI_ENABLED.key -> "false") {
+        assert(SQLConf.get.storeAssignmentPolicy == 
SQLConf.StoreAssignmentPolicy.ANSI)
+
+        createTableWithValue("store_assignment_ansi_src", "STRING", 
"'2147483648'")
+        createTable("store_assignment_ansi", "INT")
+        assertUnsafeCastAnalysisException("STRING", "INT") {
+          insertIntoFrom("store_assignment_ansi", 
"store_assignment_ansi_src").collect()
+        }
+
+        withSQLConf(
+          SQLConf.STORE_ASSIGNMENT_POLICY.key -> 
SQLConf.StoreAssignmentPolicy.LEGACY.toString) {
+          val insert = insertIntoFrom("store_assignment_ansi", 
"store_assignment_ansi_src")
+          insert.collect()
+          checkGlutenPlan[ProjectExecTransformer](insert)
+          checkAnswer(spark.table("store_assignment_ansi"), Row(null))
+        }
+      }
+    }
+  }
+
+  test("storeAssignmentPolicy preserves configured cast modes") {
+    withSQLConf(SQLConf.ANSI_ENABLED.key -> "false") {
+      withTable("store_assignment_ansi_src", "store_assignment_ansi") {
+        createTableWithValue("store_assignment_ansi_src", "STRING", 
"'2147483648'")
+        createTable("store_assignment_ansi", "INT")
+
+        withSQLConf(
+          SQLConf.STORE_ASSIGNMENT_POLICY.key -> 
SQLConf.StoreAssignmentPolicy.ANSI.toString) {
+          assertUnsafeCastAnalysisException("STRING", "INT") {
+            insertIntoFrom("store_assignment_ansi", 
"store_assignment_ansi_src").collect()
+          }
+          checkAnswer(spark.table("store_assignment_ansi"), Seq.empty[Row])
+        }
+      }
+    }
+
+    withSQLConf(SQLConf.ANSI_ENABLED.key -> "true") {
+      withTable("store_assignment_legacy_src", "store_assignment_legacy") {
+        createTableWithValue("store_assignment_legacy_src", "STRING", 
"'2147483648'")
+        createTable("store_assignment_legacy", "INT")
+
+        // Disable the whole-plan ANSI fallback so the legacy store-assignment 
cast
+        // gets a chance to offload while the session runs in ANSI mode.
+        withSQLConf(
+          SQLConf.STORE_ASSIGNMENT_POLICY.key -> 
SQLConf.StoreAssignmentPolicy.LEGACY.toString,
+          GlutenConfig.GLUTEN_ANSI_FALLBACK_ENABLED.key -> "false"
+        ) {
+          val insert = insertIntoFrom("store_assignment_legacy", 
"store_assignment_legacy_src")
+          insert.collect()
+          checkGlutenPlan[ProjectExecTransformer](insert)
+          checkAnswer(spark.table("store_assignment_legacy"), Row(null))
+        }
+      }
+    }
+  }
+
+  test("storeAssignmentPolicy strict rejects unsafe insert casts") {
+    withTable("store_assignment_strict_src", "store_assignment_strict") {
+      withSQLConf(
+        SQLConf.STORE_ASSIGNMENT_POLICY.key -> 
SQLConf.StoreAssignmentPolicy.STRICT.toString) {
+        createTableWithValue("store_assignment_strict_src", "INT", "1")
+        createTable("store_assignment_strict", "TINYINT")
+
+        assertUnsafeCastAnalysisException("INT", "TINYINT") {
+          insertIntoFrom("store_assignment_strict", 
"store_assignment_strict_src").collect()
+        }
+        checkAnswer(spark.table("store_assignment_strict"), Seq.empty[Row])
+      }
+    }
+  }
+
+  private def createTable(table: String, dataType: String): Unit =
+    spark.sql(s"CREATE TABLE $table (c $dataType) USING PARQUET")
+
+  private def createTableWithValue(table: String, dataType: String, value: 
String): Unit = {
+    createTable(table, dataType)
+    spark.sql(s"INSERT INTO $table VALUES ($value)").collect()
+  }
+
+  private def insertIntoFrom(target: String, source: String) =
+    spark.sql(s"INSERT INTO $target SELECT c FROM $source")
+
+  private def assertUnsafeCastAnalysisException(
+      fromType: String,
+      toType: String)(f: => Unit): Unit = {
+    val exception = intercept[AnalysisException](f)
+    // Spark 3.3/3.4 report the types in lower case, e.g. "string to int". 
Since Spark 3.5,
+    // the types in the exception message are quoted in upper case, e.g. 
"STRING" to "INT".
+    // The case conversion can be removed once Spark 3.4 and earlier are no 
longer supported.
+    val message = exceptionMessages(exception).toLowerCase()
+    assert(message.contains(fromType.toLowerCase()), message)
+    assert(message.contains(toType.toLowerCase()), message)
+    assert(message.contains("cast"), message)
+  }
+
+  private def exceptionMessages(e: Throwable): String = {
+    val message = Option(e.getMessage).getOrElse("")
+    if (e.getCause == null) {
+      message
+    } else {
+      message + "\n" + exceptionMessages(e.getCause)
+    }
+  }
+}
diff --git a/cpp/velox/operators/functions/RegistrationAllFunctions.cc 
b/cpp/velox/operators/functions/RegistrationAllFunctions.cc
index 752953390d..49c9e3728c 100644
--- a/cpp/velox/operators/functions/RegistrationAllFunctions.cc
+++ b/cpp/velox/operators/functions/RegistrationAllFunctions.cc
@@ -33,6 +33,7 @@
 #include "velox/functions/sparksql/Rand.h"
 #include "velox/functions/sparksql/aggregates/Register.h"
 #include "velox/functions/sparksql/registration/Register.h"
+#include "velox/functions/sparksql/specialforms/SparkCastExpr.h"
 #include "velox/functions/sparksql/window/WindowFunctionsRegistration.h"
 
 using namespace facebook;
@@ -84,6 +85,7 @@ void registerFunctionOverwrite() {
 
 void registerAllFunctions() {
   velox::functions::sparksql::registerFunctions("");
+  velox::functions::sparksql::registerSparkCastModeSpecialForms();
   velox::aggregate::prestosql::registerAllAggregateFunctions(
       "", true /*registerCompanionFunctions*/, false /*onlyPrestoSignatures*/, 
true /*overwrite*/);
   velox::functions::aggregate::sparksql::registerAggregateFunctions(
diff --git a/cpp/velox/substrait/SubstraitToVeloxExpr.cc 
b/cpp/velox/substrait/SubstraitToVeloxExpr.cc
index 6ef99685c1..68a245c97b 100755
--- a/cpp/velox/substrait/SubstraitToVeloxExpr.cc
+++ b/cpp/velox/substrait/SubstraitToVeloxExpr.cc
@@ -17,6 +17,7 @@
 
 #include "SubstraitToVeloxExpr.h"
 #include "TypeUtils.h"
+#include "velox/functions/sparksql/specialforms/SparkCastExpr.h"
 #include "velox/type/Timestamp.h"
 #include "velox/vector/FlatVector.h"
 #include "velox/vector/VariantToVector.h"
@@ -24,6 +25,9 @@
 using namespace facebook::velox;
 
 namespace {
+constexpr const char* kSparkAnsiCast = "spark_ansi_cast";
+constexpr const char* kSparkLegacyCast = "spark_legacy_cast";
+
 ArrayVectorPtr makeArrayVector(const VectorPtr& elements) {
   BufferPtr offsets = allocateOffsets(1, elements->pool());
   BufferPtr sizes = allocateOffsets(1, elements->pool());
@@ -147,14 +151,20 @@ TypePtr getScalarType(const 
::substrait::Expression::Literal& literal) {
   }
 }
 
-/// Whether is try cast.
-bool isTryCast(::substrait::Expression::Cast::FailureBehavior failureBehavior) 
{
+enum class SparkCastMode {
+  kLegacy,
+  kAnsi,
+  kTry,
+};
+
+SparkCastMode sparkCastMode(::substrait::Expression::Cast::FailureBehavior 
failureBehavior) {
   switch (failureBehavior) {
     case 
::substrait::Expression_Cast_FailureBehavior_FAILURE_BEHAVIOR_UNSPECIFIED:
+      return SparkCastMode::kLegacy;
     case 
::substrait::Expression_Cast_FailureBehavior_FAILURE_BEHAVIOR_THROW_EXCEPTION:
-      return false;
+      return SparkCastMode::kAnsi;
     case 
::substrait::Expression_Cast_FailureBehavior_FAILURE_BEHAVIOR_RETURN_NULL:
-      return true;
+      return SparkCastMode::kTry;
     default:
       VELOX_NYI("The given failure behavior is NOT supported: '{}'", 
std::to_string(failureBehavior));
   }
@@ -581,7 +591,20 @@ core::TypedExprPtr 
SubstraitVeloxExprConverter::toVeloxExpr(
     const RowTypePtr& inputType) {
   auto type = SubstraitParser::parseType(castExpr.type());
   std::vector<core::TypedExprPtr> inputs{toVeloxExpr(castExpr.input(), 
inputType)};
-  return std::make_shared<core::CastTypedExpr>(type, inputs, 
isTryCast(castExpr.failure_behavior()));
+  switch (sparkCastMode(castExpr.failure_behavior())) {
+    case SparkCastMode::kLegacy:
+      return std::make_shared<const core::CallTypedExpr>(type, 
std::move(inputs), kSparkLegacyCast);
+    case SparkCastMode::kAnsi: {
+      const auto castName = 
functions::sparksql::SparkCastCallToSpecialForm::isAnsiSupported(inputs[0]->type(),
 type)
+          ? kSparkAnsiCast
+          : kSparkLegacyCast;
+      return std::make_shared<const core::CallTypedExpr>(type, 
std::move(inputs), castName);
+    }
+    case SparkCastMode::kTry:
+      return std::make_shared<core::CastTypedExpr>(type, std::move(inputs), 
true);
+    default:
+      VELOX_UNREACHABLE();
+  }
 }
 
 core::TypedExprPtr SubstraitVeloxExprConverter::toVeloxExpr(
diff --git a/cpp/velox/tests/SparkFunctionTest.cc 
b/cpp/velox/tests/SparkFunctionTest.cc
index ae5e7c48d8..ceb979a5ed 100644
--- a/cpp/velox/tests/SparkFunctionTest.cc
+++ b/cpp/velox/tests/SparkFunctionTest.cc
@@ -15,14 +15,27 @@
  * limitations under the License.
  */
 
+#include <string>
 #include <vector>
 
 #include "operators/functions/RegistrationAllFunctions.h"
+#include "velox/common/base/tests/GTestUtils.h"
+#include "velox/core/Expressions.h"
+#include "velox/functions/sparksql/SparkQueryConfig.h"
 #include "velox/functions/sparksql/tests/SparkFunctionBaseTest.h"
 
 using namespace facebook::velox::functions::sparksql::test;
 using namespace facebook::velox;
 
+namespace {
+constexpr const char* kSparkAnsiCast = "spark_ansi_cast";
+constexpr const char* kSparkLegacyCast = "spark_legacy_cast";
+
+std::string sparkAnsiEnabledConfigKey() {
+  return 
functions::sparksql::SparkQueryConfig::qualify(functions::sparksql::SparkQueryConfig::kAnsiEnabled);
+}
+} // namespace
+
 class SparkFunctionTest : public SparkFunctionBaseTest {
  public:
   SparkFunctionTest() {
@@ -111,3 +124,23 @@ TEST_F(SparkFunctionTest, roundWithDecimal) {
   runRoundWithDecimalTest<int16_t>(testRoundWithDecIntegralData<int16_t>());
   runRoundWithDecimalTest<int8_t>(testRoundWithDecIntegralData<int8_t>());
 }
+
+TEST_F(SparkFunctionTest, expressionLevelAnsiCastIgnoresSessionAnsiOff) {
+  queryCtx_->testingOverrideConfigUnsafe({{sparkAnsiEnabledConfigKey(), 
"false"}});
+  auto input = makeRowVector({makeFlatVector<std::string>({"2147483648"})});
+  core::TypedExprPtr field = std::make_shared<const 
core::FieldAccessTypedExpr>(VARCHAR(), "c0");
+  auto ansiCast =
+      std::make_shared<const core::CallTypedExpr>(INTEGER(), 
std::vector<core::TypedExprPtr>{field}, kSparkAnsiCast);
+
+  VELOX_ASSERT_THROW(evaluate(ansiCast, input), "Cannot cast");
+}
+
+TEST_F(SparkFunctionTest, expressionLevelLegacyCastIgnoresSessionAnsiOn) {
+  queryCtx_->testingOverrideConfigUnsafe({{sparkAnsiEnabledConfigKey(), 
"true"}});
+  auto input = makeRowVector({makeFlatVector<int32_t>({1234567})});
+  core::TypedExprPtr field = std::make_shared<const 
core::FieldAccessTypedExpr>(INTEGER(), "c0");
+  auto legacyCast =
+      std::make_shared<const core::CallTypedExpr>(TINYINT(), 
std::vector<core::TypedExprPtr>{field}, kSparkLegacyCast);
+
+  facebook::velox::test::assertEqualVectors(makeFlatVector<int8_t>({-121}), 
evaluate(legacyCast, input));
+}
diff --git a/cpp/velox/tests/Substrait2VeloxPlanConversionTest.cc 
b/cpp/velox/tests/Substrait2VeloxPlanConversionTest.cc
index 0081bf3004..76fe6d79ec 100644
--- a/cpp/velox/tests/Substrait2VeloxPlanConversionTest.cc
+++ b/cpp/velox/tests/Substrait2VeloxPlanConversionTest.cc
@@ -269,7 +269,7 @@ TEST_F(Substrait2VeloxPlanConversionTest, ifthenTest) {
   // Convert to Velox PlanNode.
   auto planNode = planConverter_->toVeloxPlan(substraitPlan, 
std::vector<::substrait::ReadRel_LocalFiles>{split});
   ASSERT_EQ(
-      "-- Project[1][expressions: ] -> \n  -- TableScan[0][table: hive_table, 
remaining filter: 
(and(and(and(and(isnotnull(\"hd_vehicle_count\"),or(equalto(\"hd_buy_potential\",>10000),equalto(\"hd_buy_potential\",unknown))),greaterthan(\"hd_vehicle_count\",0)),if(greaterthan(\"hd_vehicle_count\",0),greaterthan(divide(cast(\"hd_dep_count\"
 as DOUBLE),cast(\"hd_vehicle_count\" as 
DOUBLE)),1.2))),isnotnull(\"hd_demo_sk\"))), data columns: 
ROW<hd_demo_sk:BIGINT,hd_buy_potential:VARCHAR,hd_d [...]
+      "-- Project[1][expressions: ] -> \n  -- TableScan[0][table: hive_table, 
remaining filter: 
(and(and(and(and(isnotnull(\"hd_vehicle_count\"),or(equalto(\"hd_buy_potential\",>10000),equalto(\"hd_buy_potential\",unknown))),greaterthan(\"hd_vehicle_count\",0)),if(greaterthan(\"hd_vehicle_count\",0),greaterthan(divide(spark_legacy_cast(\"hd_dep_count\"),spark_legacy_cast(\"hd_vehicle_count\")),1.2))),isnotnull(\"hd_demo_sk\"))),
 data columns: ROW<hd_demo_sk:BIGINT,hd_buy_potential:VARCHA [...]
       planNode->toString(true, true));
 }
 
diff --git 
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/CastNode.java
 
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/CastNode.java
index 1984c44d74..6a501c0b8c 100644
--- 
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/CastNode.java
+++ 
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/CastNode.java
@@ -23,15 +23,25 @@ import io.substrait.proto.Expression;
 import java.io.Serializable;
 
 public class CastNode implements ExpressionNode, Serializable {
+  public enum CastMode {
+    LEGACY,
+    ANSI,
+    TRY
+  }
+
   private final TypeNode typeNode;
   private final ExpressionNode expressionNode;
 
-  public final boolean isTryCast;
+  public final CastMode castMode;
 
   CastNode(TypeNode typeNode, ExpressionNode expressionNode, boolean 
isTryCast) {
+    this(typeNode, expressionNode, isTryCast ? CastMode.TRY : CastMode.ANSI);
+  }
+
+  CastNode(TypeNode typeNode, ExpressionNode expressionNode, CastMode 
castMode) {
     this.typeNode = typeNode;
     this.expressionNode = expressionNode;
-    this.isTryCast = isTryCast;
+    this.castMode = castMode;
   }
 
   @Override
@@ -39,12 +49,20 @@ public class CastNode implements ExpressionNode, 
Serializable {
     Expression.Cast.Builder castBuilder = Expression.Cast.newBuilder();
     castBuilder.setType(typeNode.toProtobuf());
     castBuilder.setInput(expressionNode.toProtobuf());
-    if (!isTryCast) {
-      // Throw exception on failure.
-      castBuilder.setFailureBehaviorValue(2);
-    } else {
-      // Return null on failure.
-      castBuilder.setFailureBehaviorValue(1);
+    switch (castMode) {
+      case ANSI:
+        // Throw exception on failure.
+        castBuilder.setFailureBehaviorValue(2);
+        break;
+      case TRY:
+        // Return null on failure.
+        castBuilder.setFailureBehaviorValue(1);
+        break;
+      case LEGACY:
+        // Leave failure behavior unspecified to preserve Spark legacy cast 
semantics.
+        break;
+      default:
+        throw new IllegalStateException("Unsupported cast mode: " + castMode);
     }
     Expression.Builder builder = Expression.newBuilder();
     builder.setCast(castBuilder.build());
diff --git 
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/ExpressionBuilder.java
 
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/ExpressionBuilder.java
index 4bdef37878..9d5b5c9a10 100644
--- 
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/ExpressionBuilder.java
+++ 
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/expression/ExpressionBuilder.java
@@ -242,6 +242,11 @@ public class ExpressionBuilder {
     return new CastNode(typeNode, expressionNode, isTryCast);
   }
 
+  public static CastNode makeCast(
+      TypeNode typeNode, ExpressionNode expressionNode, CastNode.CastMode 
castMode) {
+    return new CastNode(typeNode, expressionNode, castMode);
+  }
+
   public static StringMapNode makeStringMap(Map<String, String> values) {
     return new StringMapNode(values);
   }
diff --git 
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/UnaryExpressionTransformer.scala
 
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/UnaryExpressionTransformer.scala
index 1c0faf599b..4e5e82bcbb 100644
--- 
a/gluten-substrait/src/main/scala/org/apache/gluten/expression/UnaryExpressionTransformer.scala
+++ 
b/gluten-substrait/src/main/scala/org/apache/gluten/expression/UnaryExpressionTransformer.scala
@@ -22,7 +22,7 @@ import org.apache.gluten.sql.shims.SparkShimLoader
 import org.apache.gluten.substrait.`type`.ListNode
 import org.apache.gluten.substrait.`type`.MapNode
 import org.apache.gluten.substrait.SubstraitContext
-import org.apache.gluten.substrait.expression.{ExpressionBuilder, 
ExpressionNode, StructLiteralNode}
+import org.apache.gluten.substrait.expression.{CastNode, ExpressionBuilder, 
ExpressionNode, StructLiteralNode}
 
 import org.apache.spark.sql.catalyst.expressions._
 import org.apache.spark.sql.types._
@@ -45,10 +45,19 @@ case class CastTransformer(substraitExprName: String, 
child: ExpressionTransform
   extends UnaryExpressionTransformer {
   override def doTransform(context: SubstraitContext): ExpressionNode = {
     val typeNode = ConverterUtils.getTypeNode(dataType, original.nullable)
+    val sparkShims = SparkShimLoader.getSparkShims
+    // Store-assignment casts can carry EvalMode.ANSI even when session ANSI 
is disabled.
+    val castMode = if (sparkShims.withTryEvalMode(original)) {
+      CastNode.CastMode.TRY
+    } else if (sparkShims.withAnsiEvalMode(original)) {
+      CastNode.CastMode.ANSI
+    } else {
+      CastNode.CastMode.LEGACY
+    }
     ExpressionBuilder.makeCast(
       typeNode,
       child.doTransform(context),
-      SparkShimLoader.getSparkShims.withTryEvalMode(original))
+      castMode)
   }
 }
 
diff --git 
a/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
 
b/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
index 10160d6a5d..1208049c85 100644
--- 
a/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
+++ 
b/shims/spark33/src/main/scala/org/apache/gluten/sql/shims/spark33/Spark33Shims.scala
@@ -210,6 +210,15 @@ class Spark33Shims extends SparkShims {
     }
   }
 
+  override def withAnsiEvalMode(expr: Expression): Boolean = {
+    expr match {
+      // Use the cast's own flag rather than the session conf: 
store-assignment casts
+      // can carry ansiEnabled = false even when the session runs in ANSI mode.
+      case c: Cast => c.ansiEnabled
+      case _ => false
+    }
+  }
+
   override def createParquetFilters(
       conf: SQLConf,
       schema: MessageType,
diff --git 
a/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
 
b/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
index a102ba89e1..7cf3088003 100644
--- 
a/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
+++ 
b/shims/spark34/src/main/scala/org/apache/gluten/sql/shims/spark34/Spark34Shims.scala
@@ -451,6 +451,7 @@ class Spark34Shims extends SparkShims {
       case s: Subtract => s.evalMode == EvalMode.ANSI
       case d: Divide => d.evalMode == EvalMode.ANSI
       case m: Multiply => m.evalMode == EvalMode.ANSI
+      case c: Cast => c.evalMode == EvalMode.ANSI
       case i: IntegralDivide => i.evalMode == EvalMode.ANSI
       case _ => false
     }
diff --git 
a/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
 
b/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
index 1cbcad8a30..5e4286ac9b 100644
--- 
a/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
+++ 
b/shims/spark35/src/main/scala/org/apache/gluten/sql/shims/spark35/Spark35Shims.scala
@@ -490,6 +490,7 @@ class Spark35Shims extends SparkShims {
       case s: Subtract => s.evalMode == EvalMode.ANSI
       case d: Divide => d.evalMode == EvalMode.ANSI
       case m: Multiply => m.evalMode == EvalMode.ANSI
+      case c: Cast => c.evalMode == EvalMode.ANSI
       case i: IntegralDivide => i.evalMode == EvalMode.ANSI
       case _ => false
     }


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

Reply via email to