[CALCITE-2441] RelBuilder.scan should expand TranslatableTable and views Add class ViewExpanders, which has utility methods for creating ToRelContext and ViewExpander.
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/9c26a9e7 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/9c26a9e7 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/9c26a9e7 Branch: refs/heads/master Commit: 9c26a9e74dafadb27ea363d82cfe1105d5dd3329 Parents: 332ffb4 Author: Julian Hyde <[email protected]> Authored: Thu Oct 25 16:50:44 2018 -0700 Committer: Julian Hyde <[email protected]> Committed: Wed Oct 31 12:01:28 2018 -0700 ---------------------------------------------------------------------- .../org/apache/calcite/jdbc/CalciteSchema.java | 1 - .../calcite/plan/RelOptMaterialization.java | 2 +- .../org/apache/calcite/plan/RelOptUtil.java | 18 +-- .../org/apache/calcite/plan/ViewExpanders.java | 67 +++++++++++ .../calcite/prepare/LixToRelTranslator.java | 20 ++-- .../org/apache/calcite/prepare/PlannerImpl.java | 95 +++++++++------- .../org/apache/calcite/prepare/Prepare.java | 8 +- .../calcite/prepare/QueryableRelBuilder.java | 3 +- .../apache/calcite/rel/core/RelFactories.java | 44 ++++++++ .../rel/rules/AggregateStarTableRule.java | 4 +- .../rel/rules/LoptSemiJoinOptimizer.java | 3 +- .../apache/calcite/rel/rules/TableScanRule.java | 4 +- .../apache/calcite/schema/impl/ViewTable.java | 25 +++- .../apache/calcite/sql2rel/RelDecorrelator.java | 4 - .../calcite/sql2rel/SqlToRelConverter.java | 16 +-- .../apache/calcite/tools/FrameworkConfig.java | 6 + .../org/apache/calcite/tools/Frameworks.java | 18 ++- .../org/apache/calcite/tools/RelBuilder.java | 1 + .../org/apache/calcite/test/CalciteAssert.java | 5 + .../org/apache/calcite/test/RelBuilderTest.java | 113 ++++++++++++++----- .../org/apache/calcite/tools/PlannerTest.java | 40 +++++++ 21 files changed, 364 insertions(+), 133 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java index 8e4c897..fa25f69 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java @@ -730,7 +730,6 @@ public abstract class CalciteSchema { public TableEntryImpl(CalciteSchema schema, String name, Table table, ImmutableList<String> sqls) { super(schema, name, sqls); - assert table != null; this.table = Objects.requireNonNull(table); } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java index e89c3f0..fea1cba 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java @@ -104,7 +104,7 @@ public class RelOptMaterialization { final RelOptCluster cluster = scan.getCluster(); final RelNode scan2 = - starRelOptTable.toRel(RelOptUtil.getContext(cluster)); + starRelOptTable.toRel(ViewExpanders.simpleContext(cluster)); return RelOptUtil.createProject(scan2, Mappings.asList(mapping.inverse())); } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java index b5063d1..cda1e66 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java @@ -20,7 +20,6 @@ import org.apache.calcite.avatica.AvaticaConnection; import org.apache.calcite.linq4j.Ord; import org.apache.calcite.rel.RelHomogeneousShuttle; import org.apache.calcite.rel.RelNode; -import org.apache.calcite.rel.RelRoot; import org.apache.calcite.rel.RelShuttle; import org.apache.calcite.rel.RelVisitor; import org.apache.calcite.rel.RelWriter; @@ -2791,20 +2790,9 @@ public abstract class RelOptUtil { return query; } - /** Returns a simple - * {@link org.apache.calcite.plan.RelOptTable.ToRelContext}. */ - public static RelOptTable.ToRelContext getContext( - final RelOptCluster cluster) { - return new RelOptTable.ToRelContext() { - public RelOptCluster getCluster() { - return cluster; - } - - public RelRoot expandView(RelDataType rowType, String queryString, - List<String> schemaPath, List<String> viewPath) { - throw new UnsupportedOperationException(); - } - }; + @Deprecated // to be removed before 2.0 + public static RelOptTable.ToRelContext getContext(RelOptCluster cluster) { + return ViewExpanders.simpleContext(cluster); } /** Returns the number of {@link org.apache.calcite.rel.core.Join} nodes in a http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java b/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java new file mode 100644 index 0000000..eea2e33 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/plan/ViewExpanders.java @@ -0,0 +1,67 @@ +/* + * 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.plan; + +import org.apache.calcite.rel.RelRoot; +import org.apache.calcite.rel.type.RelDataType; + +import java.util.List; +import javax.annotation.Nonnull; + +/** + * Utilities for {@link RelOptTable.ViewExpander} and + * {@link RelOptTable.ToRelContext}. + */ +@Nonnull +public abstract class ViewExpanders { + private ViewExpanders() {} + + /** Converts a {@code ViewExpander} to a {@code ToRelContext}. */ + public static RelOptTable.ToRelContext toRelContext( + RelOptTable.ViewExpander viewExpander, RelOptCluster cluster) { + if (viewExpander instanceof RelOptTable.ToRelContext) { + return (RelOptTable.ToRelContext) viewExpander; + } + return new RelOptTable.ToRelContext() { + public RelOptCluster getCluster() { + return cluster; + } + + public RelRoot expandView(RelDataType rowType, String queryString, + List<String> schemaPath, List<String> viewPath) { + return viewExpander.expandView(rowType, queryString, schemaPath, + viewPath); + } + }; + } + + /** Creates a simple {@code ToRelContext} that cannot expand views. */ + public static RelOptTable.ToRelContext simpleContext(RelOptCluster cluster) { + return new RelOptTable.ToRelContext() { + public RelOptCluster getCluster() { + return cluster; + } + + public RelRoot expandView(RelDataType rowType, String queryString, + List<String> schemaPath, List<String> viewPath) { + throw new UnsupportedOperationException(); + } + }; + } +} + +// End ViewExpanders.java http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java index 6994550..8a05671 100644 --- a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java +++ b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java @@ -27,12 +27,11 @@ import org.apache.calcite.linq4j.tree.NewExpression; import org.apache.calcite.linq4j.tree.Types; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptTable; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.rel.RelNode; -import org.apache.calcite.rel.RelRoot; import org.apache.calcite.rel.logical.LogicalFilter; import org.apache.calcite.rel.logical.LogicalProject; import org.apache.calcite.rel.logical.LogicalTableScan; -import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; import org.apache.calcite.util.BuiltInMethod; @@ -49,7 +48,7 @@ import java.util.List; * * @see QueryableRelBuilder */ -class LixToRelTranslator implements RelOptTable.ToRelContext { +class LixToRelTranslator { final RelOptCluster cluster; private final Prepare preparingStmt; final JavaTypeFactory typeFactory; @@ -60,13 +59,14 @@ class LixToRelTranslator implements RelOptTable.ToRelContext { this.typeFactory = (JavaTypeFactory) cluster.getTypeFactory(); } - public RelOptCluster getCluster() { - return cluster; - } - - public RelRoot expandView(RelDataType rowType, String queryString, - List<String> schemaPath, List<String> viewPath) { - return preparingStmt.expandView(rowType, queryString, schemaPath, viewPath); + RelOptTable.ToRelContext toRelContext() { + if (preparingStmt instanceof RelOptTable.ViewExpander) { + final RelOptTable.ViewExpander viewExpander = + (RelOptTable.ViewExpander) this.preparingStmt; + return ViewExpanders.toRelContext(viewExpander, cluster); + } else { + return ViewExpanders.simpleContext(cluster); + } } public <T> RelNode translate(Queryable<T> queryable) { http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java index 6f746ab..9269155 100644 --- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java @@ -60,7 +60,7 @@ import java.util.List; import java.util.Properties; /** Implementation of {@link org.apache.calcite.tools.Planner}. */ -public class PlannerImpl implements Planner { +public class PlannerImpl implements Planner, ViewExpander { private final SqlOperatorTable operatorTable; private final ImmutableList<Program> programs; private final FrameworkConfig config; @@ -229,7 +229,7 @@ public class PlannerImpl implements Planner { .withConvertTableAccess(false) .build(); final SqlToRelConverter sqlToRelConverter = - new SqlToRelConverter(new ViewExpanderImpl(), validator, + new SqlToRelConverter(this, validator, createCatalogReader(), cluster, convertletTable, config); root = sqlToRelConverter.convertQuery(validatedSqlNode, false, true); @@ -242,50 +242,63 @@ public class PlannerImpl implements Planner { return root; } - /** Implements {@link org.apache.calcite.plan.RelOptTable.ViewExpander} - * interface for {@link org.apache.calcite.tools.Planner}. */ + /** @deprecated Now {@link PlannerImpl} implements {@link ViewExpander} + * directly. */ + @Deprecated public class ViewExpanderImpl implements ViewExpander { - @Override public RelRoot expandView(RelDataType rowType, String queryString, - List<String> schemaPath, List<String> viewPath) { - SqlParser parser = SqlParser.create(queryString, parserConfig); - SqlNode sqlNode; - try { - sqlNode = parser.parseQuery(); - } catch (SqlParseException e) { - throw new RuntimeException("parse failed", e); - } + ViewExpanderImpl() { + } - final SqlConformance conformance = conformance(); - final CalciteCatalogReader catalogReader = - createCatalogReader().withSchemaPath(schemaPath); - final SqlValidator validator = - new CalciteSqlValidator(operatorTable, catalogReader, typeFactory, - conformance); - validator.setIdentifierExpansion(true); - - final RexBuilder rexBuilder = createRexBuilder(); - final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder); - final SqlToRelConverter.Config config = SqlToRelConverter - .configBuilder() - .withConfig(sqlToRelConverterConfig) - .withTrimUnusedFields(false) - .withConvertTableAccess(false) - .build(); - final SqlToRelConverter sqlToRelConverter = - new SqlToRelConverter(new ViewExpanderImpl(), validator, - catalogReader, cluster, convertletTable, config); - - root = sqlToRelConverter.convertQuery(sqlNode, true, false); - root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true)); - final RelBuilder relBuilder = - config.getRelBuilderFactory().create(cluster, null); - root = root.withRel( - RelDecorrelator.decorrelateQuery(root.rel, relBuilder)); - - return PlannerImpl.this.root; + public RelRoot expandView(RelDataType rowType, String queryString, + List<String> schemaPath, List<String> viewPath) { + return PlannerImpl.this.expandView(rowType, queryString, schemaPath, + viewPath); } } + @Override public RelRoot expandView(RelDataType rowType, String queryString, + List<String> schemaPath, List<String> viewPath) { + if (planner == null) { + ready(); + } + SqlParser parser = SqlParser.create(queryString, parserConfig); + SqlNode sqlNode; + try { + sqlNode = parser.parseQuery(); + } catch (SqlParseException e) { + throw new RuntimeException("parse failed", e); + } + + final SqlConformance conformance = conformance(); + final CalciteCatalogReader catalogReader = + createCatalogReader().withSchemaPath(schemaPath); + final SqlValidator validator = + new CalciteSqlValidator(operatorTable, catalogReader, typeFactory, + conformance); + validator.setIdentifierExpansion(true); + + final RexBuilder rexBuilder = createRexBuilder(); + final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder); + final SqlToRelConverter.Config config = SqlToRelConverter + .configBuilder() + .withConfig(sqlToRelConverterConfig) + .withTrimUnusedFields(false) + .withConvertTableAccess(false) + .build(); + final SqlToRelConverter sqlToRelConverter = + new SqlToRelConverter(this, validator, + catalogReader, cluster, convertletTable, config); + + final RelRoot root = + sqlToRelConverter.convertQuery(sqlNode, true, false); + final RelRoot root2 = + root.withRel(sqlToRelConverter.flattenTypes(root.rel, true)); + final RelBuilder relBuilder = + config.getRelBuilderFactory().create(cluster, null); + return root2.withRel( + RelDecorrelator.decorrelateQuery(root.rel, relBuilder)); + } + // CalciteCatalogReader is stateless; no need to store one private CalciteCatalogReader createCatalogReader() { final SchemaPlus rootSchema = rootSchema(defaultSchema); http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/prepare/Prepare.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java index 8d1a55b..226a27f 100644 --- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java +++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java @@ -31,6 +31,7 @@ import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelRoot; @@ -175,7 +176,7 @@ public abstract class Prepare { if (node instanceof TableScan) { final RelOptCluster cluster = node.getCluster(); final RelOptTable.ToRelContext context = - RelOptUtil.getContext(cluster); + ViewExpanders.simpleContext(cluster); final RelNode r = node.getTable().toRel(context); planner.registerClass(r); } @@ -398,11 +399,6 @@ public abstract class Prepare { return THREAD_TRIM.get() || RelOptUtil.countJoins(rootRel) < 2; } - public RelRoot expandView(RelDataType rowType, String queryString, - List<String> schemaPath, List<String> viewPath) { - throw new UnsupportedOperationException(); - } - protected abstract void init(Class runtimeContextClass); protected abstract SqlValidator getSqlValidator(); http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java b/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java index 5edf4cc..1269e9d 100644 --- a/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java +++ b/core/src/main/java/org/apache/calcite/prepare/QueryableRelBuilder.java @@ -99,7 +99,8 @@ class QueryableRelBuilder<T> implements QueryableFactory<T> { RelOptTableImpl.create(null, table.getRowType(translator.typeFactory), tableEntry, null); if (table instanceof TranslatableTable) { - return ((TranslatableTable) table).toRel(translator, relOptTable); + return ((TranslatableTable) table).toRel(translator.toRelContext(), + relOptTable); } else { return LogicalTableScan.create(translator.cluster, relOptTable); } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java index 5870f34..8baef30 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java +++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java @@ -20,6 +20,7 @@ import org.apache.calcite.plan.Contexts; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.logical.LogicalAggregate; @@ -37,6 +38,7 @@ import org.apache.calcite.rel.logical.LogicalValues; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.schema.TranslatableTable; import org.apache.calcite.sql.SemiJoinType; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.tools.RelBuilder; @@ -49,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; +import javax.annotation.Nonnull; /** * Contains factory interface and default implementation for creating various @@ -390,6 +393,47 @@ public class RelFactories { } /** + * Creates a {@link TableScanFactory} that can expand + * {@link TranslatableTable} instances, but explodes on views. + * + * @param tableScanFactory Factory for non-translatable tables + * @return Table scan factory + */ + @Nonnull public static TableScanFactory expandingScanFactory( + @Nonnull TableScanFactory tableScanFactory) { + return expandingScanFactory( + (rowType, queryString, schemaPath, viewPath) -> { + throw new UnsupportedOperationException("cannot expand view"); + }, + tableScanFactory); + } + + /** + * Creates a {@link TableScanFactory} that uses a + * {@link org.apache.calcite.plan.RelOptTable.ViewExpander} to handle + * {@link TranslatableTable} instances, and falls back to a default + * factory for other tables. + * + * @param viewExpander View expander + * @param tableScanFactory Factory for non-translatable tables + * @return Table scan factory + */ + @Nonnull public static TableScanFactory expandingScanFactory( + @Nonnull RelOptTable.ViewExpander viewExpander, + @Nonnull TableScanFactory tableScanFactory) { + return (cluster, table) -> { + final TranslatableTable translatableTable = + table.unwrap(TranslatableTable.class); + if (translatableTable != null) { + final RelOptTable.ToRelContext toRelContext = + ViewExpanders.toRelContext(viewExpander, cluster); + return translatableTable.toRel(toRelContext, table); + } + return tableScanFactory.createScan(cluster, table); + }; + } + + /** * Can create a {@link Match} of * the appropriate type for a rule's calling convention. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java index f1b6d6e..e974763 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java @@ -27,8 +27,8 @@ import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptRuleOperand; import org.apache.calcite.plan.RelOptTable; -import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.SubstitutionVisitor; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.prepare.CalcitePrepareImpl; import org.apache.calcite.prepare.RelOptTableImpl; import org.apache.calcite.rel.RelNode; @@ -148,7 +148,7 @@ public class AggregateStarTableRule extends RelOptRule { aggregateTableRowType, tableEntry, rowCount); - relBuilder.push(aggregateRelOptTable.toRel(RelOptUtil.getContext(cluster))); + relBuilder.push(aggregateRelOptTable.toRel(ViewExpanders.simpleContext(cluster))); if (tileKey == null) { if (CalcitePrepareImpl.DEBUG) { System.out.println("Using materialization " http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java index 05196d4..7d322fc 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptSemiJoinOptimizer.java @@ -19,6 +19,7 @@ package org.apache.calcite.rel.rules; import org.apache.calcite.plan.RelOptCost; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.JoinInfo; import org.apache.calcite.rel.core.SemiJoin; @@ -281,7 +282,7 @@ public class LoptSemiJoinOptimizer { final List<Integer> bestKeyOrder = new ArrayList<>(); LcsTableScan tmpFactRel = (LcsTableScan) factTable.toRel( - RelOptUtil.getContext(factRel.getCluster())); + ViewExpanders.simpleContext(factRel.getCluster())); LcsIndexOptimizer indexOptimizer = new LcsIndexOptimizer(tmpFactRel); FemLocalIndex bestIndex = http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/rel/rules/TableScanRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/TableScanRule.java b/core/src/main/java/org/apache/calcite/rel/rules/TableScanRule.java index 851c7c3..24dd9ff 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/TableScanRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/TableScanRule.java @@ -19,7 +19,7 @@ package org.apache.calcite.rel.rules; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptTable; -import org.apache.calcite.plan.RelOptUtil; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.logical.LogicalTableScan; @@ -53,7 +53,7 @@ public class TableScanRule extends RelOptRule { final LogicalTableScan oldRel = call.rel(0); RelNode newRel = oldRel.getTable().toRel( - RelOptUtil.getContext(oldRel.getCluster())); + ViewExpanders.simpleContext(oldRel.getCluster())); call.transformTo(newRel); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java index cb23cd1..0c21e64 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java @@ -24,6 +24,8 @@ import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelRoot; +import org.apache.calcite.rel.RelShuttleImpl; +import org.apache.calcite.rel.core.TableScan; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelProtoDataType; @@ -118,13 +120,26 @@ public class ViewTable return expandView(context, relOptTable.getRowType(), viewSql).rel; } - private RelRoot expandView(RelOptTable.ToRelContext preparingStmt, + private RelRoot expandView(RelOptTable.ToRelContext context, RelDataType rowType, String queryString) { try { - RelRoot root = preparingStmt.expandView(rowType, queryString, schemaPath, viewPath); - - root = root.withRel(RelOptUtil.createCastRel(root.rel, rowType, true)); - return root; + final RelRoot root = + context.expandView(rowType, queryString, schemaPath, viewPath); + final RelNode rel = RelOptUtil.createCastRel(root.rel, rowType, true); + // Expand any views + final RelNode rel2 = rel.accept( + new RelShuttleImpl() { + @Override public RelNode visit(TableScan scan) { + final RelOptTable table = scan.getTable(); + final TranslatableTable translatableTable = + table.unwrap(TranslatableTable.class); + if (translatableTable != null) { + return translatableTable.toRel(context, table); + } + return super.visit(scan); + } + }); + return root.withRel(rel2); } catch (Exception e) { throw new RuntimeException("Error while parsing view definition: " + queryString, e); http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java index e694d19..10cbfa1 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java @@ -101,11 +101,9 @@ import com.google.common.collect.SortedSetMultimap; import org.slf4j.Logger; import java.math.BigDecimal; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -2673,8 +2671,6 @@ public class RelDecorrelator implements ReflectiveVisitor { final Holder<Integer> offset = Holder.of(0); int corrIdGenerator = 0; - final Deque<RelNode> stack = new ArrayDeque<>(); - /** Creates a CorelMap by iterating over a {@link RelNode} tree. */ CorelMap build(RelNode... rels) { for (RelNode rel : rels) { http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java index 6c8cb15..9750664 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -25,6 +25,7 @@ import org.apache.calcite.plan.RelOptSamplingParameters; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.plan.ViewExpanders; import org.apache.calcite.prepare.Prepare; import org.apache.calcite.prepare.RelOptTableImpl; import org.apache.calcite.rel.RelCollation; @@ -3207,20 +3208,7 @@ public class SqlToRelConverter { } private RelOptTable.ToRelContext createToRelContext() { - return new RelOptTable.ToRelContext() { - public RelOptCluster getCluster() { - return cluster; - } - - @Override public RelRoot expandView( - RelDataType rowType, - String queryString, - List<String> schemaPath, - List<String> viewPath) { - return viewExpander.expandView(rowType, queryString, schemaPath, viewPath); - } - - }; + return ViewExpanders.toRelContext(viewExpander, cluster); } public RelNode toRel(final RelOptTable table) { http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java b/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java index ace0226..e4a62dd 100644 --- a/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java +++ b/core/src/main/java/org/apache/calcite/tools/FrameworkConfig.java @@ -19,6 +19,7 @@ package org.apache.calcite.tools; import org.apache.calcite.materialize.SqlStatisticProvider; import org.apache.calcite.plan.Context; import org.apache.calcite.plan.RelOptCostFactory; +import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelTraitDef; import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.rex.RexExecutor; @@ -133,6 +134,11 @@ public interface FrameworkConfig { * direction of relationships. */ SqlStatisticProvider getStatisticProvider(); + + /** + * Returns a view expander. + */ + RelOptTable.ViewExpander getViewExpander(); } // End FrameworkConfig.java http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/tools/Frameworks.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/tools/Frameworks.java b/core/src/main/java/org/apache/calcite/tools/Frameworks.java index e5d83fb..49cd1ac 100644 --- a/core/src/main/java/org/apache/calcite/tools/Frameworks.java +++ b/core/src/main/java/org/apache/calcite/tools/Frameworks.java @@ -24,6 +24,7 @@ import org.apache.calcite.plan.Context; import org.apache.calcite.plan.RelOptCluster; import org.apache.calcite.plan.RelOptCostFactory; import org.apache.calcite.plan.RelOptSchema; +import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelTraitDef; import org.apache.calcite.prepare.CalcitePrepareImpl; import org.apache.calcite.prepare.PlannerImpl; @@ -202,6 +203,7 @@ public class Frameworks { private RelDataTypeSystem typeSystem; private boolean evolveLattice; private SqlStatisticProvider statisticProvider; + private RelOptTable.ViewExpander viewExpander; /** Creates a ConfigBuilder, initializing to defaults. */ private ConfigBuilder() { @@ -236,7 +238,7 @@ public class Frameworks { return new StdFrameworkConfig(context, convertletTable, operatorTable, programs, traitDefs, parserConfig, sqlToRelConverterConfig, defaultSchema, costFactory, typeSystem, executor, evolveLattice, - statisticProvider); + statisticProvider, viewExpander); } public ConfigBuilder context(Context c) { @@ -329,6 +331,11 @@ public class Frameworks { this.statisticProvider = Objects.requireNonNull(statisticProvider); return this; } + + public ConfigBuilder viewExpander(RelOptTable.ViewExpander viewExpander) { + this.viewExpander = viewExpander; + return this; + } } /** @@ -349,6 +356,7 @@ public class Frameworks { private final RexExecutor executor; private final boolean evolveLattice; private final SqlStatisticProvider statisticProvider; + private final RelOptTable.ViewExpander viewExpander; StdFrameworkConfig(Context context, SqlRexConvertletTable convertletTable, @@ -362,7 +370,8 @@ public class Frameworks { RelDataTypeSystem typeSystem, RexExecutor executor, boolean evolveLattice, - SqlStatisticProvider statisticProvider) { + SqlStatisticProvider statisticProvider, + RelOptTable.ViewExpander viewExpander) { this.context = context; this.convertletTable = convertletTable; this.operatorTable = operatorTable; @@ -376,6 +385,7 @@ public class Frameworks { this.executor = executor; this.evolveLattice = evolveLattice; this.statisticProvider = statisticProvider; + this.viewExpander = viewExpander; } public SqlParser.Config getParserConfig() { @@ -429,6 +439,10 @@ public class Frameworks { public SqlStatisticProvider getStatisticProvider() { return statisticProvider; } + + public RelOptTable.ViewExpander getViewExpander() { + return viewExpander; + } } } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/main/java/org/apache/calcite/tools/RelBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index d13df3c..d2b5d5d 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -933,6 +933,7 @@ public class RelBuilder { } final RelNode scan = scanFactory.createScan(cluster, relOptTable); push(scan); + rename(relOptTable.getRowType().getFieldNames()); return this; } http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/test/java/org/apache/calcite/test/CalciteAssert.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java index d6a0efd..4519d04 100644 --- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java +++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java @@ -768,6 +768,11 @@ public class CalciteAssert { + " (40, 'Empty')) as t(deptno, dname)", ImmutableList.of(), ImmutableList.of("POST", "DEPT"), null)); + post.add("DEPT30", + ViewTable.viewMacro(post, + "select * from dept where deptno = 30", + ImmutableList.of("POST"), ImmutableList.of("POST", "DEPT30"), + null)); post.add("EMPS", ViewTable.viewMacro(post, "select * from (values\n" http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java index 880a1a9..3624204 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -17,12 +17,15 @@ package org.apache.calcite.test; import org.apache.calcite.jdbc.CalciteConnection; +import org.apache.calcite.plan.Contexts; +import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelTraitDef; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.AggregateCall; import org.apache.calcite.rel.core.Correlate; import org.apache.calcite.rel.core.Exchange; import org.apache.calcite.rel.core.JoinRelType; +import org.apache.calcite.rel.core.RelFactories; import org.apache.calcite.rel.core.TableFunctionScan; import org.apache.calcite.rel.core.TableModify; import org.apache.calcite.rel.core.Window; @@ -56,13 +59,14 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import org.junit.Ignore; +import org.hamcrest.Matcher; import org.junit.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -122,6 +126,30 @@ public class RelBuilderTest { .programs(Programs.heuristicJoinOrder(Programs.RULE_SET, true, 2)); } + /** Creates a config builder that will contain a view, "MYVIEW", and also + * the SCOTT JDBC schema, whose tables implement + * {@link org.apache.calcite.schema.TranslatableTable}. */ + static Frameworks.ConfigBuilder expandingConfig(Connection connection) + throws SQLException { + final CalciteConnection calciteConnection = + connection.unwrap(CalciteConnection.class); + final SchemaPlus root = calciteConnection.getRootSchema(); + CalciteAssert.SchemaSpec spec = CalciteAssert.SchemaSpec.SCOTT; + CalciteAssert.addSchema(root, spec); + final String viewSql = + String.format(Locale.ROOT, "select * from \"%s\".\"%s\" where 1=1", + spec.schemaName, "EMP"); + + // create view + ViewTableMacro macro = ViewTable.viewMacro(root, viewSql, + Collections.singletonList("test"), Arrays.asList("test", "view"), false); + + // register view (in root schema) + root.add("MYVIEW", macro); + + return Frameworks.newConfigBuilder().defaultSchema(root); + } + @Test public void testScan() { // Equivalent SQL: // SELECT * @@ -2189,38 +2217,67 @@ public class RelBuilderTest { * * <p>This test currently fails (thus ignored). */ - @Ignore("https://issues.apache.org/jira/browse/CALCITE-2441") - @Test public void testExpandViewInRelBuilder() throws Exception { - final Connection connection = DriverManager.getConnection("jdbc:calcite:"); - final SchemaPlus root = connection.unwrap(CalciteConnection.class).getRootSchema(); - CalciteAssert.SchemaSpec spec = CalciteAssert.SchemaSpec.SCOTT; - CalciteAssert.addSchema(root, spec); - final String viewSql = - String.format(Locale.ROOT, "select * from \"%s\".\"%s\" where 1=1", - spec.schemaName, "EMP"); - - // create view - ViewTableMacro macro = ViewTable.viewMacro(root, viewSql, - Collections.singletonList("test"), Arrays.asList("test", "view"), false); - - // register view (in root schema) - root.add("MYVIEW", macro); - - FrameworkConfig config = Frameworks.newConfigBuilder().defaultSchema(root).build(); - RelNode node = RelBuilder.create(config).scan("MYVIEW").build(); - - int count = 0; - try (PreparedStatement statement = - connection.unwrap(RelRunner.class).prepare(node); - ResultSet resultSet = statement.executeQuery()) { - while (resultSet.next()) { - count++; + @Test public void testExpandViewInRelBuilder() throws SQLException { + try (Connection connection = DriverManager.getConnection("jdbc:calcite:")) { + final Frameworks.ConfigBuilder configBuilder = + expandingConfig(connection); + final RelOptTable.ViewExpander viewExpander = + (RelOptTable.ViewExpander) Frameworks.getPlanner(configBuilder.build()); + final RelFactories.TableScanFactory tableScanFactory = + RelFactories.expandingScanFactory(viewExpander, + RelFactories.DEFAULT_TABLE_SCAN_FACTORY); + configBuilder.context(Contexts.of(tableScanFactory)); + final RelBuilder builder = RelBuilder.create(configBuilder.build()); + RelNode node = builder.scan("MYVIEW").build(); + + int count = 0; + try (PreparedStatement statement = + connection.unwrap(RelRunner.class).prepare(node); + ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + count++; + } } + + assertTrue(count > 1); } + } - assertTrue(count > 1); + @Test public void testExpandTable() throws SQLException { + final RelOptTable.ViewExpander viewExpander = + (rowType, queryString, schemaPath, viewPath) -> null; + final RelFactories.TableScanFactory tableScanFactory = + RelFactories.expandingScanFactory(viewExpander, + RelFactories.DEFAULT_TABLE_SCAN_FACTORY); + try (Connection connection = DriverManager.getConnection("jdbc:calcite:")) { + // First, use a non-expanding RelBuilder. Plan contains LogicalTableScan. + final Frameworks.ConfigBuilder configBuilder = + expandingConfig(connection); + final RelBuilder builder = RelBuilder.create(configBuilder.build()); + final String expected = "LogicalFilter(condition=[>($2, 10)])\n" + + " LogicalTableScan(table=[[JDBC_SCOTT, EMP]])\n"; + checkExpandTable(builder, hasTree(expected)); + + // Next, use an expanding RelBuilder. Plan contains JdbcTableScan, + // because RelBuilder.scan has called RelOptTable.toRel. + final FrameworkConfig config = configBuilder + .context(Contexts.of(tableScanFactory)).build(); + final RelBuilder builder2 = RelBuilder.create(config); + final String expected2 = "LogicalFilter(condition=[>($2, 10)])\n" + + " JdbcTableScan(table=[[JDBC_SCOTT, EMP]])\n"; + checkExpandTable(builder2, hasTree(expected2)); + } } + private void checkExpandTable(RelBuilder builder, Matcher<RelNode> matcher) { + final RelNode root = + builder.scan("JDBC_SCOTT", "EMP") + .filter( + builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field(2), + builder.literal(10))) + .build(); + assertThat(root, matcher); + } } // End RelBuilderTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/9c26a9e7/core/src/test/java/org/apache/calcite/tools/PlannerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java index 8931458..e0ed1c1 100644 --- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java +++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java @@ -77,6 +77,7 @@ import org.apache.calcite.util.Util; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; +import org.hamcrest.Matcher; import org.junit.Ignore; import org.junit.Test; @@ -88,6 +89,7 @@ import static org.apache.calcite.plan.RelOptRule.operand; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -1132,6 +1134,44 @@ public class PlannerTest { Planner planner = getPlanner(null, Programs.of(ruleSet)); planner.close(); } + + @Test public void testView() throws Exception { + final String sql = "select * FROM dept"; + final String expected = "LogicalProject(DEPTNO=[$0], DNAME=[$1])\n" + + " LogicalValues(type=[RecordType(INTEGER DEPTNO, CHAR(11) DNAME)], " + + "tuples=[[{ 10, 'Sales ' }," + + " { 20, 'Marketing ' }," + + " { 30, 'Engineering' }," + + " { 40, 'Empty ' }]])\n"; + checkView(sql, is(expected)); + } + + @Test public void testViewOnView() throws Exception { + final String sql = "select * FROM dept30"; + final String expected = "LogicalProject(DEPTNO=[$0], DNAME=[$1])\n" + + " LogicalFilter(condition=[=($0, 30)])\n" + + " LogicalProject(DEPTNO=[$0], DNAME=[$1])\n" + + " LogicalValues(type=[RecordType(INTEGER DEPTNO, CHAR(11) DNAME)], " + + "tuples=[[{ 10, 'Sales ' }," + + " { 20, 'Marketing ' }," + + " { 30, 'Engineering' }," + + " { 40, 'Empty ' }]])\n"; + checkView(sql, is(expected)); + } + + private void checkView(String sql, Matcher<String> matcher) + throws SqlParseException, ValidationException, RelConversionException { + final SchemaPlus rootSchema = Frameworks.createRootSchema(true); + final FrameworkConfig config = Frameworks.newConfigBuilder() + .defaultSchema( + CalciteAssert.addSchema(rootSchema, CalciteAssert.SchemaSpec.POST)) + .build(); + final Planner planner = Frameworks.getPlanner(config); + SqlNode parse = planner.parse(sql); + final SqlNode validate = planner.validate(parse); + final RelRoot root = planner.rel(validate); + assertThat(toString(root.rel), matcher); + } } // End PlannerTest.java
