http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java index 234b666..dbc230a 100644 --- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java @@ -36,11 +36,16 @@ import org.apache.calcite.rel.core.Aggregate; import org.apache.calcite.rel.core.AggregateCall; import org.apache.calcite.rel.core.Correlate; import org.apache.calcite.rel.core.CorrelationId; +import org.apache.calcite.rel.core.Filter; import org.apache.calcite.rel.core.Join; import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.core.Minus; import org.apache.calcite.rel.core.Project; import org.apache.calcite.rel.core.SemiJoin; import org.apache.calcite.rel.core.Sort; +import org.apache.calcite.rel.core.TableScan; +import org.apache.calcite.rel.core.Union; +import org.apache.calcite.rel.core.Values; import org.apache.calcite.rel.logical.LogicalAggregate; import org.apache.calcite.rel.logical.LogicalExchange; import org.apache.calcite.rel.logical.LogicalFilter; @@ -65,9 +70,13 @@ import org.apache.calcite.rel.metadata.RelMetadataProvider; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexBuilder; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.rex.RexTableInputRef; import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.tools.Frameworks; @@ -80,6 +89,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; import org.hamcrest.CoreMatchers; import org.hamcrest.CustomTypeSafeMatcher; @@ -91,17 +101,27 @@ import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.isA; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -133,6 +153,8 @@ public class RelMetadataTest extends SqlToRelTestBase { private static final double DEPT_SIZE = 4d; + private static final String EMP_QNAME = "[CATALOG, SALES, EMP]"; + //~ Methods ---------------------------------------------------------------- private static Matcher<? super Number> nearTo(Number v, Number epsilon) { @@ -1481,6 +1503,631 @@ public class RelMetadataTest extends SqlToRelTestBase { assertThat(RelMdUtil.linear(12, 0, 10, 100, 200), is(200d)); } + @Test public void testExpressionLineageStar() { + // All columns in output + final RelNode tableRel = convertSql("select * from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(4, tableRel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(tableRel, ref); + final String inputRef = RexInputRef.of(4, tableRel.getRowType().getFieldList()).toString(); + assertThat(r.size(), is(1)); + final String resultString = r.iterator().next().toString(); + assertThat(resultString, startsWith(EMP_QNAME)); + assertThat(resultString, endsWith(inputRef)); + } + + @Test public void testExpressionLineageTwoColumns() { + // mgr is column 3 in catalog.sales.emp + // deptno is column 7 in catalog.sales.emp + final RelNode rel = convertSql("select mgr, deptno from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref1 = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r1 = mq.getExpressionLineage(rel, ref1); + assertThat(r1.size(), is(1)); + final RexTableInputRef result1 = (RexTableInputRef) r1.iterator().next(); + assertThat(result1.getQualifiedName(), is(EMP_QNAME)); + assertThat(result1.getIndex(), is(3)); + + final RexNode ref2 = RexInputRef.of(1, rel.getRowType().getFieldList()); + final Set<RexNode> r2 = mq.getExpressionLineage(rel, ref2); + assertThat(r2.size(), is(1)); + final RexTableInputRef result2 = (RexTableInputRef) r2.iterator().next(); + assertThat(result2.getQualifiedName(), is(EMP_QNAME)); + assertThat(result2.getIndex(), is(7)); + + assertThat(result1.getIdentifier(), is(result2.getIdentifier())); + } + + @Test public void testExpressionLineageTwoColumnsSwapped() { + // deptno is column 7 in catalog.sales.emp + // mgr is column 3 in catalog.sales.emp + final RelNode rel = convertSql("select deptno, mgr from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref1 = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r1 = mq.getExpressionLineage(rel, ref1); + assertThat(r1.size(), is(1)); + final RexTableInputRef result1 = (RexTableInputRef) r1.iterator().next(); + assertThat(result1.getQualifiedName(), is(EMP_QNAME)); + assertThat(result1.getIndex(), is(7)); + + final RexNode ref2 = RexInputRef.of(1, rel.getRowType().getFieldList()); + final Set<RexNode> r2 = mq.getExpressionLineage(rel, ref2); + assertThat(r2.size(), is(1)); + final RexTableInputRef result2 = (RexTableInputRef) r2.iterator().next(); + assertThat(result2.getQualifiedName(), is(EMP_QNAME)); + assertThat(result2.getIndex(), is(3)); + + assertThat(result1.getIdentifier(), is(result2.getIdentifier())); + } + + @Test public void testExpressionLineageCombineTwoColumns() { + // empno is column 0 in catalog.sales.emp + // deptno is column 7 in catalog.sales.emp + final RelNode rel = convertSql("select empno + deptno from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + + assertThat(r.size(), is(1)); + final RexNode result = r.iterator().next(); + assertThat(result.getKind(), is(SqlKind.PLUS)); + final RexCall call = (RexCall) result; + assertThat(call.getOperands().size(), is(2)); + final RexTableInputRef inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(0)); + final RexTableInputRef inputRef2 = (RexTableInputRef) call.getOperands().get(1); + assertThat(inputRef2.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef2.getIndex(), is(7)); + assertThat(inputRef1.getIdentifier(), is(inputRef2.getIdentifier())); + } + + @Test public void testExpressionLineageInnerJoinLeft() { + // ename is column 1 in catalog.sales.emp + final RelNode rel = convertSql("select ename from emp,dept"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + assertThat(r.size(), is(1)); + final RexTableInputRef result = (RexTableInputRef) r.iterator().next(); + assertThat(result.getQualifiedName(), is(EMP_QNAME)); + assertThat(result.getIndex(), is(1)); + } + + @Test public void testExpressionLineageInnerJoinRight() { + // ename is column 0 in catalog.sales.bonus + final RelNode rel = convertSql("select bonus.ename from emp join bonus using (ename)"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + assertThat(r.size(), is(1)); + final RexTableInputRef result = (RexTableInputRef) r.iterator().next(); + assertThat(result.getQualifiedName(), is("[CATALOG, SALES, BONUS]")); + assertThat(result.getIndex(), is(0)); + } + + @Test public void testExpressionLineageSelfJoin() { + // deptno is column 7 in catalog.sales.emp + // sal is column 5 in catalog.sales.emp + final RelNode rel = convertSql("select a.deptno, b.sal from (select * from emp limit 7) as a\n" + + "inner join (select * from emp limit 2) as b\n" + + "on a.deptno = b.deptno"); + final RelNode tableRel = convertSql("select * from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref1 = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r1 = mq.getExpressionLineage(rel, ref1); + final String inputRef1 = RexInputRef.of(7, tableRel.getRowType().getFieldList()).toString(); + assertThat(r1.size(), is(1)); + final String resultString1 = r1.iterator().next().toString(); + assertThat(resultString1, startsWith(EMP_QNAME)); + assertThat(resultString1, endsWith(inputRef1)); + + final RexNode ref2 = RexInputRef.of(1, rel.getRowType().getFieldList()); + final Set<RexNode> r2 = mq.getExpressionLineage(rel, ref2); + final String inputRef2 = RexInputRef.of(5, tableRel.getRowType().getFieldList()).toString(); + assertThat(r2.size(), is(1)); + final String resultString2 = r2.iterator().next().toString(); + assertThat(resultString2, startsWith(EMP_QNAME)); + assertThat(resultString2, endsWith(inputRef2)); + + assertThat(((RexTableInputRef) r1.iterator().next()).getIdentifier(), + not(((RexTableInputRef) r2.iterator().next()).getIdentifier())); + } + + @Test public void testExpressionLineageOuterJoin() { + // lineage cannot be determined + final RelNode rel = convertSql("select name as dname from emp left outer join dept" + + " on emp.deptno = dept.deptno"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + assertNull(r); + } + + @Test public void testExpressionLineageFilter() { + // ename is column 1 in catalog.sales.emp + final RelNode rel = convertSql("select ename from emp where deptno = 10"); + final RelNode tableRel = convertSql("select * from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + final String inputRef = RexInputRef.of(1, tableRel.getRowType().getFieldList()).toString(); + assertThat(r.size(), is(1)); + final String resultString = r.iterator().next().toString(); + assertThat(resultString, startsWith(EMP_QNAME)); + assertThat(resultString, endsWith(inputRef)); + } + + @Test public void testExpressionLineageAggregateGroupColumn() { + // deptno is column 7 in catalog.sales.emp + final RelNode rel = convertSql("select deptno, count(*) from emp where deptno > 10 " + + "group by deptno having count(*) = 0"); + final RelNode tableRel = convertSql("select * from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + final String inputRef = RexInputRef.of(7, tableRel.getRowType().getFieldList()).toString(); + assertThat(r.size(), is(1)); + final String resultString = r.iterator().next().toString(); + assertThat(resultString, startsWith(EMP_QNAME)); + assertThat(resultString, endsWith(inputRef)); + } + + @Test public void testExpressionLineageAggregateAggColumn() { + // lineage cannot be determined + final RelNode rel = convertSql("select deptno, count(*) from emp where deptno > 10 " + + "group by deptno having count(*) = 0"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(1, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + assertNull(r); + } + + @Test public void testExpressionLineageUnion() { + // sal is column 5 in catalog.sales.emp + final RelNode rel = convertSql("select sal from (\n" + + " select * from emp union all select * from emp) " + + "where deptno = 10"); + final RelNode tableRel = convertSql("select * from emp"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + final String inputRef = RexInputRef.of(5, tableRel.getRowType().getFieldList()).toString(); + assertThat(r.size(), is(2)); + for (RexNode result : r) { + final String resultString = result.toString(); + assertThat(resultString, startsWith(EMP_QNAME)); + assertThat(resultString, endsWith(inputRef)); + } + + Iterator<RexNode> it = r.iterator(); + assertThat(((RexTableInputRef) it.next()).getIdentifier(), + not(((RexTableInputRef) it.next()).getIdentifier())); + } + + @Test public void testExpressionLineageMultiUnion() { + // empno is column 0 in catalog.sales.emp + // sal is column 5 in catalog.sales.emp + final RelNode rel = convertSql("select a.empno + b.sal from \n" + + " (select empno, ename from emp,dept) a join " + + " (select * from emp union all select * from emp) b \n" + + " on a.empno = b.empno \n" + + " where b.deptno = 10"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + + // With the union, we should get two origins + // The first one should be the same one: join + // The second should come from each union input + final Set<String> set = new HashSet<>(); + assertThat(r.size(), is(2)); + for (RexNode result : r) { + assertThat(result.getKind(), is(SqlKind.PLUS)); + final RexCall call = (RexCall) result; + assertThat(call.getOperands().size(), is(2)); + final RexTableInputRef inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + // Add join alpha to set + set.add(inputRef1.getQualifiedName()); + assertThat(inputRef1.getIndex(), is(0)); + final RexTableInputRef inputRef2 = (RexTableInputRef) call.getOperands().get(1); + assertThat(inputRef2.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef2.getIndex(), is(5)); + assertThat(inputRef1.getIdentifier(), not(inputRef2.getIdentifier())); + } + assertThat(set.size(), is(1)); + } + + @Test public void testExpressionLineageValues() { + // lineage cannot be determined + final RelNode rel = convertSql("select * from (values (1), (2)) as t(c)"); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final RexNode ref = RexInputRef.of(0, rel.getRowType().getFieldList()); + final Set<RexNode> r = mq.getExpressionLineage(rel, ref); + assertNull(r); + } + + @Test public void testAllPredicates() { + final Project rel = (Project) convertSql("select * from emp, dept"); + final Join join = (Join) rel.getInput(); + final RelOptTable empTable = join.getInput(0).getTable(); + final RelOptTable deptTable = join.getInput(1).getTable(); + Frameworks.withPlanner( + new Frameworks.PlannerAction<Void>() { + public Void apply(RelOptCluster cluster, + RelOptSchema relOptSchema, + SchemaPlus rootSchema) { + checkAllPredicates(cluster, empTable, deptTable); + return null; + } + }); + } + + private void checkAllPredicates(RelOptCluster cluster, RelOptTable empTable, + RelOptTable deptTable) { + final RelBuilder relBuilder = RelBuilder.proto().create(cluster, null); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + + final LogicalTableScan empScan = LogicalTableScan.create(cluster, empTable); + relBuilder.push(empScan); + + RelOptPredicateList predicates = + mq.getAllPredicates(empScan); + assertThat(predicates.pulledUpPredicates.isEmpty(), is(true)); + + relBuilder.filter( + relBuilder.equals(relBuilder.field("EMPNO"), + relBuilder.literal(BigDecimal.ONE))); + + final RelNode filter = relBuilder.peek(); + predicates = mq.getAllPredicates(filter); + assertThat(predicates.pulledUpPredicates.size(), is(1)); + RexCall call = (RexCall) predicates.pulledUpPredicates.get(0); + assertThat(call.getOperands().size(), is(2)); + RexTableInputRef inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(0)); + + final LogicalTableScan deptScan = + LogicalTableScan.create(cluster, deptTable); + relBuilder.push(deptScan); + + relBuilder.join(JoinRelType.INNER, + relBuilder.equals(relBuilder.field(2, 0, "DEPTNO"), + relBuilder.field(2, 1, "DEPTNO"))); + + relBuilder.project(relBuilder.field("DEPTNO")); + final RelNode project = relBuilder.peek(); + predicates = mq.getAllPredicates(project); + assertThat(predicates.pulledUpPredicates.size(), is(2)); + // From Filter + call = (RexCall) predicates.pulledUpPredicates.get(0); + assertThat(call.getOperands().size(), is(2)); + inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(0)); + // From Join + call = (RexCall) predicates.pulledUpPredicates.get(1); + assertThat(call.getOperands().size(), is(2)); + inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(7)); + RexTableInputRef inputRef2 = (RexTableInputRef) call.getOperands().get(1); + assertThat(inputRef2.getQualifiedName(), is("[CATALOG, SALES, DEPT]")); + assertThat(inputRef2.getIndex(), is(0)); + } + + @Test public void testAllPredicatesAggregate1() { + final String sql = "select a, max(b) from (\n" + + " select empno as a, sal as b from emp where empno = 5)subq\n" + + "group by a"; + final RelNode rel = convertSql(sql); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + RelOptPredicateList inputSet = mq.getAllPredicates(rel); + ImmutableList<RexNode> pulledUpPredicates = inputSet.pulledUpPredicates; + assertThat(pulledUpPredicates.size(), is(1)); + RexCall call = (RexCall) pulledUpPredicates.get(0); + assertThat(call.getOperands().size(), is(2)); + final RexTableInputRef inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(0)); + final RexLiteral constant = (RexLiteral) call.getOperands().get(1); + assertThat(constant.toString(), is("5")); + } + + @Test public void testAllPredicatesAggregate2() { + final String sql = "select * from (select a, max(b) from (\n" + + " select empno as a, sal as b from emp)subq\n" + + "group by a) \n" + + "where a = 5"; + final RelNode rel = convertSql(sql); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + RelOptPredicateList inputSet = mq.getAllPredicates(rel); + ImmutableList<RexNode> pulledUpPredicates = inputSet.pulledUpPredicates; + assertThat(pulledUpPredicates.size(), is(1)); + RexCall call = (RexCall) pulledUpPredicates.get(0); + assertThat(call.getOperands().size(), is(2)); + final RexTableInputRef inputRef1 = (RexTableInputRef) call.getOperands().get(0); + assertThat(inputRef1.getQualifiedName(), is(EMP_QNAME)); + assertThat(inputRef1.getIndex(), is(0)); + final RexLiteral constant = (RexLiteral) call.getOperands().get(1); + assertThat(constant.toString(), is("5")); + } + + @Test public void testAllPredicatesAggregate3() { + final String sql = "select * from (select a, max(b) as b from (\n" + + " select empno as a, sal as b from emp)subq\n" + + "group by a) \n" + + "where b = 5"; + final RelNode rel = convertSql(sql); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + RelOptPredicateList inputSet = mq.getAllPredicates(rel); + // Filter on aggregate, we cannot infer lineage + assertNull(inputSet); + } + + private void checkNodeTypeCount(String sql, Map<Class<? extends RelNode>, Integer> expected) { + final RelNode rel = convertSql(sql); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + final Multimap<Class<? extends RelNode>, RelNode> result = mq.getNodeTypes(rel); + assertThat(result, notNullValue()); + final Map<Class<? extends RelNode>, Integer> resultCount = new HashMap<>(); + for (Entry<Class<? extends RelNode>, Collection<RelNode>> e : result.asMap().entrySet()) { + resultCount.put(e.getKey(), e.getValue().size()); + } + assertEquals(expected, resultCount); + } + + @Test public void testNodeTypeCountEmp() { + final String sql = "select * from emp"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountDept() { + final String sql = "select * from dept"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountValues() { + final String sql = "select * from (values (1), (2)) as t(c)"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(Values.class, 1); + expected.put(Project.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountCartesian() { + final String sql = "select * from emp,dept"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountJoin() { + final String sql = "select * from emp\n" + + "inner join dept on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountJoinFinite() { + final String sql = "select * from (select * from emp limit 14) as emp\n" + + "inner join (select * from dept limit 4) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountJoinEmptyFinite() { + final String sql = "select * from (select * from emp limit 0) as emp\n" + + "inner join (select * from dept limit 4) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountLeftJoinEmptyFinite() { + final String sql = "select * from (select * from emp limit 0) as emp\n" + + "left join (select * from dept limit 4) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountRightJoinEmptyFinite() { + final String sql = "select * from (select * from emp limit 0) as emp\n" + + "right join (select * from dept limit 4) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountJoinFiniteEmpty() { + final String sql = "select * from (select * from emp limit 7) as emp\n" + + "inner join (select * from dept limit 0) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountJoinEmptyEmpty() { + final String sql = "select * from (select * from emp limit 0) as emp\n" + + "inner join (select * from dept limit 0) as dept\n" + + "on emp.deptno = dept.deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Join.class, 1); + expected.put(Project.class, 3); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountUnion() { + final String sql = "select ename from emp\n" + + "union all\n" + + "select name from dept"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Project.class, 2); + expected.put(Union.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountUnionOnFinite() { + final String sql = "select ename from (select * from emp limit 100)\n" + + "union all\n" + + "select name from (select * from dept limit 40)"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Union.class, 1); + expected.put(Project.class, 4); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountMinusOnFinite() { + final String sql = "select ename from (select * from emp limit 100)\n" + + "except\n" + + "select name from (select * from dept limit 40)"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 2); + expected.put(Minus.class, 1); + expected.put(Project.class, 4); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountFilter() { + final String sql = "select * from emp where ename='Mathilda'"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Filter.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountSort() { + final String sql = "select * from emp order by ename"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Sort.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountSortLimit() { + final String sql = "select * from emp order by ename limit 10"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Sort.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountSortLimitOffset() { + final String sql = "select * from emp order by ename limit 10 offset 5"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Sort.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountSortLimitOffsetOnFinite() { + final String sql = "select * from (select * from emp limit 12)\n" + + "order by ename limit 20 offset 5"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 2); + expected.put(Sort.class, 2); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountAggregate() { + final String sql = "select deptno from emp group by deptno"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Aggregate.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountAggregateGroupingSets() { + final String sql = "select deptno from emp\n" + + "group by grouping sets ((deptno), (ename, deptno))"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 3); + expected.put(Aggregate.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountAggregateEmptyKeyOnEmptyTable() { + final String sql = "select count(*) from (select * from emp limit 0)"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 2); + expected.put(Aggregate.class, 1); + expected.put(Sort.class, 1); + checkNodeTypeCount(sql, expected); + } + + @Test public void testNodeTypeCountFilterAggregateEmptyKey() { + final String sql = "select count(*) from emp where 1 = 0"; + final Map<Class<? extends RelNode>, Integer> expected = new HashMap<>(); + expected.put(TableScan.class, 1); + expected.put(Project.class, 1); + expected.put(Filter.class, 1); + expected.put(Aggregate.class, 1); + checkNodeTypeCount(sql, expected); + } + /** * Matcher that succeeds for any collection that, when converted to strings * and sorted on those strings, matches the given reference string.
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml ---------------------------------------------------------------------- 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 2314612..dd3861b 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -6593,7 +6593,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$ <Resource name="planAfter"> <![CDATA[ LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[null], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[7], SLACKER=[$8]) - LogicalFilter(condition=[AND(=($7, 7), =($0, 10), IS NULL($3))]) + LogicalFilter(condition=[AND(=($7, 7), IS NULL($3), =($0, 10))]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) ]]> </Resource>
