| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.calcite.test; |
| |
| import org.apache.calcite.prepare.Prepare; |
| import org.apache.calcite.rel.RelNode; |
| import org.apache.calcite.rel.RelVisitor; |
| import org.apache.calcite.rel.externalize.RelXmlWriter; |
| import org.apache.calcite.rel.type.RelDataType; |
| import org.apache.calcite.rel.type.RelDataTypeFactory; |
| import org.apache.calcite.sql.SqlExplainLevel; |
| import org.apache.calcite.sql.type.SqlTypeName; |
| import org.apache.calcite.sql2rel.SqlToRelConverter; |
| import org.apache.calcite.util.Bug; |
| import org.apache.calcite.util.Litmus; |
| import org.apache.calcite.util.TestUtil; |
| import org.apache.calcite.util.Util; |
| |
| import com.google.common.base.Function; |
| |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| |
| /** |
| * Unit test for {@link org.apache.calcite.sql2rel.SqlToRelConverter}. |
| */ |
| public class SqlToRelConverterTest extends SqlToRelTestBase { |
| //~ Methods ---------------------------------------------------------------- |
| |
| public SqlToRelConverterTest() { |
| super(); |
| } |
| |
| protected DiffRepository getDiffRepos() { |
| return DiffRepository.lookup(SqlToRelConverterTest.class); |
| } |
| |
| /** Sets the SQL statement for a test. */ |
| public final Sql sql(String sql) { |
| return new Sql(sql, true, true, tester, false, |
| SqlToRelConverter.Config.DEFAULT); |
| } |
| |
| protected final void check( |
| String sql, |
| String plan) { |
| sql(sql).convertsTo(plan); |
| } |
| |
| @Test public void testIntegerLiteral() { |
| final String sql = "select 1 from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testIntervalLiteralYearToMonth() { |
| final String sql = "select\n" |
| + " cast(empno as Integer) * (INTERVAL '1-1' YEAR TO MONTH)\n" |
| + "from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testIntervalLiteralHourToMinute() { |
| final String sql = "select\n" |
| + " cast(empno as Integer) * (INTERVAL '1:1' HOUR TO MINUTE)\n" |
| + "from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAliasList() { |
| final String sql = "select a + b from (\n" |
| + " select deptno, 1 as one, name from dept\n" |
| + ") as d(a, b, c)\n" |
| + "where c like 'X%'"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAliasList2() { |
| final String sql = "select * from (\n" |
| + " select a, b, c from (values (1, 2, 3)) as t (c, b, a)\n" |
| + ") join dept on dept.deptno = c\n" |
| + "order by c + a"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Tests that AND(x, AND(y, z)) gets flattened to AND(x, y, z). |
| */ |
| @Test public void testMultiAnd() { |
| final String sql = "select * from emp\n" |
| + "where deptno < 10\n" |
| + "and deptno > 5\n" |
| + "and (deptno = 8 or empno < 100)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinOn() { |
| final String sql = "SELECT * FROM emp\n" |
| + "JOIN dept on emp.deptno = dept.deptno"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-245">[CALCITE-245] |
| * Off-by-one translation of ON clause of JOIN</a>. |
| */ |
| @Test public void testConditionOffByOne() { |
| // Bug causes the plan to contain |
| // LogicalJoin(condition=[=($9, $9)], joinType=[inner]) |
| final String sql = "SELECT * FROM emp\n" |
| + "JOIN dept on emp.deptno + 0 = dept.deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testConditionOffByOneReversed() { |
| final String sql = "SELECT * FROM emp\n" |
| + "JOIN dept on dept.deptno = emp.deptno + 0"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinOnExpression() { |
| final String sql = "SELECT * FROM emp\n" |
| + "JOIN dept on emp.deptno + 1 = dept.deptno - 2"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinOnIn() { |
| final String sql = "select * from emp join dept\n" |
| + " on emp.deptno = dept.deptno and emp.empno in (1, 3)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinOnInSubQuery() { |
| final String sql = "select * from emp left join dept\n" |
| + "on emp.empno = 1\n" |
| + "or dept.deptno in (select deptno from emp where empno > 5)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testJoinOnExists() { |
| final String sql = "select * from emp left join dept\n" |
| + "on emp.empno = 1\n" |
| + "or exists (select deptno from emp where empno > dept.deptno + 5)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testJoinUsing() { |
| sql("SELECT * FROM emp JOIN dept USING (deptno)").ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-74">[CALCITE-74] |
| * JOIN ... USING fails in 3-way join with |
| * UnsupportedOperationException</a>. */ |
| @Test public void testJoinUsingThreeWay() { |
| final String sql = "select *\n" |
| + "from emp as e\n" |
| + "join dept as d using (deptno)\n" |
| + "join emp as e2 using (empno)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinUsingCompound() { |
| final String sql = "SELECT * FROM emp LEFT JOIN (" |
| + "SELECT *, deptno * 5 as empno FROM dept) " |
| + "USING (deptno,empno)"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-801">[CALCITE-801] |
| * NullPointerException using USING on table alias with column |
| * aliases</a>. */ |
| @Test public void testValuesUsing() { |
| final String sql = "select d.deptno, min(e.empid) as empid\n" |
| + "from (values (100, 'Bill', 1)) as e(empid, name, deptno)\n" |
| + "join (values (1, 'LeaderShip')) as d(deptno, name)\n" |
| + " using (deptno)\n" |
| + "group by d.deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinNatural() { |
| sql("SELECT * FROM emp NATURAL JOIN dept").ok(); |
| } |
| |
| @Test public void testJoinNaturalNoCommonColumn() { |
| final String sql = "SELECT *\n" |
| + "FROM emp NATURAL JOIN (SELECT deptno AS foo, name FROM dept) AS d"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinNaturalMultipleCommonColumn() { |
| final String sql = "SELECT *\n" |
| + "FROM emp\n" |
| + "NATURAL JOIN (SELECT deptno, name AS ename FROM dept) AS d"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinWithUnion() { |
| final String sql = "select grade\n" |
| + "from (select empno from emp union select deptno from dept),\n" |
| + " salgrade"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroup() { |
| sql("select deptno from emp group by deptno").ok(); |
| } |
| |
| @Test public void testGroupJustOneAgg() { |
| // just one agg |
| final String sql = |
| "select deptno, sum(sal) as sum_sal from emp group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupExpressionsInsideAndOut() { |
| // Expressions inside and outside aggs. Common sub-expressions should be |
| // eliminated: 'sal' always translates to expression #2. |
| final String sql = "select\n" |
| + " deptno + 4, sum(sal), sum(3 + sal), 2 * count(sal)\n" |
| + "from emp group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAggregateNoGroup() { |
| sql("select sum(deptno) from emp").ok(); |
| } |
| |
| @Test public void testGroupEmpty() { |
| sql("select sum(deptno) from emp group by ()").ok(); |
| } |
| |
| // Same effect as writing "GROUP BY deptno" |
| @Test public void testSingletonGroupingSet() { |
| sql("select sum(sal) from emp group by grouping sets (deptno)").ok(); |
| } |
| |
| @Test public void testGroupingSets() { |
| sql("select deptno, ename, sum(sal) from emp\n" |
| + "group by grouping sets ((deptno), (ename, deptno))\n" |
| + "order by 2").ok(); |
| } |
| |
| @Test public void testGroupingSetsProduct() { |
| // Example in SQL:2011: |
| // GROUP BY GROUPING SETS ((A, B), (C)), GROUPING SETS ((X, Y), ()) |
| // is transformed to |
| // GROUP BY GROUPING SETS ((A, B, X, Y), (A, B), (C, X, Y), (C)) |
| final String sql = "select 1\n" |
| + "from (values (0, 1, 2, 3, 4)) as t(a, b, c, x, y)\n" |
| + "group by grouping sets ((a, b), c), grouping sets ((x, y), ())"; |
| sql(sql).ok(); |
| } |
| |
| /** When the GROUPING function occurs with GROUP BY (effectively just one |
| * grouping set), we can translate it directly to 1. */ |
| @Test public void testGroupingFunctionWithGroupBy() { |
| final String sql = "select\n" |
| + " deptno, grouping(deptno), count(*), grouping(empno)\n" |
| + "from emp\n" |
| + "group by empno, deptno\n" |
| + "order by 2"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupingFunction() { |
| final String sql = "select\n" |
| + " deptno, grouping(deptno), count(*), grouping(empno)\n" |
| + "from emp\n" |
| + "group by rollup(empno, deptno)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * GROUP BY with duplicates |
| * |
| * <p>From SQL spec: |
| * <blockquote>NOTE 190 — That is, a simple <em>group by clause</em> that is |
| * not primitive may be transformed into a primitive <em>group by clause</em> |
| * by deleting all parentheses, and deleting extra commas as necessary for |
| * correct syntax. If there are no grouping columns at all (for example, |
| * GROUP BY (), ()), this is transformed to the canonical form GROUP BY (). |
| * </blockquote> */ |
| // Same effect as writing "GROUP BY ()" |
| @Test public void testGroupByWithDuplicates() { |
| sql("select sum(sal) from emp group by (), ()").ok(); |
| } |
| |
| /** GROUP BY with duplicate (and heavily nested) GROUPING SETS. */ |
| @Test public void testDuplicateGroupingSets() { |
| final String sql = "select sum(sal) from emp\n" |
| + "group by sal,\n" |
| + " grouping sets (deptno,\n" |
| + " grouping sets ((deptno, ename), ename),\n" |
| + " (ename)),\n" |
| + " ()"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupingSetsCartesianProduct() { |
| // Equivalent to (a, c), (a, d), (b, c), (b, d) |
| final String sql = "select 1\n" |
| + "from (values (1, 2, 3, 4)) as t(a, b, c, d)\n" |
| + "group by grouping sets (a, b), grouping sets (c, d)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupingSetsCartesianProduct2() { |
| final String sql = "select 1\n" |
| + "from (values (1, 2, 3, 4)) as t(a, b, c, d)\n" |
| + "group by grouping sets (a, (a, b)), grouping sets (c), d"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testRollupSimple() { |
| // a is nullable so is translated as just "a" |
| // b is not null, so is represented as 0 inside Aggregate, then |
| // using "CASE WHEN i$b THEN NULL ELSE b END" |
| final String sql = "select a, b, count(*) as c\n" |
| + "from (values (cast(null as integer), 2)) as t(a, b)\n" |
| + "group by rollup(a, b)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testRollup() { |
| // Equivalent to {(a, b), (a), ()} * {(c, d), (c), ()} |
| final String sql = "select 1\n" |
| + "from (values (1, 2, 3, 4)) as t(a, b, c, d)\n" |
| + "group by rollup(a, b), rollup(c, d)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testRollupTuples() { |
| // rollup(b, (a, d)) is (b, a, d), (b), () |
| final String sql = "select 1\n" |
| + "from (values (1, 2, 3, 4)) as t(a, b, c, d)\n" |
| + "group by rollup(b, (a, d))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testCube() { |
| // cube(a, b) is {(a, b), (a), (b), ()} |
| final String sql = "select 1\n" |
| + "from (values (1, 2, 3, 4)) as t(a, b, c, d)\n" |
| + "group by cube(a, b)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupingSetsWith() { |
| final String sql = "with t(a, b, c, d) as (values (1, 2, 3, 4))\n" |
| + "select 1 from t\n" |
| + "group by rollup(a, b), rollup(c, d)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testHaving() { |
| // empty group-by clause, having |
| final String sql = "select sum(sal + sal) from emp having sum(sal) > 10"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupBug281() { |
| // Dtbug 281 gives: |
| // Internal error: |
| // Type 'RecordType(VARCHAR(128) $f0)' has no field 'NAME' |
| final String sql = |
| "select name from (select name from dept group by name)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupBug281b() { |
| // Try to confuse it with spurious columns. |
| final String sql = "select name, foo from (\n" |
| + "select deptno, name, count(deptno) as foo\n" |
| + "from dept\n" |
| + "group by name, deptno, name)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testGroupByExpression() { |
| // This used to cause an infinite loop, |
| // SqlValidatorImpl.getValidatedNodeType |
| // calling getValidatedNodeTypeIfKnown |
| // calling getValidatedNodeType. |
| final String sql = "select count(*)\n" |
| + "from emp\n" |
| + "group by substring(ename FROM 1 FOR 1)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAggDistinct() { |
| final String sql = "select deptno, sum(sal), sum(distinct sal), count(*)\n" |
| + "from emp\n" |
| + "group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAggFilter() { |
| final String sql = "select\n" |
| + " deptno, sum(sal * 2) filter (where empno < 10), count(*)\n" |
| + "from emp\n" |
| + "group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testFakeStar() { |
| sql("SELECT * FROM (VALUES (0, 0)) AS T(A, \"*\")").ok(); |
| } |
| |
| @Test public void testSelectDistinct() { |
| sql("select distinct sal + 5 from emp").ok(); |
| } |
| |
| @Test public void testSelectDistinctGroup() { |
| sql("select distinct sum(sal) from emp group by deptno").ok(); |
| } |
| |
| /** |
| * Tests that if the clause of SELECT DISTINCT contains duplicate |
| * expressions, they are only aggregated once. |
| */ |
| @Test public void testSelectDistinctDup() { |
| final String sql = |
| "select distinct sal + 5, deptno, sal + 5 from emp where deptno < 10"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSelectWithoutFrom() { |
| final String sql = "select 2+2"; |
| sql(sql).ok(); |
| } |
| |
| /** Tests referencing columns from a sub-query that has duplicate column |
| * names. I think the standard says that this is illegal. We roll with it, |
| * and rename the second column to "e0". */ |
| @Test public void testDuplicateColumnsInSubQuery() { |
| String sql = "select \"e\" from (\n" |
| + "select empno as \"e\", deptno as d, 1 as \"e\" from EMP)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrder() { |
| final String sql = "select empno from emp order by empno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderDescNullsLast() { |
| final String sql = "select empno from emp order by empno desc nulls last"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByOrdinalDesc() { |
| // FRG-98 |
| if (!tester.getConformance().isSortByOrdinal()) { |
| return; |
| } |
| final String sql = |
| "select empno + 1, deptno, empno from emp order by 2 desc"; |
| sql(sql).ok(); |
| |
| // ordinals rounded down, so 2.5 should have same effect as 2, and |
| // generate identical plan |
| final String sql2 = |
| "select empno + 1, deptno, empno from emp order by 2.5 desc"; |
| sql(sql2).ok(); |
| } |
| |
| @Test public void testOrderDistinct() { |
| // The relexp aggregates by 3 expressions - the 2 select expressions |
| // plus the one to sort on. A little inefficient, but acceptable. |
| final String sql = "select distinct empno, deptno + 1\n" |
| + "from emp order by deptno + 1 + empno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByNegativeOrdinal() { |
| // Regardless of whether sort-by-ordinals is enabled, negative ordinals |
| // are treated like ordinary numbers. |
| final String sql = |
| "select empno + 1, deptno, empno from emp order by -1 desc"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByOrdinalInExpr() { |
| // Regardless of whether sort-by-ordinals is enabled, ordinals |
| // inside expressions are treated like integers. |
| final String sql = |
| "select empno + 1, deptno, empno from emp order by 1 + 2 desc"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByIdenticalExpr() { |
| // Expression in ORDER BY clause is identical to expression in SELECT |
| // clause, so plan should not need an extra project. |
| final String sql = |
| "select empno + 1 from emp order by deptno asc, empno + 1 desc"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByAlias() { |
| final String sql = |
| "select empno + 1 as x, empno - 2 as y from emp order by y"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByAliasInExpr() { |
| final String sql = "select empno + 1 as x, empno - 2 as y\n" |
| + "from emp order by y + 3"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByAliasOverrides() { |
| if (!tester.getConformance().isSortByAlias()) { |
| return; |
| } |
| |
| // plan should contain '(empno + 1) + 3' |
| final String sql = "select empno + 1 as empno, empno - 2 as y\n" |
| + "from emp order by empno + 3"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderByAliasDoesNotOverride() { |
| if (tester.getConformance().isSortByAlias()) { |
| return; |
| } |
| |
| // plan should contain 'empno + 3', not '(empno + 1) + 3' |
| final String sql = "select empno + 1 as empno, empno - 2 as y\n" |
| + "from emp order by empno + 3"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderBySameExpr() { |
| final String sql = "select empno from emp, dept\n" |
| + "order by sal + empno desc, sal * empno, sal + empno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderUnion() { |
| final String sql = "select empno, sal from emp\n" |
| + "union all\n" |
| + "select deptno, deptno from dept\n" |
| + "order by sal desc, empno asc"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderUnionOrdinal() { |
| if (!tester.getConformance().isSortByOrdinal()) { |
| return; |
| } |
| final String sql = "select empno, sal from emp\n" |
| + "union all\n" |
| + "select deptno, deptno from dept\n" |
| + "order by 2"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderUnionExprs() { |
| final String sql = "select empno, sal from emp\n" |
| + "union all\n" |
| + "select deptno, deptno from dept\n" |
| + "order by empno * sal + 2"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderOffsetFetch() { |
| final String sql = "select empno from emp\n" |
| + "order by empno offset 10 rows fetch next 5 rows only"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOffsetFetch() { |
| final String sql = "select empno from emp\n" |
| + "offset 10 rows fetch next 5 rows only"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOffset() { |
| final String sql = "select empno from emp offset 10 rows"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testFetch() { |
| final String sql = "select empno from emp fetch next 5 rows only"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-439">[CALCITE-439] |
| * SqlValidatorUtil.uniquify() may not terminate under some |
| * conditions</a>. */ |
| @Test public void testGroupAlias() { |
| final String sql = "select \"$f2\", max(x), max(x + 1)\n" |
| + "from (values (1, 2)) as t(\"$f2\", x)\n" |
| + "group by \"$f2\""; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOrderGroup() { |
| final String sql = "select deptno, count(*)\n" |
| + "from emp\n" |
| + "group by deptno\n" |
| + "order by deptno * sum(sal) desc, min(empno)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testCountNoGroup() { |
| final String sql = "select count(*), sum(sal)\n" |
| + "from emp\n" |
| + "where empno > 10"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWith() { |
| final String sql = "with emp2 as (select * from emp)\n" |
| + "select * from emp2"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-309">[CALCITE-309] |
| * WITH ... ORDER BY query gives AssertionError</a>. */ |
| @Test public void testWithOrder() { |
| final String sql = "with emp2 as (select * from emp)\n" |
| + "select * from emp2 order by deptno"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWithUnionOrder() { |
| final String sql = "with emp2 as (select empno, deptno as x from emp)\n" |
| + "select * from emp2\n" |
| + "union all\n" |
| + "select * from emp2\n" |
| + "order by empno + x"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWithUnion() { |
| final String sql = "with emp2 as (select * from emp where deptno > 10)\n" |
| + "select empno from emp2 where deptno < 30\n" |
| + "union all\n" |
| + "select deptno from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWithAlias() { |
| final String sql = "with w(x, y) as\n" |
| + " (select * from dept where deptno > 10)\n" |
| + "select x from w where x < 30 union all select deptno from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWithInsideWhereExists() { |
| final String sql = "select * from emp\n" |
| + "where exists (\n" |
| + " with dept2 as (select * from dept where dept.deptno >= emp.deptno)\n" |
| + " select 1 from dept2 where deptno <= emp.deptno)"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testWithInsideWhereExistsRex() { |
| final String sql = "select * from emp\n" |
| + "where exists (\n" |
| + " with dept2 as (select * from dept where dept.deptno >= emp.deptno)\n" |
| + " select 1 from dept2 where deptno <= emp.deptno)"; |
| sql(sql).decorrelate(false).expand(false).ok(); |
| } |
| |
| @Test public void testWithInsideWhereExistsDecorrelate() { |
| final String sql = "select * from emp\n" |
| + "where exists (\n" |
| + " with dept2 as (select * from dept where dept.deptno >= emp.deptno)\n" |
| + " select 1 from dept2 where deptno <= emp.deptno)"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| @Test public void testWithInsideWhereExistsDecorrelateRex() { |
| final String sql = "select * from emp\n" |
| + "where exists (\n" |
| + " with dept2 as (select * from dept where dept.deptno >= emp.deptno)\n" |
| + " select 1 from dept2 where deptno <= emp.deptno)"; |
| sql(sql).decorrelate(true).expand(false).ok(); |
| } |
| |
| @Test public void testWithInsideScalarSubQuery() { |
| final String sql = "select (\n" |
| + " with dept2 as (select * from dept where deptno > 10)" |
| + " select count(*) from dept2) as c\n" |
| + "from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testWithInsideScalarSubQueryRex() { |
| final String sql = "select (\n" |
| + " with dept2 as (select * from dept where deptno > 10)" |
| + " select count(*) from dept2) as c\n" |
| + "from emp"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testTableExtend() { |
| final String sql = "select * from dept extend (x varchar(5) not null)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testExplicitTable() { |
| sql("table emp").ok(); |
| } |
| |
| @Test public void testCollectionTable() { |
| sql("select * from table(ramp(3))").ok(); |
| } |
| |
| @Test public void testSample() { |
| final String sql = |
| "select * from emp tablesample substitute('DATASET1') where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSampleQuery() { |
| final String sql = "select * from (\n" |
| + " select * from emp as e tablesample substitute('DATASET1')\n" |
| + " join dept on e.deptno = dept.deptno\n" |
| + ") tablesample substitute('DATASET2')\n" |
| + "where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSampleBernoulli() { |
| final String sql = |
| "select * from emp tablesample bernoulli(50) where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSampleBernoulliQuery() { |
| final String sql = "select * from (\n" |
| + " select * from emp as e tablesample bernoulli(10) repeatable(1)\n" |
| + " join dept on e.deptno = dept.deptno\n" |
| + ") tablesample bernoulli(50) repeatable(99)\n" |
| + "where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSampleSystem() { |
| final String sql = |
| "select * from emp tablesample system(50) where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSampleSystemQuery() { |
| final String sql = "select * from (\n" |
| + " select * from emp as e tablesample system(10) repeatable(1)\n" |
| + " join dept on e.deptno = dept.deptno\n" |
| + ") tablesample system(50) repeatable(99)\n" |
| + "where empno > 5"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testCollectionTableWithCursorParam() { |
| final String sql = "select * from table(dedup(" |
| + "cursor(select ename from emp)," |
| + " cursor(select name from dept), 'NAME'))"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testUnnest() { |
| final String sql = "select*from unnest(multiset[1,2])"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUnnestSubQuery() { |
| final String sql = "select*from unnest(multiset(select*from dept))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUnnestArray() { |
| sql("select*from unnest(array(select*from dept))").ok(); |
| } |
| |
| @Test public void testUnnestWithOrdinality() { |
| final String sql = |
| "select*from unnest(array(select*from dept)) with ordinality"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testMultisetSubQuery() { |
| final String sql = |
| "select multiset(select deptno from dept) from (values(true))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testMultiset() { |
| final String sql = "select 'a',multiset[10] from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testMultisetOfColumns() { |
| final String sql = "select 'abc',multiset[deptno,sal] from emp"; |
| sql(sql).expand(true).ok(); |
| } |
| |
| @Test public void testMultisetOfColumnsRex() { |
| sql("select 'abc',multiset[deptno,sal] from emp").ok(); |
| } |
| |
| @Test public void testCorrelationJoin() { |
| final String sql = "select *,\n" |
| + " multiset(select * from emp where deptno=dept.deptno) as empset\n" |
| + "from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testCorrelationJoinRex() { |
| final String sql = "select *,\n" |
| + " multiset(select * from emp where deptno=dept.deptno) as empset\n" |
| + "from dept"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-864">[CALCITE-864] |
| * Correlation variable has incorrect row type if it is populated by right |
| * side of a Join</a>. */ |
| @Test public void testCorrelatedSubQueryInJoin() { |
| final String sql = "select *\n" |
| + "from emp as e\n" |
| + "join dept as d using (deptno)\n" |
| + "where d.name = (\n" |
| + " select max(name)\n" |
| + " from dept as d2\n" |
| + " where d2.deptno = d.deptno)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testExists() { |
| final String sql = "select*from emp\n" |
| + "where exists (select 1 from dept where deptno=55)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testExistsCorrelated() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testNotExistsCorrelated() { |
| final String sql = "select * from emp where not exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testExistsCorrelatedDecorrelate() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| @Test public void testExistsCorrelatedDecorrelateRex() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(true).expand(false).ok(); |
| } |
| |
| @Test public void testExistsCorrelatedLimit() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno limit 1)"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testExistsCorrelatedLimitDecorrelate() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno limit 1)"; |
| sql(sql).decorrelate(true).expand(true).ok(); |
| } |
| |
| @Test public void testExistsCorrelatedLimitDecorrelateRex() { |
| final String sql = "select*from emp where exists (\n" |
| + " select 1 from dept where emp.deptno=dept.deptno limit 1)"; |
| sql(sql).decorrelate(true).expand(false).ok(); |
| } |
| |
| @Test public void testInValueListShort() { |
| final String sql = "select empno from emp where deptno in (10, 20)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInValueListLong() { |
| // Go over the default threshold of 20 to force a subQuery. |
| final String sql = "select empno from emp where deptno in" |
| + " (10, 20, 30, 40, 50, 60, 70, 80, 90, 100" |
| + ", 110, 120, 130, 140, 150, 160, 170, 180, 190" |
| + ", 200, 210, 220, 230)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInUncorrelatedSubQuery() { |
| final String sql = "select empno from emp where deptno in" |
| + " (select deptno from dept)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInUncorrelatedSubQueryRex() { |
| final String sql = "select empno from emp where deptno in" |
| + " (select deptno from dept)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testCompositeInUncorrelatedSubQueryRex() { |
| final String sql = "select empno from emp where (empno, deptno) in" |
| + " (select deptno - 10, deptno from dept)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testNotInUncorrelatedSubQuery() { |
| final String sql = "select empno from emp where deptno not in" |
| + " (select deptno from dept)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testNotInUncorrelatedSubQueryRex() { |
| final String sql = "select empno from emp where deptno not in" |
| + " (select deptno from dept)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testWhereInCorrelated() { |
| final String sql = "select empno from emp as e\n" |
| + "join dept as d using (deptno)\n" |
| + "where e.sal in (\n" |
| + " select e2.sal from emp as e2 where e2.deptno > e.deptno)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testInUncorrelatedSubQueryInSelect() { |
| // In the SELECT clause, the value of IN remains in 3-valued logic |
| // -- it's not forced into 2-valued by the "... IS TRUE" wrapper as in the |
| // WHERE clause -- so the translation is more complicated. |
| final String sql = "select name, deptno in (\n" |
| + " select case when true then deptno else null end from emp)\n" |
| + "from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInUncorrelatedSubQueryInSelectRex() { |
| // In the SELECT clause, the value of IN remains in 3-valued logic |
| // -- it's not forced into 2-valued by the "... IS TRUE" wrapper as in the |
| // WHERE clause -- so the translation is more complicated. |
| final String sql = "select name, deptno in (\n" |
| + " select case when true then deptno else null end from emp)\n" |
| + "from dept"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testInUncorrelatedSubQueryInHavingRex() { |
| final String sql = "select sum(sal) as s\n" |
| + "from emp\n" |
| + "group by deptno\n" |
| + "having count(*) > 2\n" |
| + "and deptno in (\n" |
| + " select case when true then deptno else null end from emp)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testUncorrelatedScalarSubQueryInOrderRex() { |
| final String sql = "select ename\n" |
| + "from emp\n" |
| + "order by (select case when true then deptno else null end from emp) desc,\n" |
| + " ename"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testUncorrelatedScalarSubQueryInGroupOrderRex() { |
| final String sql = "select sum(sal) as s\n" |
| + "from emp\n" |
| + "group by deptno\n" |
| + "order by (select case when true then deptno else null end from emp) desc,\n" |
| + " count(*)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testUncorrelatedScalarSubQueryInAggregateRex() { |
| final String sql = "select sum((select min(deptno) from emp)) as s\n" |
| + "from emp\n" |
| + "group by deptno\n"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| /** Plan should be as {@link #testInUncorrelatedSubQueryInSelect}, but with |
| * an extra NOT. Both queries require 3-valued logic. */ |
| @Test public void testNotInUncorrelatedSubQueryInSelect() { |
| final String sql = "select empno, deptno not in (\n" |
| + " select case when true then deptno else null end from dept)\n" |
| + "from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testNotInUncorrelatedSubQueryInSelectRex() { |
| final String sql = "select empno, deptno not in (\n" |
| + " select case when true then deptno else null end from dept)\n" |
| + "from emp"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| /** Since 'deptno NOT IN (SELECT deptno FROM dept)' can not be null, we |
| * generate a simpler plan. */ |
| @Test public void testNotInUncorrelatedSubQueryInSelectNotNull() { |
| final String sql = "select empno, deptno not in (\n" |
| + " select deptno from dept)\n" |
| + "from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testNotInUncorrelatedSubQueryInSelectNotNullRex() { |
| final String sql = "select empno, deptno not in (\n" |
| + " select deptno from dept)\n" |
| + "from emp"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testUnnestSelect() { |
| final String sql = "select*from unnest(select multiset[deptno] from dept)"; |
| sql(sql).expand(true).ok(); |
| } |
| |
| @Test public void testUnnestSelectRex() { |
| final String sql = "select*from unnest(select multiset[deptno] from dept)"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testJoinUnnest() { |
| final String sql = "select*from dept as d, unnest(multiset[d.deptno * 2])"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testJoinUnnestRex() { |
| final String sql = "select*from dept as d, unnest(multiset[d.deptno * 2])"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testLateral() { |
| final String sql = "select * from emp,\n" |
| + " LATERAL (select * from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testLateralDecorrelate() { |
| final String sql = "select * from emp,\n" |
| + " LATERAL (select * from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(true).expand(true).ok(); |
| } |
| |
| @Test public void testLateralDecorrelateRex() { |
| final String sql = "select * from emp,\n" |
| + " LATERAL (select * from dept where emp.deptno=dept.deptno)"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| @Test public void testNestedCorrelations() { |
| final String sql = "select *\n" |
| + "from (select 2+deptno d2, 3+deptno d3 from emp) e\n" |
| + " where exists (select 1 from (select deptno+1 d1 from dept) d\n" |
| + " where d1=e.d2 and exists (select 2 from (select deptno+4 d4, deptno+5 d5, deptno+6 d6 from dept)\n" |
| + " where d4=d.d1 and d5=d.d1 and d6=e.d3))"; |
| sql(sql).decorrelate(false).ok(); |
| } |
| |
| @Test public void testNestedCorrelationsDecorrelated() { |
| final String sql = "select *\n" |
| + "from (select 2+deptno d2, 3+deptno d3 from emp) e\n" |
| + " where exists (select 1 from (select deptno+1 d1 from dept) d\n" |
| + " where d1=e.d2 and exists (select 2 from (select deptno+4 d4, deptno+5 d5, deptno+6 d6 from dept)\n" |
| + " where d4=d.d1 and d5=d.d1 and d6=e.d3))"; |
| sql(sql).decorrelate(true).expand(true).ok(); |
| } |
| |
| @Test public void testNestedCorrelationsDecorrelatedRex() { |
| final String sql = "select *\n" |
| + "from (select 2+deptno d2, 3+deptno d3 from emp) e\n" |
| + " where exists (select 1 from (select deptno+1 d1 from dept) d\n" |
| + " where d1=e.d2 and exists (select 2 from (select deptno+4 d4, deptno+5 d5, deptno+6 d6 from dept)\n" |
| + " where d4=d.d1 and d5=d.d1 and d6=e.d3))"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| @Test public void testElement() { |
| sql("select element(multiset[5]) from emp").ok(); |
| } |
| |
| @Test public void testElementInValues() { |
| sql("values element(multiset[5])").ok(); |
| } |
| |
| @Test public void testUnionAll() { |
| final String sql = |
| "select empno from emp union all select deptno from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUnion() { |
| final String sql = |
| "select empno from emp union select deptno from dept"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUnionValues() { |
| // union with values |
| final String sql = "values (10), (20)\n" |
| + "union all\n" |
| + "select 34 from emp\n" |
| + "union all values (30), (45 + 10)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUnionSubQuery() { |
| // union of subQuery, inside from list, also values |
| final String sql = "select deptno from emp as emp0 cross join\n" |
| + " (select empno from emp union all\n" |
| + " select deptno from dept where deptno > 20 union all\n" |
| + " values (45), (67))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testIsDistinctFrom() { |
| final String sql = "select 1 is distinct from 2 from (values(true))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testIsNotDistinctFrom() { |
| final String sql = "select 1 is not distinct from 2 from (values(true))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testNotLike() { |
| // note that 'x not like y' becomes 'not(x like y)' |
| final String sql = "values ('a' not like 'b' escape 'c')"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOverMultiple() { |
| final String sql = "select sum(sal) over w1,\n" |
| + " sum(deptno) over w1,\n" |
| + " sum(deptno) over w2\n" |
| + "from emp\n" |
| + "where deptno - sal > 999\n" |
| + "window w1 as (partition by job order by hiredate rows 2 preceding),\n" |
| + " w2 as (partition by job order by hiredate rows 3 preceding disallow partial),\n" |
| + " w3 as (partition by job order by hiredate range interval '1' second preceding)"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-750">[CALCITE-750] |
| * Allow windowed aggregate on top of regular aggregate</a>. */ |
| @Test public void testNestedAggregates() { |
| final String sql = "SELECT\n" |
| + " avg(sum(sal) + 2 * min(empno) + 3 * avg(empno))\n" |
| + " over (partition by deptno)\n" |
| + "from emp\n" |
| + "group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test one of the custom conversions which is recognized by the class of the |
| * operator (in this case, |
| * {@link org.apache.calcite.sql.fun.SqlCaseOperator}). |
| */ |
| @Test public void testCase() { |
| sql("values (case 'a' when 'a' then 1 end)").ok(); |
| } |
| |
| /** |
| * Tests one of the custom conversions which is recognized by the identity |
| * of the operator (in this case, |
| * {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#CHARACTER_LENGTH}). |
| */ |
| @Test public void testCharLength() { |
| // Note that CHARACTER_LENGTH becomes CHAR_LENGTH. |
| sql("values (character_length('foo'))").ok(); |
| } |
| |
| @Test public void testOverAvg() { |
| // AVG(x) gets translated to SUM(x)/COUNT(x). Because COUNT controls |
| // the return type there usually needs to be a final CAST to get the |
| // result back to match the type of x. |
| final String sql = "select sum(sal) over w1,\n" |
| + " avg(sal) over w1\n" |
| + "from emp\n" |
| + "window w1 as (partition by job order by hiredate rows 2 preceding)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOverAvg2() { |
| // Check to see if extra CAST is present. Because CAST is nested |
| // inside AVG it passed to both SUM and COUNT so the outer final CAST |
| // isn't needed. |
| final String sql = "select sum(sal) over w1,\n" |
| + " avg(CAST(sal as real)) over w1\n" |
| + "from emp\n" |
| + "window w1 as (partition by job order by hiredate rows 2 preceding)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testOverCountStar() { |
| final String sql = "select count(sal) over w1,\n" |
| + " count(*) over w1\n" |
| + "from emp\n" |
| + "window w1 as (partition by job order by hiredate rows 2 preceding)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Tests that a window containing only ORDER BY is implicitly CURRENT ROW. |
| */ |
| @Test public void testOverOrderWindow() { |
| final String sql = "select last_value(deptno) over w\n" |
| + "from emp\n" |
| + "window w as (order by empno)"; |
| sql(sql).ok(); |
| |
| // Same query using inline window |
| final String sql2 = "select last_value(deptno) over (order by empno)\n" |
| + "from emp\n"; |
| sql(sql2).ok(); |
| } |
| |
| /** |
| * Tests that a window with a FOLLOWING bound becomes BETWEEN CURRENT ROW |
| * AND FOLLOWING. |
| */ |
| @Test public void testOverOrderFollowingWindow() { |
| // Window contains only ORDER BY (implicitly CURRENT ROW). |
| final String sql = "select last_value(deptno) over w\n" |
| + "from emp\n" |
| + "window w as (order by empno rows 2 following)"; |
| sql(sql).ok(); |
| |
| // Same query using inline window |
| final String sql2 = "select\n" |
| + " last_value(deptno) over (order by empno rows 2 following)\n" |
| + "from emp\n"; |
| sql(sql2).ok(); |
| } |
| |
| @Test public void testInterval() { |
| // temporarily disabled per DTbug 1212 |
| if (!Bug.DT785_FIXED) { |
| return; |
| } |
| final String sql = |
| "values(cast(interval '1' hour as interval hour to second))"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testStream() { |
| final String sql = |
| "select stream productId from orders where productId = 10"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testStreamGroupBy() { |
| final String sql = "select stream\n" |
| + " floor(rowtime to second) as rowtime, count(*) as c\n" |
| + "from orders\n" |
| + "group by floor(rowtime to second)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testStreamWindowedAggregation() { |
| final String sql = "select stream *,\n" |
| + " count(*) over (partition by productId\n" |
| + " order by rowtime\n" |
| + " range interval '1' second preceding) as c\n" |
| + "from orders"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testExplainAsXml() { |
| String sql = "select 1 + 2, 3 from (values (true))"; |
| final RelNode rel = tester.convertSqlToRel(sql).rel; |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| RelXmlWriter planWriter = |
| new RelXmlWriter(pw, SqlExplainLevel.EXPPLAN_ATTRIBUTES); |
| rel.explain(planWriter); |
| pw.flush(); |
| TestUtil.assertEqualsVerbose( |
| "<RelNode type=\"LogicalProject\">\n" |
| + "\t<Property name=\"EXPR$0\">\n" |
| + "\t\t+(1, 2)\t</Property>\n" |
| + "\t<Property name=\"EXPR$1\">\n" |
| + "\t\t3\t</Property>\n" |
| + "\t<Inputs>\n" |
| + "\t\t<RelNode type=\"LogicalValues\">\n" |
| + "\t\t\t<Property name=\"tuples\">\n" |
| + "\t\t\t\t[{ true }]\t\t\t</Property>\n" |
| + "\t\t\t<Inputs/>\n" |
| + "\t\t</RelNode>\n" |
| + "\t</Inputs>\n" |
| + "</RelNode>\n", |
| Util.toLinux(sw.toString())); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-412">[CALCITE-412] |
| * RelFieldTrimmer: when trimming Sort, the collation and trait set don't |
| * match</a>. */ |
| @Test public void testSortWithTrim() { |
| final String sql = "select ename from (select * from emp order by sal) a"; |
| sql(sql).trim(true).ok(); |
| } |
| |
| @Test public void testOffset0() { |
| final String sql = "select * from emp offset 0"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test group-by CASE expression involving a non-query IN |
| */ |
| @Test public void testGroupByCaseSubQuery() { |
| final String sql = "SELECT CASE WHEN emp.empno IN (3) THEN 0 ELSE 1 END\n" |
| + "FROM emp\n" |
| + "GROUP BY (CASE WHEN emp.empno IN (3) THEN 0 ELSE 1 END)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test aggregate function on a CASE expression involving a non-query IN |
| */ |
| @Test public void testAggCaseSubQuery() { |
| final String sql = |
| "SELECT SUM(CASE WHEN empno IN (3) THEN 0 ELSE 1 END) FROM emp"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-753">[CALCITE-753] |
| * Test aggregate operators do not derive row types with duplicate column |
| * names</a>. */ |
| @Test public void testAggNoDuplicateColumnNames() { |
| final String sql = "SELECT empno, EXPR$2, COUNT(empno) FROM (\n" |
| + " SELECT empno, deptno AS EXPR$2\n" |
| + " FROM emp)\n" |
| + "GROUP BY empno, EXPR$2"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testAggScalarSubQuery() { |
| final String sql = "SELECT SUM(SELECT min(deptno) FROM dept) FROM emp"; |
| sql(sql).ok(); |
| } |
| |
| /** Test aggregate function on a CASE expression involving IN with a |
| * sub-query. |
| * |
| * <p>Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-551">[CALCITE-551] |
| * Sub-query inside aggregate function</a>. |
| */ |
| @Test public void testAggCaseInSubQuery() { |
| final String sql = "SELECT SUM(\n" |
| + " CASE WHEN deptno IN (SELECT deptno FROM dept) THEN 1 ELSE 0 END)\n" |
| + "FROM emp"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| @Test public void testCorrelatedSubQueryInAggregate() { |
| final String sql = "SELECT SUM(\n" |
| + " (select char_length(name) from dept\n" |
| + " where dept.deptno = emp.empno))\n" |
| + "FROM emp"; |
| sql(sql).expand(false).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-614">[CALCITE-614] |
| * IN within CASE within GROUP BY gives AssertionError</a>. |
| */ |
| @Test public void testGroupByCaseIn() { |
| final String sql = "select\n" |
| + " (CASE WHEN (deptno IN (10, 20)) THEN 0 ELSE deptno END),\n" |
| + " min(empno) from EMP\n" |
| + "group by (CASE WHEN (deptno IN (10, 20)) THEN 0 ELSE deptno END)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInsert() { |
| final String sql = |
| "insert into emp (deptno, empno, ename) values (10, 150, 'Fred')"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testDelete() { |
| final String sql = "delete from emp"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testDeleteWhere() { |
| final String sql = "delete from emp where deptno = 10"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUpdate() { |
| final String sql = "update emp set empno = empno + 1"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUpdateSubQuery() { |
| final String sql = "update emp\n" |
| + "set empno = (\n" |
| + " select min(empno) from emp as e where e.deptno = emp.deptno)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testUpdateWhere() { |
| final String sql = "update emp set empno = empno + 1 where deptno = 10"; |
| sql(sql).ok(); |
| } |
| |
| @Ignore("CALCITE-985") |
| @Test public void testMerge() { |
| final String sql = "merge into emp as target\n" |
| + "using (select * from emp where deptno = 30) as source\n" |
| + "on target.empno = source.empno\n" |
| + "when matched then\n" |
| + " update set sal = sal + source.sal\n" |
| + "when not matched then\n" |
| + " insert (empno, deptno, sal)\n" |
| + " values (source.empno, source.deptno, source.sal)"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testSelectView() { |
| // translated condition: deptno = 20 and sal > 1000 and empno > 100 |
| final String sql = "select * from emp_20 where empno > 100"; |
| sql(sql).ok(); |
| } |
| |
| @Test public void testInsertView() { |
| final String sql = "insert into emp_20 (empno, ename) values (150, 'Fred')"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-695">[CALCITE-695] |
| * SqlSingleValueAggFunction is created when it may not be needed</a>. |
| */ |
| @Test public void testSubQueryAggregateFunctionFollowedBySimpleOperation() { |
| final String sql = "select deptno\n" |
| + "from EMP\n" |
| + "where deptno > (select min(deptno) * 2 + 10 from EMP)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-695">[CALCITE-695] |
| * SqlSingleValueAggFunction is created when it may not be needed</a>. |
| */ |
| @Test public void testSubQueryValues() { |
| final String sql = "select deptno\n" |
| + "from EMP\n" |
| + "where deptno > (values 10)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-695">[CALCITE-695] |
| * SqlSingleValueAggFunction is created when it may not be needed</a>. |
| */ |
| @Test public void testSubQueryLimitOne() { |
| final String sql = "select deptno\n" |
| + "from EMP\n" |
| + "where deptno > (select deptno\n" |
| + "from EMP order by deptno limit 1)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-710">[CALCITE-710] |
| * When look up subqueries, perform the same logic as the way when ones were |
| * registered</a>. |
| */ |
| @Test public void testIdenticalExpressionInSubQuery() { |
| final String sql = "select deptno\n" |
| + "from EMP\n" |
| + "where deptno in (1, 2) or deptno in (1, 2)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-694">[CALCITE-694] |
| * Scan HAVING clause for sub-queries and IN-lists</a> relating to IN. |
| */ |
| @Test public void testHavingAggrFunctionIn() { |
| final String sql = "select deptno\n" |
| + "from emp\n" |
| + "group by deptno\n" |
| + "having sum(case when deptno in (1, 2) then 0 else 1 end) +\n" |
| + "sum(case when deptno in (3, 4) then 0 else 1 end) > 10"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-694">[CALCITE-694] |
| * Scan HAVING clause for sub-queries and IN-lists</a>, with a sub-query in |
| * the HAVING clause. |
| */ |
| @Test public void testHavingInSubQueryWithAggrFunction() { |
| final String sql = "select sal\n" |
| + "from emp\n" |
| + "group by sal\n" |
| + "having sal in (\n" |
| + " select deptno\n" |
| + " from dept\n" |
| + " group by deptno\n" |
| + " having sum(deptno) > 0)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-716">[CALCITE-716] |
| * Scalar sub-query and aggregate function in SELECT or HAVING clause gives |
| * AssertionError</a>; variant involving HAVING clause. |
| */ |
| @Test public void testAggregateAndScalarSubQueryInHaving() { |
| final String sql = "select deptno\n" |
| + "from emp\n" |
| + "group by deptno\n" |
| + "having max(emp.empno) > (SELECT min(emp.empno) FROM emp)\n"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-716">[CALCITE-716] |
| * Scalar sub-query and aggregate function in SELECT or HAVING clause gives |
| * AssertionError</a>; variant involving SELECT clause. |
| */ |
| @Test public void testAggregateAndScalarSubQueryInSelect() { |
| final String sql = "select deptno,\n" |
| + " max(emp.empno) > (SELECT min(emp.empno) FROM emp) as b\n" |
| + "from emp\n" |
| + "group by deptno\n"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-770">[CALCITE-770] |
| * window aggregate and ranking functions with grouped aggregates</a>. |
| */ |
| @Test public void testWindowAggWithGroupBy() { |
| final String sql = "select min(deptno), rank() over (order by empno),\n" |
| + "max(empno) over (partition by deptno)\n" |
| + "from emp group by deptno, empno\n"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-847">[CALCITE-847] |
| * AVG window function in GROUP BY gives AssertionError</a>. |
| */ |
| @Test public void testWindowAverageWithGroupBy() { |
| final String sql = "select avg(deptno) over ()\n" |
| + "from emp\n" |
| + "group by deptno"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-770">[CALCITE-770] |
| * variant involving joins</a>. |
| */ |
| @Test public void testWindowAggWithGroupByAndJoin() { |
| final String sql = "select min(d.deptno), rank() over (order by e.empno),\n" |
| + " max(e.empno) over (partition by e.deptno)\n" |
| + "from emp e, dept d\n" |
| + "where e.deptno = d.deptno\n" |
| + "group by d.deptno, e.empno, e.deptno\n"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-770">[CALCITE-770] |
| * variant involving HAVING clause</a>. |
| */ |
| @Test public void testWindowAggWithGroupByAndHaving() { |
| final String sql = "select min(deptno), rank() over (order by empno),\n" |
| + "max(empno) over (partition by deptno)\n" |
| + "from emp group by deptno, empno\n" |
| + "having empno < 10 and min(deptno) < 20\n"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-770">[CALCITE-770] |
| * variant involving join with subQuery that contains window function and |
| * GROUP BY</a>. |
| */ |
| @Test public void testWindowAggInSubQueryJoin() { |
| final String sql = "select T.x, T.y, T.z, emp.empno\n" |
| + "from (select min(deptno) as x,\n" |
| + " rank() over (order by empno) as y,\n" |
| + " max(empno) over (partition by deptno) as z\n" |
| + " from emp group by deptno, empno) as T\n" |
| + " inner join emp on T.x = emp.deptno\n" |
| + " and T.y = emp.empno\n"; |
| sql(sql).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1313">[CALCITE-1313] |
| * Validator should derive type of expression in ORDER BY</a>. |
| */ |
| @Test public void testOrderByOver() { |
| String sql = "select deptno, rank() over(partition by empno order by deptno)\n" |
| + "from emp order by row_number() over(partition by empno order by deptno)"; |
| sql(sql).ok(); |
| } |
| |
| /** |
| * Test case (correlated scalar aggregate subQuery) for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-714">[CALCITE-714] |
| * When de-correlating, push join condition into subQuery</a>. |
| */ |
| @Test public void testCorrelationScalarAggAndFilter() { |
| final String sql = "SELECT e1.empno\n" |
| + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n" |
| + "and e1.deptno < 10 and d1.deptno < 15\n" |
| + "and e1.sal > (select avg(sal) from emp e2 where e1.empno = e2.empno)"; |
| sql(sql).decorrelate(true).expand(true).ok(); |
| } |
| |
| @Test public void testCorrelationScalarAggAndFilterRex() { |
| final String sql = "SELECT e1.empno\n" |
| + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n" |
| + "and e1.deptno < 10 and d1.deptno < 15\n" |
| + "and e1.sal > (select avg(sal) from emp e2 where e1.empno = e2.empno)"; |
| sql(sql).decorrelate(true).expand(false).ok(); |
| } |
| |
| /** |
| * Test case (correlated EXISTS subQuery) for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-714">[CALCITE-714] |
| * When de-correlating, push join condition into subQuery</a>. |
| */ |
| @Test public void testCorrelationExistsAndFilter() { |
| final String sql = "SELECT e1.empno\n" |
| + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n" |
| + "and e1.deptno < 10 and d1.deptno < 15\n" |
| + "and exists (select * from emp e2 where e1.empno = e2.empno)"; |
| sql(sql).decorrelate(true).expand(true).ok(); |
| } |
| |
| @Test public void testCorrelationExistsAndFilterRex() { |
| final String sql = "SELECT e1.empno\n" |
| + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n" |
| + "and e1.deptno < 10 and d1.deptno < 15\n" |
| + "and exists (select * from emp e2 where e1.empno = e2.empno)"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| /** |
| * Test case (correlated NOT EXISTS subQuery) for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-714">[CALCITE-714] |
| * When de-correlating, push join condition into subQuery</a>. |
| */ |
| @Test public void testCorrelationNotExistsAndFilter() { |
| final String sql = "SELECT e1.empno\n" |
| + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n" |
| + "and e1.deptno < 10 and d1.deptno < 15\n" |
| + "and not exists (select * from emp e2 where e1.empno = e2.empno)"; |
| sql(sql).decorrelate(true).ok(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150] |
| * Dynamic Table / Dynamic Star support</a> |
| */ |
| @Test |
| public void testSelectFromDynamicTable() throws Exception { |
| final String sql = "select n_nationkey, n_name from SALES.NATION"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testSelectStarFromDynamicTable() throws Exception { |
| final String sql = "select * from SALES.NATION"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testReferDynamicStarInSelectOB() throws Exception { |
| final String sql = "select n_nationkey, n_name\n" |
| + "from (select * from SALES.NATION)\n" |
| + "order by n_regionkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testDynamicStarInTableJoin() throws Exception { |
| final String sql = "select * from " |
| + " (select * from SALES.NATION) T1, " |
| + " (SELECT * from SALES.CUSTOMER) T2 " |
| + " where T1.n_nationkey = T2.c_nationkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testReferDynamicStarInSelectWhereGB() throws Exception { |
| final String sql = "select n_regionkey, count(*) as cnt from " |
| + "(select * from SALES.NATION) where n_nationkey > 5 " |
| + "group by n_regionkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testDynamicStarInJoinAndSubQ() throws Exception { |
| final String sql = "select * from " |
| + " (select * from SALES.NATION T1, " |
| + " SALES.CUSTOMER T2 where T1.n_nationkey = T2.c_nationkey)"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testStarJoinStaticDynTable() throws Exception { |
| final String sql = "select * from SALES.NATION N, SALES.REGION as R " |
| + "where N.n_regionkey = R.r_regionkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testGrpByColFromStarInSubQuery() throws Exception { |
| final String sql = "SELECT n.n_nationkey AS col " |
| + " from (SELECT * FROM SALES.NATION) as n " |
| + " group by n.n_nationkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testDynStarInExistSubQ() throws Exception { |
| final String sql = "select *\n" |
| + "from SALES.REGION where exists (select * from SALES.NATION)"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** |
| * Test case for Dynamic Table / Dynamic Star support |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a> |
| */ |
| @Test |
| public void testSelStarOrderBy() throws Exception { |
| final String sql = "SELECT * from SALES.NATION order by n_nationkey"; |
| sql(sql).with(getTesterWithDynamicTable()).ok(); |
| } |
| |
| /** Test case for |
| * <a href="https://issues.apache.org/jira/browse/CALCITE-1321">[CALCITE-1321] |
| * Configurable IN list size when converting IN clause to join</a>. */ |
| @Test public void testInToSemiJoin() { |
| final String sql = "SELECT empno" |
| + " FROM emp AS e" |
| + " WHERE cast(e.empno as bigint) in (130, 131, 132, 133, 134)"; |
| // No conversion to join since less than IN-list size threshold 10 |
| SqlToRelConverter.Config noConvertConfig = SqlToRelConverter.configBuilder(). |
| |
| withInSubqueryThreshold(10).build(); |
| sql(sql).withConfig(noConvertConfig).convertsTo("${planNotConverted}"); |
| // Conversion to join since greater than IN-list size threshold 2 |
| SqlToRelConverter.Config convertConfig = SqlToRelConverter.configBuilder(). |
| withInSubqueryThreshold(2).build(); |
| sql(sql).withConfig(convertConfig).convertsTo("${planConverted}"); |
| } |
| |
| private Tester getTesterWithDynamicTable() { |
| return tester.withCatalogReaderFactory( |
| new Function<RelDataTypeFactory, Prepare.CatalogReader>() { |
| public Prepare.CatalogReader apply(RelDataTypeFactory typeFactory) { |
| return new MockCatalogReader(typeFactory, true) { |
| @Override public MockCatalogReader init() { |
| // CREATE SCHEMA "SALES; |
| // CREATE DYNAMIC TABLE "NATION" |
| // CREATE DYNAMIC TABLE "CUSTOMER" |
| |
| MockSchema schema = new MockSchema("SALES"); |
| registerSchema(schema); |
| |
| MockTable nationTable = new MockDynamicTable(this, schema.getCatalogName(), |
| schema.getName(), "NATION", false, 100); |
| registerTable(nationTable); |
| |
| MockTable customerTable = new MockDynamicTable(this, schema.getCatalogName(), |
| schema.getName(), "CUSTOMER", false, 100); |
| registerTable(customerTable); |
| |
| // CREATE TABLE "REGION" - static table with known schema. |
| final RelDataType intType = |
| typeFactory.createSqlType(SqlTypeName.INTEGER); |
| final RelDataType varcharType = |
| typeFactory.createSqlType(SqlTypeName.VARCHAR); |
| |
| MockTable regionTable = MockTable.create(this, schema, "REGION", false, 100); |
| regionTable.addColumn("R_REGIONKEY", intType); |
| regionTable.addColumn("R_NAME", varcharType); |
| regionTable.addColumn("R_COMMENT", varcharType); |
| registerTable(regionTable); |
| return this; |
| } |
| // CHECKSTYLE: IGNORE 1 |
| }.init(); |
| } |
| }); |
| } |
| |
| /** |
| * Visitor that checks that every {@link RelNode} in a tree is valid. |
| * |
| * @see RelNode#isValid(org.apache.calcite.util.Litmus) |
| */ |
| public static class RelValidityChecker extends RelVisitor { |
| int invalidCount; |
| |
| public void visit(RelNode node, int ordinal, RelNode parent) { |
| if (!node.isValid(Litmus.THROW)) { |
| ++invalidCount; |
| } |
| super.visit(node, ordinal, parent); |
| } |
| } |
| |
| /** Allows fluent testing. */ |
| public class Sql { |
| private final String sql; |
| private final boolean expand; |
| private final boolean decorrelate; |
| private final Tester tester; |
| private final boolean trim; |
| private final SqlToRelConverter.Config config; |
| |
| Sql(String sql, boolean expand, boolean decorrelate, Tester tester, |
| boolean trim, SqlToRelConverter.Config config) { |
| this.sql = sql; |
| this.expand = expand; |
| this.decorrelate = decorrelate; |
| this.tester = tester; |
| this.trim = trim; |
| this.config = config; |
| } |
| |
| public void ok() { |
| convertsTo("${plan}"); |
| } |
| |
| public void convertsTo(String plan) { |
| tester.withExpand(expand) |
| .withDecorrelation(decorrelate) |
| .withConfig(config) |
| .assertConvertsTo(sql, plan, trim); |
| } |
| |
| public Sql withConfig(SqlToRelConverter.Config config) { |
| return new Sql(sql, expand, decorrelate, tester, trim, config); |
| } |
| |
| public Sql expand(boolean expand) { |
| return new Sql(sql, expand, decorrelate, tester, trim, config); |
| } |
| |
| public Sql decorrelate(boolean decorrelate) { |
| return new Sql(sql, expand, decorrelate, tester, trim, config); |
| } |
| |
| public Sql with(Tester tester) { |
| return new Sql(sql, expand, decorrelate, tester, trim, config); |
| } |
| |
| public Sql trim(boolean trim) { |
| return new Sql(sql, expand, decorrelate, tester, trim, config); |
| } |
| } |
| } |
| |
| // End SqlToRelConverterTest.java |