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

xuzifu666 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 cc1dcc4892 [CALCITE-7665] Convert multi-value NOT IN to AND of 
NOT_EQUALS in SqlToRelConverter
cc1dcc4892 is described below

commit cc1dcc48925699d729e8e77d08526bc3c618f704
Author: Yu Xu <[email protected]>
AuthorDate: Sun Jul 26 11:28:16 2026 +0800

    [CALCITE-7665] Convert multi-value NOT IN to AND of NOT_EQUALS in 
SqlToRelConverter
---
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |  7 +++++
 .../apache/calcite/test/SqlToRelConverterTest.java | 10 ++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    |  4 +--
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 30 +++++++++++++++++++---
 4 files changed, 46 insertions(+), 5 deletions(-)

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 d5cdb29ade..828c3d94ab 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -1994,6 +1994,13 @@ public RelNode convertToSingleValueSubq(
     case ALL:
       return RexUtil.composeConjunction(rexBuilder, comparisons, true);
     case NOT_IN:
+      if (comparisons.stream().allMatch(c -> c.getKind() == SqlKind.EQUALS)) {
+        final List<RexNode> notEqualsComparisons =
+            Util.transform(comparisons,
+                c -> rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS,
+                    ((RexCall) c).getOperands()));
+        return RexUtil.composeConjunction(rexBuilder, notEqualsComparisons, 
false);
+      }
       return rexBuilder.makeCall(SqlStdOperatorTable.NOT,
           RexUtil.composeDisjunction(rexBuilder, comparisons));
     case IN:
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 8b75a495b7..4b2a764e66 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1286,6 +1286,16 @@ private void checkCallTypesConsistent(String sql) {
     sql(sql).ok();
   }
 
+  @Test void testNotInSingleValue() {
+    final String sql = "select * from emp where deptno not in (10)";
+    sql(sql).ok();
+  }
+
+  @Test void testNotInMultiValue() {
+    final String sql = "select * from emp where deptno not in (10, 20)";
+    sql(sql).ok();
+  }
+
   @Test void testAggFilterWithInSubQuery() {
     final String sql = "select\n"
         + "  count(*) filter (where empno in (select deptno from 
empnullables))\n"
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 33d72da4d3..35dc172427 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -6938,7 +6938,7 @@ join sales.emp e on e.deptno = d.deptno and d.deptno not 
in (4, 6)]]>
     <Resource name="planBefore">
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], 
JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], 
DEPTNO0=[$16], SLACKER0=[$17])
-  LogicalJoin(condition=[AND(=($16, $7), NOT(OR(=($7, 4), =($7, 6))))], 
joinType=[inner])
+  LogicalJoin(condition=[AND(=($16, $7), <>($7, 4), <>($7, 6))], 
joinType=[inner])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -6947,7 +6947,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
       <![CDATA[
 LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], 
JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], 
DEPTNO0=[$16], SLACKER0=[$17])
   LogicalJoin(condition=[=($16, $7)], joinType=[inner])
-    LogicalFilter(condition=[NOT(OR(=($7, 4), =($7, 6)))])
+    LogicalFilter(condition=[AND(<>($7, 4), <>($7, 6))])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
     LogicalFilter(condition=[SEARCH($7, Sarg[(-∞..4), (4..6), (6..+∞)])])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index ee3a20d4cd..f3c0134656 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -6318,7 +6318,7 @@ 
LogicalProject(EXPR$0=[COMPARE_STRINGS_OR_NUMERIC_VALUES(1, 1)])
     <Resource name="plan">
       <![CDATA[
 LogicalProject(EMPNO=[$0])
-  LogicalFilter(condition=[CASE(true, NOT(OR(=($7, 10), =($7, 20))), false, 
NOT(false), NOT(OR(=($7, 30), =($7, 40))))])
+  LogicalFilter(condition=[CASE(true, AND(<>($7, 10), <>($7, 20)), false, 
NOT(false), AND(<>($7, 30), <>($7, 40)))])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -6330,7 +6330,7 @@ LogicalProject(EMPNO=[$0])
     <Resource name="plan">
       <![CDATA[
 LogicalProject(EMPNO=[$0])
-  LogicalFilter(condition=[CASE(true, NOT(OR(=($7, 10), =($7, 20))), 
NOT(true))])
+  LogicalFilter(condition=[CASE(true, AND(<>($7, 10), <>($7, 20)), NOT(true))])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -6342,7 +6342,7 @@ LogicalProject(EMPNO=[$0])
     <Resource name="plan">
       <![CDATA[
 LogicalProject(EMPNO=[$0])
-  LogicalFilter(condition=[CASE(true, CAST(NOT(OR(=($7, 10), =($7, 
20)))):BOOLEAN, null:BOOLEAN)])
+  LogicalFilter(condition=[CASE(true, CAST(AND(<>($7, 10), <>($7, 
20))):BOOLEAN, null:BOOLEAN)])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
@@ -6362,6 +6362,30 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], SAL=[$
         LogicalProject($f0=[true])
           LogicalFilter(condition=[=($cor0.DEPTNO, $0)])
             LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testNotInMultiValue">
+    <Resource name="sql">
+      <![CDATA[select * from emp where deptno not in (10, 20)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+  LogicalFilter(condition=[AND(<>($7, 10), <>($7, 20))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testNotInSingleValue">
+    <Resource name="sql">
+      <![CDATA[select * from emp where deptno not in (10)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
+  LogicalFilter(condition=[<>($7, 10)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to