Repository: calcite Updated Branches: refs/heads/master 3a335bec7 -> 01be1ce69
[CALCITE-2071] Query with IN and OR in WHERE clause returns wrong result (Vineet Garg) Initial test case. (Volodymyr Vysotskyi) Add quidem tests; add a method to derive collations for EnumerableCalc, without which one of the added tests gets a failed assert when project is pushed through an EnumerableMergeJoin. (Julian Hyde) Close apache/calcite#575 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/01be1ce6 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/01be1ce6 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/01be1ce6 Branch: refs/heads/master Commit: 01be1ce69a5cafd8c60d528c28f1220554ee48c7 Parents: 3a335be Author: Vineet Garg <[email protected]> Authored: Mon Dec 11 16:09:00 2017 -0800 Committer: Julian Hyde <[email protected]> Committed: Wed Dec 13 17:13:44 2017 -0800 ---------------------------------------------------------------------- .../org/apache/calcite/plan/RelOptUtil.java | 2 +- .../calcite/rel/metadata/RelMdCollation.java | 6 ++ .../calcite/sql2rel/SqlToRelConverter.java | 12 ++++ .../java/org/apache/calcite/test/JdbcTest.java | 18 +++++ core/src/test/resources/sql/sub-query.iq | 72 ++++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/01be1ce6/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java index 5021569..e7e3b7a 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -546,7 +546,7 @@ public abstract class RelOptUtil { switch (logic) { case TRUE_FALSE_UNKNOWN: case UNKNOWN_AS_TRUE: - if (!containsNullableFields(seekRel)) { + if (notIn && !containsNullableFields(seekRel)) { logic = Logic.TRUE_FALSE; } } http://git-wip-us.apache.org/repos/asf/calcite/blob/01be1ce6/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java index 32596ed..b48e763 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java @@ -26,6 +26,7 @@ import org.apache.calcite.rel.RelCollationTraitDef; import org.apache.calcite.rel.RelCollations; import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.core.Calc; import org.apache.calcite.rel.core.Filter; import org.apache.calcite.rel.core.Join; import org.apache.calcite.rel.core.Project; @@ -149,6 +150,11 @@ public class RelMdCollation project(mq, project.getInput(), project.getProjects())); } + public ImmutableList<RelCollation> collations(Calc calc, + RelMetadataQuery mq) { + return ImmutableList.copyOf(calc(mq, calc.getInput(), calc.getProgram())); + } + public ImmutableList<RelCollation> collations(Values values, RelMetadataQuery mq) { return ImmutableList.copyOf( http://git-wip-us.apache.org/repos/asf/calcite/blob/01be1ce6/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java ---------------------------------------------------------------------- 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 e1b0d17..8326b02 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -1731,6 +1731,18 @@ public class SqlToRelConverter { break; } if (node instanceof SqlCall) { + switch (kind) { + // Do no change logic for AND, IN and NOT IN expressions; + // but do change logic for OR, NOT and others; + // EXISTS was handled already. + case AND: + case IN: + case NOT_IN: + break; + default: + logic = RelOptUtil.Logic.TRUE_FALSE_UNKNOWN; + break; + } for (SqlNode operand : ((SqlCall) node).getOperandList()) { if (operand != null) { // In the case of an IN expression, locate scalar http://git-wip-us.apache.org/repos/asf/calcite/blob/01be1ce6/core/src/test/java/org/apache/calcite/test/JdbcTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java index 2fd5d0a..e2effb8 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -725,6 +725,24 @@ public class JdbcTest { assertTrue(connection.isClosed()); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-2071">[CALCITE-2071] + * Query with IN and OR in WHERE clause returns wrong result</a>. + * More cases in sub-query.iq. */ + @Test public void testWhereInOr() { + final String sql = "select \"empid\"\n" + + "from \"hr\".\"emps\" t\n" + + "where (\"empid\" in (select \"empid\" from \"hr\".\"emps\")\n" + + " or \"empid\" in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n" + + " 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25))\n" + + "and \"empid\" in (100, 200, 150)"; + CalciteAssert.hr() + .query(sql) + .returnsUnordered("empid=100", + "empid=200", + "empid=150"); + } + /** Tests that a driver can be extended with its own parser and can execute * its own flavor of DDL. */ @Test public void testMockDdl() throws Exception { http://git-wip-us.apache.org/repos/asf/calcite/blob/01be1ce6/core/src/test/resources/sql/sub-query.iq ---------------------------------------------------------------------- diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq index f7b5a58..2cc03dd 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -685,4 +685,76 @@ OR EXISTS (select * from "scott".emp e where emp.deptno = e.deptno + 20); !ok +# [CALCITE-2071] Query with IN and OR in WHERE clause returns wrong result +select empno +from "scott".emp +where (empno in (select empno from "scott".emp) + or empno in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)) +and empno in (7876, 7698, 7900); ++-------+ +| EMPNO | ++-------+ +| 7698 | +| 7876 | +| 7900 | ++-------+ +(3 rows) + +!ok + +# Equivalent to above (by de Morgan's law) +select empno +from "scott".emp +where not (empno not in (select empno from "scott".emp) + and empno not in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)) +and empno in (7876, 7698, 7900); ++-------+ +| EMPNO | ++-------+ +| 7698 | +| 7876 | +| 7900 | ++-------+ +(3 rows) + +!ok + +# Not equivalent to above, but happens to have same result +select empno +from "scott".emp +where (empno = 12345 + or empno in (select empno from "scott".emp) + or not empno in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)) +and empno in (7876, 7698, 7900); ++-------+ +| EMPNO | ++-------+ +| 7698 | +| 7876 | +| 7900 | ++-------+ +(3 rows) + +!ok + +# Similar to above, but never suffered from [CALCITE-2071] because AND +select empno +from "scott".emp +where (empno in (select empno from "scott".emp) + and empno in (7876, 7698, 7900)) +and empno in (7876, 7698, 7900); ++-------+ +| EMPNO | ++-------+ +| 7698 | +| 7876 | +| 7900 | ++-------+ +(3 rows) + +!ok + # End sub-query.iq
