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

cloud-fan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 1062b5e0963e [SPARK-53454][SQL] Handle AlwaysTrue/AlwaysFalse in 
JDBCSQLBuilder
1062b5e0963e is described below

commit 1062b5e0963e2face450708cb5c50978779c3a1f
Author: Shrirang Mhalgi <[email protected]>
AuthorDate: Sun May 31 09:02:50 2026 +0800

    [SPARK-53454][SQL] Handle AlwaysTrue/AlwaysFalse in JDBCSQLBuilder
    
    ### What changes were proposed in this pull request?
    Handle `AlwaysTrue` and `AlwaysFalse` predicates in 
`JDBCSQLBuilder.build()` by producing `"1 = 1"` and `"1 = 0"` respectively.
    
    `AlwaysTrue`/`AlwaysFalse` implement `Literal<Boolean>`, so they previously 
matched the `Literal` branch and produced bare `"TRUE"`/`"FALSE"` strings via 
`visitLiteral()`. Some databases (Oracle, DB2) do not support these as boolean 
expressions in WHERE clauses.
    
    The fix is placed in `JDBCSQLBuilder` (not the base V2ExpressionSQLBuilder) 
so that only the JDBC wire path gets the portable form, while 
`ToStringSQLBuilder` continues emitting `TRUE/FALSE` for human-readable display 
(EXPLAIN, PushedFilters info).
    
    ### Why are the changes needed?
    When AQE simplifies predicates to `AlwaysTrue`/`AlwaysFalse` and they are 
pushed down to JDBC, the generated SQL contains bare `TRUE`/`FALSE` which fails 
on databases that don't support boolean literals.
    
    This is a fresh, simplified approach per reviewer feedback on the original 
PR #52198 (auto-closed due to inactivity) -  using `1=1`/`1=0` universally 
without a dialect-specific API.
    
    ### Does this PR introduce _any_ user-facing change?
    Yes. JDBC pushed filter SQL now uses `1 = 1`/`1 = 0` instead of 
`TRUE`/`FALSE` for `AlwaysTrue/AlwaysFalse` predicates. This fixes queries that 
previously failed on Oracle/DB2.
    
    ### How was this patch tested?
    - Added unit test in `JDBCSuite` (using NoopDialect to test 
`JDBCSQLBuilder` directly)
    - Full JDBC test suite passes
    
    ### Was this patch authored or co-authored using generative AI tooling?
    Yes.
    
    Closes #56085 from shrirangmhalgi/SPARK-53454-jdbc-always-true-false.
    
    Lead-authored-by: Shrirang Mhalgi <[email protected]>
    Co-authored-by: Wenchen Fan <[email protected]>
    Signed-off-by: Wenchen Fan <[email protected]>
---
 .../main/scala/org/apache/spark/sql/jdbc/JdbcDialects.scala    | 10 +++++++++-
 .../src/test/scala/org/apache/spark/sql/jdbc/JDBCSuite.scala   |  7 +++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

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 2c915c734b24..3b5182721c29 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
@@ -41,7 +41,7 @@ import 
org.apache.spark.sql.connector.catalog.functions.UnboundFunction
 import org.apache.spark.sql.connector.catalog.index.TableIndex
 import org.apache.spark.sql.connector.expressions.{Expression, Literal, 
NamedReference}
 import org.apache.spark.sql.connector.expressions.aggregate.AggregateFunc
-import org.apache.spark.sql.connector.expressions.filter.Predicate
+import org.apache.spark.sql.connector.expressions.filter.{AlwaysFalse, 
AlwaysTrue, Predicate}
 import org.apache.spark.sql.connector.util.V2ExpressionSQLBuilder
 import org.apache.spark.sql.errors.QueryCompilationErrors
 import org.apache.spark.sql.execution.datasources.jdbc.{DriverRegistry, 
JDBCOptions, JdbcOptionsInWrite, JdbcUtils}
@@ -400,6 +400,14 @@ 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.
+    override def build(expr: Expression): String = expr match {
+      case _: AlwaysTrue => "1 = 1"
+      case _: AlwaysFalse => "1 = 0"
+      case _ => super.build(expr)
+    }
+
     // Some dialects do not support boolean type and this convenient util 
function is
     // provided to generate SQL string without boolean values.
     protected def inputToSQLNoBool(input: Expression): String = input match {
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 f79e63921627..e3263c3cf357 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,6 +35,7 @@ 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.execution.{DataSourceScanExec, ExtendedMode, 
ProjectExec}
 import org.apache.spark.sql.execution.command.{ExplainCommand, 
ShowCreateTableCommand}
 import org.apache.spark.sql.execution.datasources.{LogicalRelation, 
LogicalRelationWithTable}
@@ -892,6 +893,12 @@ class JDBCSuite extends SharedSparkSession {
     assert(doCompileFilter(EqualTo("col0.nested", 3)).isEmpty)
   }
 
+  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")
+  }
+
   test("Dialect unregister") {
     JdbcDialects.unregisterDialect(H2Dialect())
     try {


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

Reply via email to