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

cloud-fan 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 4a330ce9254a [SPARK-53454][SQL][FOLLOWUP] Parenthesize 
AlwaysTrue/AlwaysFalse SQL so it is valid as a nested operand
4a330ce9254a is described below

commit 4a330ce9254a8b0f5cd42109e3757fcef01a2a04
Author: Wenchen Fan <[email protected]>
AuthorDate: Tue Jun 2 19:46:18 2026 +0800

    [SPARK-53454][SQL][FOLLOWUP] Parenthesize AlwaysTrue/AlwaysFalse SQL so it 
is valid as a nested operand
    
    ### What changes were proposed in this pull request?
    Followup to https://github.com/apache/spark/pull/56085.
    
    That PR made `JDBCSQLBuilder.build()` emit `"1 = 1"` / `"1 = 0"` for 
`AlwaysTrue` / `AlwaysFalse` predicates. This change parenthesizes the output 
-- `"(1 = 1)"` / `"(1 = 0)"` -- so it remains valid SQL when the predicate is 
nested as an operand of a larger expression.
    
    ### Why are the changes needed?
    `AlwaysTrue` / `AlwaysFalse` can appear not only as a standalone `WHERE` 
predicate but also nested as an operand of a larger expression (e.g. when an 
equality against a boolean column is expanded to a null-aware form). With the 
bare `1 = 1`, the generated SQL inlines into invalid syntax such as `"a" = 1 = 
1` or `1 = 1 IS NOT NULL`, which databases reject (e.g. PostgreSQL: `ERROR: 
syntax error at or near "="`). Parenthesizing produces `"a" = (1 = 1)` / `(1 = 
1) IS NOT NULL`, which is v [...]
    
    ### Does this PR introduce _any_ user-facing change?
    Yes. JDBC pushed-filter SQL for `AlwaysTrue` / `AlwaysFalse` now uses `(1 = 
1)` / `(1 = 0)` instead of bare `1 = 1` / `1 = 0`. This is semantically 
identical and fixes queries that previously generated invalid SQL when these 
predicates were nested.
    
    ### How was this patch tested?
    Extended the existing unit test in `JDBCSuite` to also cover the 
nested-operand case (`"a" = (1 = 1)`). Verified the test fails without the fix 
(`"1 = 1"` not equal to `"(1 = 1)"`) and passes with it.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    Generated-by: Claude Code
    
    Closes #56263 from cloud-fan/SPARK-53454-followup.
    
    Authored-by: Wenchen Fan <[email protected]>
    Signed-off-by: Wenchen Fan <[email protected]>
    (cherry picked from commit 62cbb3ac4e47a2392cbb7b42cac1045be1d50f9b)
    Signed-off-by: Wenchen Fan <[email protected]>
---
 .../scala/org/apache/spark/sql/jdbc/JdbcDialects.scala  |  7 +++++--
 .../scala/org/apache/spark/sql/jdbc/JDBCSuite.scala     | 17 ++++++++++++++---
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git 
a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala 
b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala
index 3b5182721c29..a34d23512e99 100644
--- a/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala
+++ b/sql/core/src/main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala
@@ -402,9 +402,12 @@ abstract class JdbcDialect extends Serializable with 
Logging {
   private[jdbc] class JDBCSQLBuilder extends V2ExpressionSQLBuilder {
     // SPARK-53454: Produce portable SQL for AlwaysTrue/AlwaysFalse predicates.
     // Some databases (Oracle, DB2) do not support bare TRUE/FALSE in WHERE 
clauses.
+    // The result is parenthesized so it stays valid when nested as an operand 
of a
+    // larger expression (e.g. "a" = (1 = 1) or (1 = 1) IS NOT NULL), not just 
as a
+    // standalone WHERE predicate.
     override def build(expr: Expression): String = expr match {
-      case _: AlwaysTrue => "1 = 1"
-      case _: AlwaysFalse => "1 = 0"
+      case _: AlwaysTrue => "(1 = 1)"
+      case _: AlwaysFalse => "(1 = 0)"
       case _ => super.build(expr)
     }
 
diff --git a/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala 
b/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala
index e3263c3cf357..4a7771305994 100644
--- a/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala
+++ b/sql/core/src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala
@@ -35,7 +35,8 @@ import org.apache.spark.sql.catalyst.{analysis, 
TableIdentifier}
 import org.apache.spark.sql.catalyst.parser.CatalystSqlParser
 import org.apache.spark.sql.catalyst.plans.logical.ShowCreateTable
 import org.apache.spark.sql.catalyst.util.{CaseInsensitiveMap, 
CharVarcharUtils, DateTimeTestUtils}
-import org.apache.spark.sql.connector.expressions.filter.{AlwaysFalse, 
AlwaysTrue}
+import org.apache.spark.sql.connector.expressions.{Expression => V2Expression, 
FieldReference}
+import org.apache.spark.sql.connector.expressions.filter.{AlwaysFalse, 
AlwaysTrue, Predicate}
 import org.apache.spark.sql.execution.{DataSourceScanExec, ExtendedMode, 
ProjectExec}
 import org.apache.spark.sql.execution.command.{ExplainCommand, 
ShowCreateTableCommand}
 import org.apache.spark.sql.execution.datasources.{LogicalRelation, 
LogicalRelationWithTable}
@@ -895,8 +896,18 @@ class JDBCSuite extends SharedSparkSession {
 
   test("SPARK-53454: AlwaysTrue/AlwaysFalse compile to portable SQL in 
JDBCSQLBuilder") {
     val dialect = JdbcDialects.get("jdbc:")
-    assert(dialect.compileExpression(new AlwaysTrue).get === "1 = 1")
-    assert(dialect.compileExpression(new AlwaysFalse).get === "1 = 0")
+    assert(dialect.compileExpression(new AlwaysTrue).get === "(1 = 1)")
+    assert(dialect.compileExpression(new AlwaysFalse).get === "(1 = 0)")
+
+    // The result must stay valid when AlwaysTrue/AlwaysFalse is nested as an 
operand
+    // of a larger expression, not just as a standalone WHERE predicate. 
Without the
+    // surrounding parentheses the bare `1 = 1` would inline into invalid SQL 
such as
+    // `a = 1 = 1`.
+    val ref = FieldReference("a")
+    val eqTrue = new Predicate("=", Array[V2Expression](ref, new AlwaysTrue))
+    val eqFalse = new Predicate("=", Array[V2Expression](ref, new AlwaysFalse))
+    assert(dialect.compileExpression(eqTrue).get === "\"a\" = (1 = 1)")
+    assert(dialect.compileExpression(eqFalse).get === "\"a\" = (1 = 0)")
   }
 
   test("Dialect unregister") {


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

Reply via email to