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

mbudiu pushed a commit to branch issue7070
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit c8595113e9fbd3c32575f00b6a1adafab24249c6
Author: Mihai Budiu <[email protected]>
AuthorDate: Thu Sep 25 15:41:50 2025 -0700

    Move predicate simplification into SqlToRelConverter
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../apache/calcite/plan/RelOptPredicateList.java   |  2 ++
 .../calcite/rel/metadata/RelMdPredicates.java      | 10 +------
 .../java/org/apache/calcite/rex/RexSimplify.java   | 21 +++++++++++++
 .../apache/calcite/sql2rel/SqlToRelConverter.java  | 15 ++++++++--
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  2 +-
 .../org/apache/calcite/test/RelOptRulesTest.java   |  2 +-
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 35 +++++-----------------
 7 files changed, 47 insertions(+), 40 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java 
b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
index 1f8a846160..17743a1607 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
@@ -95,6 +95,8 @@ public class RelOptPredicateList {
    * {@link #pulledUpPredicates}. */
   public final ImmutableMap<RexNode, RexNode> constantMap;
 
+  // Please keep this constructor private; if you add additional constructors,
+  // please check the invariants similar to this constructor.
   private RelOptPredicateList(ImmutableList<RexNode> pulledUpPredicates,
       ImmutableList<RexNode> leftInferredPredicates,
       ImmutableList<RexNode> rightInferredPredicates,
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index 3577b487bb..44b3f1c1d5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -320,17 +320,9 @@ public RelOptPredicateList getPredicates(Filter filter, 
RelMetadataQuery mq) {
     final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
     List<RexNode> predicates =
         
RexUtil.retainDeterministic(RelOptUtil.conjunctions(filter.getCondition()));
-    RelOptCluster cluster = filter.getCluster();
-    final RexExecutor executor =
-        Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR);
-    RexSimplify simplify = new RexSimplify(rexBuilder, 
RelOptPredicateList.EMPTY, executor);
-    // The RelOptPredicateList data structure requires this invariant: no 
comparisons with null
-    List<RexNode> simplified = predicates.stream()
-        .map(e -> simplify.simplifyComparisonWithNull(e, RexUnknownAs.FALSE))
-        .collect(Collectors.toList());
     return Util.first(inputInfo, RelOptPredicateList.EMPTY)
         .union(rexBuilder,
-            RelOptPredicateList.of(rexBuilder, simplified));
+            RelOptPredicateList.of(rexBuilder, predicates));
   }
 
   /**
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java 
b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index d1b6ae5d9a..7d435108c3 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -772,6 +772,27 @@ private <C extends Comparable<C>> RexNode 
simplifyComparison(RexCall e,
     return simplifyUsingPredicates(e2, clazz);
   }
 
+
+  /**
+   * If this RexNode is a comparison against NULL, return FALSE, otherwise 
return it unchanged.
+   * This is a static simplified version of the function below.
+   */
+  public static RexNode simplifyComparisonWithNull(RexNode e, RexBuilder 
rexBuilder) {
+    final RexSimplify.Comparison comparison = RexSimplify.Comparison.of(e);
+    if (comparison != null) {
+      boolean againstNull = comparison.literal.isNull();
+      // There is another possibility to check: in a comparison like 1 = null,
+      // the "non-literal" side of the Comparison can be null
+      if (comparison.ref instanceof RexLiteral) {
+        againstNull = againstNull || ((RexLiteral) comparison.ref).isNull();
+      }
+      if (againstNull) {
+        return rexBuilder.makeLiteral(false);
+      }
+    }
+    return e;
+  }
+
   /**
    * If this RexNode is a comparison against NULL, return a simplified form,
    * otherwise return it unchanged.
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 87d2b47201..b943a909e3 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -88,6 +88,7 @@
 import org.apache.calcite.rex.RexPatternFieldRef;
 import org.apache.calcite.rex.RexRangeRef;
 import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.rex.RexSimplify;
 import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexWindowBound;
@@ -217,6 +218,7 @@
 import java.util.function.BiFunction;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.ImmutableList.toImmutableList;
@@ -1112,6 +1114,16 @@ private static SqlNode reg(SqlValidatorScope scope, 
SqlNode e) {
     return e;
   }
 
+  private RexNode simplifyPredicate(RexNode predicate) {
+    final RexNode convertedWhere2 =
+        RexUtil.removeNullabilityCast(typeFactory, predicate);
+    List<RexNode> conjuncts = RelOptUtil.conjunctions(convertedWhere2);
+    List<RexNode> simplified = conjuncts.stream()
+        .map(e -> RexSimplify.simplifyComparisonWithNull(e, rexBuilder))
+        .collect(Collectors.toList());
+    return RexUtil.composeConjunction(rexBuilder, simplified);
+  }
+
   /**
    * Converts a WHERE clause.
    *
@@ -1127,8 +1139,7 @@ private void convertWhere(
     SqlNode newWhere = pushDownNotForIn(bb.scope, where);
     replaceSubQueries(bb, newWhere, RelOptUtil.Logic.UNKNOWN_AS_FALSE);
     final RexNode convertedWhere = bb.convertExpression(newWhere);
-    final RexNode convertedWhere2 =
-        RexUtil.removeNullabilityCast(typeFactory, convertedWhere);
+    final RexNode convertedWhere2 = simplifyPredicate(convertedWhere);
 
     // only allocate filter if the condition is not TRUE
     if (convertedWhere2.isAlwaysTrue()) {
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 306fc26f39..ebd8835ca3 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -10576,7 +10576,7 @@ private void checkLiteral2(String expression, String 
expected) {
             + "FROM (SELECT 1 AS \"additional_column\", 
\"product\".\"product_id\", \"product\".\"product_name\"\n"
             + "FROM \"foodmart\".\"product\"\n"
             + "LEFT JOIN \"foodmart\".\"product\" AS \"product0\" ON 
\"product\".\"product_id\" = \"product0\".\"product_id\") AS \"t\"\n"
-            + "WHERE \"product_name\" IS NOT NULL AND NOT EXISTS (SELECT *\n"
+            + "WHERE NOT EXISTS (SELECT *\n"
             + "FROM \"foodmart\".\"employee\"\n"
             + "WHERE \"employee_id\" = \"t\".\"additional_column\")";
     sql(sql).ok(expected);
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java 
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 73526b8c42..1bd14c3299 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -4832,7 +4832,7 @@ RelOptFixture checkDynamicFunctions(boolean 
treatDynamicCallsAsConstant) {
         + "where empno=10 and empno is not null";
     sql(sql)
         .withRule(CoreRules.FILTER_REDUCE_EXPRESSIONS)
-        .check();
+        .checkUnchanged();
   }
 
   @Test void testReduceConstantsNegated() {
diff --git 
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml 
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index bd9de19292..39ebf51d72 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -1994,13 +1994,7 @@ MultiJoin(joinFilter=[true], isFullOuterJoin=[false], 
joinTypes=[[RIGHT, INNER]]
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{0}])
-      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-      LogicalAggregate(group=[{0}])
-        LogicalProject(i=[true])
-          LogicalFilter(condition=[=($cor0.EMPNO, null)])
-            LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+  LogicalValues(tuples=[[]])
 ]]>
     </Resource>
     <Resource name="planAfter">
@@ -2016,13 +2010,7 @@ LogicalValues(tuples=[[]])
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{0}])
-      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-      LogicalAggregate(group=[{0}])
-        LogicalProject(i=[true])
-          LogicalFilter(condition=[=($cor0.EMPNO, null)])
-            LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+  LogicalValues(tuples=[[]])
 ]]>
     </Resource>
     <Resource name="planAfter">
@@ -11982,7 +11970,7 @@ and empno = 10 and mgr is null and empno = 10]]>
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalFilter(condition=[AND(=($7, 7), =($0, 10), IS NULL($3), =($0, 10))])
+  LogicalFilter(condition=[AND(=($7, 7), =($0, 10), IS NULL($3))])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -15364,7 +15352,7 @@ LogicalProject(EMPNO=[$0], DEPTNO=[$1])
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EXPR$0=[+(1, 2)], EXPR$1=[+($0, +(3, 4))], EXPR$2=[+(+(5, 6), 
$0)], EXPR$3=[null:INTEGER], EXPR$4=[CASE(IS NOT NULL(2), 2, null:INTEGER)], 
EXPR$5=[ROW(+(7, 8))])
-  LogicalFilter(condition=[AND(=($0, +(7, 8)), =($0, +(8, 7)), =($0, CASE(IS 
NOT NULL(2), 2, null:INTEGER)))])
+  LogicalFilter(condition=[AND(=($0, +(7, 8)), =($0, CASE(IS NOT NULL(2), 2, 
null:INTEGER)))])
     LogicalProject(DEPTNO=[$0], NAME=[$1], EMPNO=[$2], ENAME=[$3], JOB=[$4], 
MGR=[$5], HIREDATE=[$6], SAL=[$7], COMM=[$8], DEPTNO0=[$9], SLACKER=[$10])
       LogicalJoin(condition=[=($0, $11)], joinType=[inner])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
@@ -15555,7 +15543,7 @@ and empno = 10 and mgr is null and empno = 10]]>
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalFilter(condition=[AND(=($7, 7), =($7, 8), =($0, 10), IS NULL($3), 
=($0, 10))])
+  LogicalFilter(condition=[AND(=($7, 7), =($7, 8), =($0, 10), IS NULL($3))])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -15717,13 +15705,6 @@ where empno=10 and empno is not null]]>
     </Resource>
     <Resource name="planBefore">
       <![CDATA[
-LogicalProject(EMPNO=[$0])
-  LogicalFilter(condition=[AND(=($0, 10), IS NOT NULL($0))])
-    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-]]>
-    </Resource>
-    <Resource name="planAfter">
-      <![CDATA[
 LogicalProject(EMPNO=[$0])
   LogicalFilter(condition=[=($0, 10)])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -15737,7 +15718,7 @@ LogicalProject(EMPNO=[$0])
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0])
-  LogicalFilter(condition=[AND(=($0, 10), IS NULL($0))])
+  LogicalFilter(condition=[false])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -15814,7 +15795,7 @@ where n is null]]>
 LogicalProject(N=[$0])
   LogicalFilter(condition=[IS NULL($0)])
     LogicalProject(N=[$0])
-      LogicalFilter(condition=[AND(IS NULL($0), IS NULL($0))])
+      LogicalFilter(condition=[IS NULL($0)])
         LogicalProject(N=[null:INTEGER])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -15836,7 +15817,7 @@ LogicalProject(N=[$0])
       <![CDATA[
 LogicalAggregate(group=[{}], EXPR$0=[COUNT()])
   LogicalProject($f0=[1])
-    LogicalFilter(condition=[=(null, 1)])
+    LogicalFilter(condition=[false])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>

Reply via email to