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

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


The following commit(s) were added to refs/heads/main by this push:
     new d96709c4cc [CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL'
d96709c4cc is described below

commit d96709c4cc7ca962601317d0a70914ad95e306e1
Author: Xiong Duan <[email protected]>
AuthorDate: Thu May 15 18:59:32 2025 +0800

    [CALCITE-7019] Simplify 'NULL IN (20, 10)' to 'NULL'
---
 .../main/java/org/apache/calcite/plan/Strong.java  | 13 +++++
 .../org/apache/calcite/rex/RexProgramTest.java     | 65 ++++++++++++++++++++++
 2 files changed, 78 insertions(+)

diff --git a/core/src/main/java/org/apache/calcite/plan/Strong.java 
b/core/src/main/java/org/apache/calcite/plan/Strong.java
index 759ad69e9f..fa8fc6db61 100644
--- a/core/src/main/java/org/apache/calcite/plan/Strong.java
+++ b/core/src/main/java/org/apache/calcite/plan/Strong.java
@@ -21,11 +21,13 @@
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUnknownAs;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Sarg;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -36,6 +38,8 @@
 import java.util.List;
 import java.util.Map;
 
+import static java.util.Objects.requireNonNull;
+
 /** Utilities for strong predicates.
  *
  * <p>A predicate is strong (or null-rejecting) with regard to selected subset 
of inputs
@@ -237,6 +241,15 @@ public boolean isNull(RexNode node) {
         }
       }
       return allNull(caseValues);
+    case SEARCH:
+      final RexCall searchCall = (RexCall) node;
+      boolean isNull = isNull(searchCall.getOperands().get(0));
+      if (isNull) {
+        final Sarg<?> sarg =
+            requireNonNull(((RexLiteral) 
searchCall.getOperands().get(1)).getValueAs(Sarg.class));
+        return sarg.nullAs == RexUnknownAs.UNKNOWN;
+      }
+      return false;
     default:
       return false;
     }
diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
index ce8a0abb49..1224c63298 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
@@ -2217,6 +2217,71 @@ private void checkExponentialCnf(int n) {
     checkSimplify(e, "=(?0.int0, 10)");
   }
 
+  /** Unit test for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7019";>[CALCITE-7019]
+   * Simplify 'NULL IN (20, 10)' to 'NULL'</a>. */
+  @Test void testSimplifyIn() {
+    // NULL in (20, 10) ==> SEARCH(null:Integer, Sarg[10, 20])
+    //   ==>
+    // NULL
+    checkSimplify3_(in(nullInt, literal(20), literal(10)),
+        "null:BOOLEAN", "false", "true");
+
+    // NULL in (NULL, 10) ==> NULL = NULL or NULL = 10
+    //   ==>
+    // NULL
+    checkSimplify3_(in(nullInt, nullInt, literal(10)),
+        "null:BOOLEAN", "false", "true");
+
+    // 10 in (NULL, 10) ==> 10 = NULL or 10 = 10
+    //   ==>
+    // TRUE
+    checkSimplify(in(literal(10), nullInt, literal(10)), "true");
+
+    // 20 in (NULL, 10) ==> 20 = NULL or 20 = 10
+    //   ==>
+    // NULL
+    checkSimplify3_(in(literal(20), nullInt, literal(10)),
+        "null:BOOLEAN", "false", "true");
+
+    // 10 in (NULL, NULL) ==> 10 = null
+    //   ==>
+    // NULL
+    checkSimplify3_(in(literal(10), nullInt, nullInt),
+        "null:BOOLEAN", "false", "true");
+  }
+
+  @Test void testSimplifyNotIn() {
+    // NULL not in (20, 10) ==> not(SEARCH(null:Integer, Sarg[10, 20]))
+    //   ==>
+    // NULL
+    checkSimplify3_(not(in(nullInt, literal(20), literal(10))),
+        "null:BOOLEAN", "false", "true");
+
+    // NULL not in (NULL, 10) ==> not(null = null or null = 10)
+    //   ==>
+    // NULL
+    checkSimplify3_(not(in(nullInt, nullInt, literal(10))),
+        "null:BOOLEAN", "false", "true");
+
+    // 10 not in (NULL, 10) ==> not(10 = null or 10 = 10)
+    //   ==>
+    // FALSE
+    checkSimplify(not(in(literal(10), nullInt, literal(10))), "false");
+
+    // 20 not in (NULL, 10) ==> not(20 = null or 20 = 10)
+    //   ==>
+    // NULL
+    checkSimplify3_(not(in(literal(20), nullInt, literal(10))),
+        "null:BOOLEAN", "false", "true");
+
+    // 10 not in (NULL, NULL) ==> not(10 = null)
+    //   ==>
+    // NULL
+    checkSimplify3_(not(in(literal(10), nullInt, nullInt)),
+        "null:BOOLEAN", "false", "true");
+  }
+
   @Test void testSimplifyInAnd() {
     // deptno in (20, 10) and deptno = 10
     //   ==>

Reply via email to