This is an automated email from the ASF dual-hosted git repository.
sunchao pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new 2910e7fdcfe0 [SPARK-56840][SQL][FOLLOW-UP] Add a real NullIf repro test
2910e7fdcfe0 is described below
commit 2910e7fdcfe0c9a15654e1296a85e062c1da306a
Author: Chao Sun <[email protected]>
AuthorDate: Thu May 14 21:15:01 2026 -0700
[SPARK-56840][SQL][FOLLOW-UP] Add a real NullIf repro test
### What changes were proposed in this pull request?
Add a focused Catalyst regression test that constructs builtin `nullif`
with an unresolved nested field reference while
`ALWAYS_INLINE_COMMON_EXPR=true`. This reproduces the eager `left.dataType`
access that motivated SPARK-56840 and guards the fixed construction path
directly.
### Why are the changes needed?
The original SPARK-56840 fix was merged with an end-to-end repro that also
passes without the fix, so it does not prove the bug boundary. This follow-up
adds a real negative/positive regression test that fails before commit
`5949ab30b41` with `Invalid call to dataType on unresolved object` and passes
with the fix.
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
- `build/sbt 'catalyst/testOnly
org.apache.spark.sql.catalyst.expressions.NullExpressionsSuite -- -z "NullIf
accepts unresolved nested fields during inlined function construction"'`
- Verified the same focused test fails on pre-fix parent `9ab8e43a940` with
`UnresolvedException: Invalid call to dataType on unresolved object` and passes
on current `apache/master`.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Codex
Closes #55883 from sunchao/dev/chao/codex/nullif-real-repro.
Authored-by: Chao Sun <[email protected]>
Signed-off-by: Chao Sun <[email protected]>
(cherry picked from commit ffa3783fdf7caf3153b8c85a8bf4e5e4150a47c4)
Signed-off-by: Chao Sun <[email protected]>
---
.../catalyst/expressions/NullExpressionsSuite.scala | 18 ++++++++++++++++--
.../spark/sql/catalyst/optimizer/OptimizerSuite.scala | 2 +-
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/NullExpressionsSuite.scala
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/NullExpressionsSuite.scala
index bb4aed9b4002..5c19e69cdfa3 100644
---
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/NullExpressionsSuite.scala
+++
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/NullExpressionsSuite.scala
@@ -20,7 +20,8 @@ package org.apache.spark.sql.catalyst.expressions
import java.sql.Timestamp
import org.apache.spark.{SparkFunSuite, SparkRuntimeException}
-import org.apache.spark.sql.catalyst.analysis.SimpleAnalyzer
+import org.apache.spark.sql.catalyst.FunctionIdentifier
+import org.apache.spark.sql.catalyst.analysis.{FunctionRegistry,
SimpleAnalyzer, UnresolvedAttribute}
import org.apache.spark.sql.catalyst.expressions.codegen.CodegenContext
import org.apache.spark.sql.catalyst.expressions.objects.AssertNotNull
import org.apache.spark.sql.catalyst.plans.logical.{LocalRelation, Project}
@@ -143,7 +144,7 @@ class NullExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
assert(analyze(new Nvl(floatLit, doubleLit)).dataType == DoubleType)
}
- test("NullIf replacement preserves its data type before type coercion") {
+ test("SPARK-56840: NullIf replacement preserves its data type before type
coercion") {
Seq(true, false).foreach { alwaysInlineCommonExpr =>
withSQLConf(SQLConf.ALWAYS_INLINE_COMMON_EXPR.key ->
alwaysInlineCommonExpr.toString) {
val nullIf = new NullIf(Literal(1), Literal(1))
@@ -153,6 +154,19 @@ class NullExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
}
}
+ test(
+ "SPARK-56840: NullIf accepts unresolved nested fields during inlined
function construction") {
+ withSQLConf(SQLConf.ALWAYS_INLINE_COMMON_EXPR.key -> "true") {
+ val nullIf = FunctionRegistry.builtin.lookupFunction(
+ FunctionIdentifier("nullif"),
+ Seq(
+ UnresolvedAttribute(Seq("c", "provider")),
+ Lower(Literal("ERROR_MULTIPLE_PROVIDERS"))))
+
+ assert(nullIf.isInstanceOf[NullIf])
+ }
+ }
+
test("AtLeastNNonNulls") {
val mix = Seq(Literal("x"),
Literal.create(null, StringType),
diff --git
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/OptimizerSuite.scala
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/OptimizerSuite.scala
index 057e4ceaf0a0..57b9df6512b5 100644
---
a/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/OptimizerSuite.scala
+++
b/sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/optimizer/OptimizerSuite.scala
@@ -335,7 +335,7 @@ class OptimizerSuite extends PlanTest {
StructType(StructField("map", MapType(IntegerType, IntegerType, false),
false) :: Nil))
}
- test("NullIf typed null branch is replaced with a null literal") {
+ test("SPARK-56840: NullIf typed null branch is replaced with a null
literal") {
val optimizer = new SimpleTestOptimizer() {
override def defaultBatches: Seq[Batch] =
Batch("test", fixedPoint,
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]