You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2016/11/01 22:20:56 UTC
calcite git commit: [CALCITE-1389] Add a rewrite rule to use
materialized views with joins
Repository: calcite
Updated Branches:
refs/heads/master 03d6b00cd -> 435b6d47b
[CALCITE-1389] Add a rewrite rule to use materialized views with joins
Close apache/calcite#284
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/435b6d47
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/435b6d47
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/435b6d47
Branch: refs/heads/master
Commit: 435b6d47b195f55e4a926da3ba27f3bb0e5699cc
Parents: 03d6b00
Author: Michael Mior <mm...@uwaterloo.ca>
Authored: Tue Sep 27 14:26:43 2016 -0400
Committer: Michael Mior <mm...@uwaterloo.ca>
Committed: Tue Nov 1 18:19:32 2016 -0400
----------------------------------------------------------------------
.../org/apache/calcite/plan/RelOptUtil.java | 25 ++
.../calcite/plan/volcano/VolcanoPlanner.java | 22 +-
.../rel/rules/MaterializedViewJoinRule.java | 372 +++++++++++++++++++
.../calcite/test/MaterializationTest.java | 64 +++-
4 files changed, 460 insertions(+), 23 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/435b6d47/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 28cf336..7cb31a4 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -34,6 +34,7 @@ import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
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.externalize.RelJsonWriter;
import org.apache.calcite.rel.externalize.RelWriterImpl;
import org.apache.calcite.rel.externalize.RelXmlWriter;
@@ -197,6 +198,30 @@ public abstract class RelOptUtil {
}
/**
+ * Returns a set of tables used by this expression or its children
+ */
+ public static Set<RelOptTable> findTables(RelNode rel) {
+ return new LinkedHashSet<RelOptTable>(findAllTables(rel));
+ }
+
+ /**
+ * Returns a list of all tables used by this expression or its children
+ */
+ public static List<RelOptTable> findAllTables(RelNode rel) {
+ final List<RelOptTable> usedTables = new ArrayList<>();
+ new RelVisitor() {
+ @Override public void visit(RelNode node, int ordinal, RelNode parent) {
+ if (node instanceof TableScan) {
+ usedTables.add(node.getTable());
+ }
+ super.visit(node, ordinal, parent);
+ }
+ // CHECKSTYLE: IGNORE 1
+ }.go(rel);
+ return usedTables;
+ }
+
+ /**
* Returns a list of variables set by a relational expression or its
* descendants.
*/
http://git-wip-us.apache.org/repos/asf/calcite/blob/435b6d47/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index 9e02adf..dd96e21 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -47,7 +47,6 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelVisitor;
import org.apache.calcite.rel.convert.Converter;
import org.apache.calcite.rel.convert.ConverterRule;
-import org.apache.calcite.rel.core.TableScan;
import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
@@ -103,7 +102,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -460,7 +458,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
final List<RelOptMaterialization> applicableMaterializations =
getApplicableMaterializations(originalRoot, materializations);
useMaterializations(originalRoot, applicableMaterializations);
- final Set<RelOptTable> queryTables = findTables(originalRoot);
+ final Set<RelOptTable> queryTables = RelOptUtil.findTables(originalRoot);
// Use a lattice if the query uses at least the central (fact) table of the
// lattice.
@@ -503,7 +501,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
final List<String> qname = materialization.table.getQualifiedName();
qnameMap.put(qname, materialization);
for (RelOptTable usedTable
- : findTables(materialization.queryRel)) {
+ : RelOptUtil.findTables(materialization.queryRel)) {
usesGraph.addVertex(qname);
usesGraph.addVertex(usedTable.getQualifiedName());
usesGraph.addEdge(usedTable.getQualifiedName(), qname);
@@ -516,7 +514,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
// actually use.)
final Graphs.FrozenGraph<List<String>, DefaultEdge> frozenGraph =
Graphs.makeImmutable(usesGraph);
- final Set<RelOptTable> queryTablesUsed = findTables(root);
+ final Set<RelOptTable> queryTablesUsed = RelOptUtil.findTables(root);
final List<RelOptMaterialization> applicableMaterializations = Lists.newArrayList();
for (List<String> qname : TopologicalOrderIterator.of(usesGraph)) {
RelOptMaterialization materialization = qnameMap.get(qname);
@@ -546,20 +544,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
return false;
}
- private static Set<RelOptTable> findTables(RelNode rel) {
- final Set<RelOptTable> usedTables = new LinkedHashSet<>();
- new RelVisitor() {
- @Override public void visit(RelNode node, int ordinal, RelNode parent) {
- if (node instanceof TableScan) {
- usedTables.add(node.getTable());
- }
- super.visit(node, ordinal, parent);
- }
- // CHECKSTYLE: IGNORE 1
- }.go(rel);
- return usedTables;
- }
-
/**
* Finds an expression's equivalence set. If the expression is not
* registered, returns null.
http://git-wip-us.apache.org/repos/asf/calcite/blob/435b6d47/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java
new file mode 100644
index 0000000..4c0a84b
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java
@@ -0,0 +1,372 @@
+/*
+ * 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.rel.rules;
+
+import org.apache.calcite.plan.RelOptMaterialization;
+import org.apache.calcite.plan.RelOptPlanner;
+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.hep.HepPlanner;
+import org.apache.calcite.plan.hep.HepProgram;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
+import org.apache.calcite.plan.volcano.VolcanoPlanner;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.tools.RelBuilderFactory;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Planner rule that converts joins of multiple tables into a matching
+ * materialized view
+ */
+public class MaterializedViewJoinRule extends RelOptRule {
+ public static final MaterializedViewJoinRule INSTANCE_PROJECT =
+ new MaterializedViewJoinRule(
+ operand(LogicalProject.class,
+ operand(Join.class,
+ operand(Project.class,
+ operand(TableScan.class, none())),
+ operand(Project.class,
+ operand(TableScan.class, none())))),
+ RelFactories.LOGICAL_BUILDER,
+ "MaterializedViewJoinRule(Project-Project)");
+
+ public static final MaterializedViewJoinRule INSTANCE_TABLE_SCAN =
+ new MaterializedViewJoinRule(
+ operand(LogicalProject.class,
+ operand(Join.class,
+ operand(TableScan.class, none()),
+ operand(TableScan.class, none()))),
+ RelFactories.LOGICAL_BUILDER,
+ "MaterializedViewJoinRule(TableScan-TableScan)");
+
+ private final HepProgram multiJoinProgram = new HepProgramBuilder()
+ .addRuleInstance(ProjectRemoveRule.INSTANCE)
+ .addRuleInstance(ProjectJoinTransposeRule.INSTANCE)
+ .addRuleInstance(JoinToMultiJoinRule.INSTANCE)
+ .addRuleInstance(ProjectMultiJoinMergeRule.INSTANCE)
+ .addRuleInstance(FilterMultiJoinMergeRule.INSTANCE)
+ .build();
+
+ //~ Constructors -----------------------------------------------------------
+
+ /** Creates a MaterializedViewJoinRule. */
+ protected MaterializedViewJoinRule(RelOptRuleOperand operand, RelBuilderFactory relBuilderFactory,
+ String description) {
+ super(operand, relBuilderFactory, description);
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ public void onMatch(RelOptRuleCall call) {
+ final Project originalProject = call.rel(0);
+ // Rebuild the tree
+ final RelNode leftInput;
+ final RelNode rightInput;
+ if (call.getRelList().size() == 6) {
+ leftInput = call.rel(2).copy(call.rel(2).getTraitSet(), ImmutableList.of(call.rel(3)));
+ rightInput = call.rel(4).copy(call.rel(4).getTraitSet(), ImmutableList.of(call.rel(5)));
+ } else {
+ leftInput = call.rel(2);
+ rightInput = call.rel(3);
+ }
+ final RelNode join = call.rel(1).copy(call.rel(1).getTraitSet(),
+ ImmutableList.of(leftInput, rightInput));
+ final RelNode project = call.rel(0).copy(call.rel(0).getTraitSet(), ImmutableList.of(join));
+
+ // Convert the input expression into a MultiJoin
+ RelOptPlanner planner = call.getPlanner();
+ final HepPlanner hepPlanner =
+ new HepPlanner(multiJoinProgram, planner.getContext());
+ hepPlanner.setRoot(project);
+ RelNode best = hepPlanner.findBestExp();
+
+ if (best instanceof Project) {
+ best = ((Project) best).getInput();
+ }
+ if (!(best instanceof MultiJoin)) {
+ return;
+ }
+ apply(call, (MultiJoin) best, originalProject);
+ }
+
+ protected void apply(RelOptRuleCall call, MultiJoin join, Project originalProject) {
+ if (!isSupportedJoin(join)) {
+ return;
+ }
+ SortedMap<Integer, ImmutableBitSet> queryFilter = filterConditions(join);
+ if (queryFilter == null) {
+ return;
+ }
+ List<RelOptTable> queryTables = RelOptUtil.findAllTables(join);
+ Map<Integer, Pair<RelOptTable, RexInputRef>> queryFields =
+ originalFields(join, queryTables);
+ if (queryFields == null) {
+ return;
+ }
+
+ RelOptPlanner planner = call.getPlanner();
+ List<RelOptMaterialization> materializations =
+ planner instanceof VolcanoPlanner
+ ? ((VolcanoPlanner) planner).getMaterializations()
+ : ImmutableList.<RelOptMaterialization>of();
+ if (!materializations.isEmpty()) {
+ List<RelOptMaterialization> applicableMaterializations =
+ VolcanoPlanner.getApplicableMaterializations(join, materializations);
+
+ // Prepare a planner to convert views to MultiJoins
+ HepPlanner hepPlanner =
+ new HepPlanner(multiJoinProgram, planner.getContext());
+
+ for (RelOptMaterialization materialization : applicableMaterializations) {
+ // Skip over single table views
+ RelNode target = materialization.queryRel;
+ if (target instanceof TableScan
+ || (target instanceof Project
+ && ((Project) target).getInput() instanceof TableScan)) {
+ continue;
+ }
+
+ // Convert the view into a MultiJoin
+ hepPlanner.setRoot(target);
+ target = hepPlanner.findBestExp();
+ if (!(target instanceof Project)) { continue; }
+
+ Project viewProject = (Project) target;
+ if (!(viewProject.getInput() instanceof MultiJoin)) {
+ continue;
+ }
+ MultiJoin viewJoin = (MultiJoin) viewProject.getInput();
+ if (!isSupportedJoin(viewJoin)) {
+ continue;
+ }
+
+ List<RelOptTable> viewTables = RelOptUtil.findAllTables(viewJoin);
+
+ // Check that the same set of tables are in use
+ if (queryTables.size() != viewTables.size()
+ || !ImmutableSet.copyOf(queryTables).containsAll(viewTables)) {
+ continue;
+ }
+
+ // Extra the conditions and field from the view and ensure
+ // that they are all supported
+ SortedMap<Integer, ImmutableBitSet> viewFilter = filterConditions(viewJoin);
+ if (viewFilter == null) {
+ continue;
+ }
+ Map<Integer, Pair<RelOptTable, RexInputRef>> viewFields =
+ originalFields(viewJoin, viewTables);
+ if (viewFields == null) {
+ continue;
+ }
+
+ // If we fail to find one of the fields we are required
+ // to project, we can't use this view
+ List<RexNode> projects = materializedViewProjects(queryFields, queryFilter,
+ viewFields, originalProject);
+ if (projects.size() != originalProject.getNamedProjects().size()) {
+ continue;
+ }
+
+ final RelNode newNode = originalProject.copy(originalProject.getTraitSet(),
+ materialization.tableRel,
+ projects, originalProject.getRowType());
+ call.transformTo(newNode);
+ }
+ }
+ }
+
+ /**
+ * Checks that the join consists of either table scans or projects of scans
+ */
+ private boolean isSimpleProjects(MultiJoin join) {
+ for (RelNode input : join.getInputs()) {
+ if (!(input instanceof TableScan)
+ && !(input instanceof Project
+ && ((Project) input).getInput() instanceof TableScan)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isSupportedJoin(MultiJoin join) {
+ // We only support inner joins without post join filters over simple projects/scans
+ return !join.containsOuter() && join.getPostJoinFilter() == null && isSimpleProjects(join);
+ }
+
+ /**
+ * Produces a map from fields in a multijoin to references in the
+ * original tables referenced in the join
+ */
+ private Map<Integer, Pair<RelOptTable, RexInputRef>> originalFields(MultiJoin join,
+ List<RelOptTable> tables) {
+ List<ImmutableBitSet> projFields = join.getProjFields();
+ Map<Integer, Pair<RelOptTable, RexInputRef>> tableFields = new LinkedHashMap<>();
+ List<RelNode> inputs = join.getInputs();
+ int fieldNum = 0;
+ for (int i = 0; i < projFields.size(); i++) {
+ // Either get the project or construct a list projecting all fields
+ List<RexNode> projects;
+ if (inputs.get(i) instanceof Project) {
+ projects = ((Project) inputs.get(i)).getProjects();
+ } else {
+ assert inputs.get(i) instanceof TableScan;
+ List<RelDataTypeField> fields = inputs.get(i).getRowType().getFieldList();
+ projects = new ArrayList<>();
+ for (int j = 0; j < fields.size(); j++) {
+ projects.add(new RexInputRef(j, fields.get(j).getType()));
+ }
+ }
+
+ if (projFields.get(i) == null) { return null; }
+
+ int bit = projFields.get(i).nextSetBit(0);
+ while (bit != -1) {
+ // We currently only support rewriting of views with simple field references
+ if (!(projects.get(bit) instanceof RexInputRef)) {
+ return null;
+ }
+
+ tableFields.put(fieldNum, Pair.of(tables.get(i), (RexInputRef) projects.get(bit)));
+ fieldNum++;
+ bit = projFields.get(i).nextSetBit(bit + 1);
+ }
+ }
+
+ return tableFields;
+ }
+
+ /**
+ * If the node represents a field reference, get its index
+ */
+ private Integer getFieldIndex(RexNode operand) {
+ if (operand.isA(SqlKind.INPUT_REF)) {
+ return ((RexInputRef) operand).getIndex();
+ } else if (operand.isA(SqlKind.CAST)) {
+ return getFieldIndex(((RexCall) operand).getOperands().get(0));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Construct a map of equivalence classes of all columns
+ * in all tables used as input to the join
+ */
+ private SortedMap<Integer, ImmutableBitSet> filterConditions(MultiJoin join) {
+ SortedMap<Integer, ImmutableBitSet> equiv = new TreeMap<>();
+ RexNode filter = RexUtil.toCnf(join.getCluster().getRexBuilder(), join.getJoinFilter());
+ for (RexNode conjunct : RelOptUtil.conjunctions(filter)) {
+ List<RexNode> condition = RelOptUtil.disjunctions(conjunct);
+ if (condition.size() == 1 && condition.get(0).isA(SqlKind.EQUALS)) {
+ List<RexNode> operands = ((RexCall) condition.get(0)).getOperands();
+ Integer index1 = getFieldIndex(operands.get(0));
+ Integer index2 = getFieldIndex(operands.get(1));
+ if (index1 == null || index2 == null) {
+ // All operands to a condition must be field references or
+ // simple casts of field references
+ return null;
+ }
+ equiv.put(index1, ImmutableBitSet.of(index1, index2));
+ } else {
+ // We don't handle disjunctions or inequalities
+ return null;
+ }
+ }
+
+ equiv = ImmutableBitSet.closure(equiv);
+ return equiv;
+ }
+
+
+ /**
+ * Construct a list of projects we need on top of the materialized view
+ */
+ private List<RexNode> materializedViewProjects(
+ Map<Integer, Pair<RelOptTable, RexInputRef>> queryFields,
+ SortedMap<Integer, ImmutableBitSet> queryFilter,
+ Map<Integer, Pair<RelOptTable, RexInputRef>> viewFields,
+ Project originalProject) {
+ List<Pair<RelOptTable, RexInputRef>> viewFieldList =
+ Lists.newArrayList(viewFields.values().iterator());
+ List<Pair<RelOptTable, RexInputRef>> queryFieldList =
+ Lists.newArrayList(queryFields.values().iterator());
+ List<RexNode> projects = new ArrayList<>();
+ for (Map.Entry<Integer, Pair<RelOptTable, RexInputRef>> field
+ : queryFields.entrySet()) {
+ int fieldIndex = viewFieldList.indexOf(field.getValue());
+ if (fieldIndex == -1) {
+ // Check for equivalent fields in the view
+ ImmutableBitSet queryEquiv = queryFilter.get(field.getKey());
+ if (queryEquiv != null) {
+ for (Integer index : queryEquiv) {
+ fieldIndex = viewFieldList.indexOf(queryFieldList.get(index));
+ if (fieldIndex != -1) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (fieldIndex == -1) {
+ break;
+ }
+
+ RelDataType type = field.getValue().right.getType();
+ RelDataType originalType = originalProject.getProjects().get(projects.size()).getType();
+ // if (originalType.equals(type)) {
+ if (!SqlTypeUtil.canCastFrom(originalType, type, false)) {
+ continue;
+ }
+ projects.add(new RexInputRef(fieldIndex, type));
+ }
+
+ return projects;
+ }
+}
+
+// End MaterializedViewJoinRule.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/435b6d47/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
index 117f7fa..c22707a 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -18,12 +18,16 @@ package org.apache.calcite.test;
import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.materialize.MaterializationService;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.plan.SubstitutionVisitor;
import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelVisitor;
import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.rules.MaterializedViewJoinRule;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rex.RexBuilder;
@@ -33,7 +37,13 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.runtime.Hook;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.tools.Program;
+import org.apache.calcite.tools.Programs;
+import org.apache.calcite.tools.RuleSet;
+import org.apache.calcite.tools.RuleSets;
+import org.apache.calcite.util.Holder;
import org.apache.calcite.util.JsonBuilder;
+import org.apache.calcite.util.Pair;
import org.apache.calcite.util.TryThreadLocal;
import org.apache.calcite.util.Util;
@@ -146,20 +156,56 @@ public class MaterializationTest {
/** Checks that a given query can use a materialized view with a given
* definition. */
private void checkMaterialize(String materialize, String query) {
- checkMaterialize(materialize, query, JdbcTest.HR_MODEL, CONTAINS_M0);
+ checkMaterialize(materialize, query, JdbcTest.HR_MODEL, CONTAINS_M0,
+ RuleSets.ofList(ImmutableList.<RelOptRule>of()));
+ }
+
+ /** Checks that a given query can use a materialized view with a given
+ * definition. */
+ private void checkMaterializeWithRules(String materialize, String query, RuleSet rules) {
+ checkMaterialize(materialize, query, JdbcTest.HR_MODEL, CONTAINS_M0, rules);
}
/** Checks that a given query can use a materialized view with a given
* definition. */
private void checkMaterialize(String materialize, String query, String model,
Function<ResultSet, Void> explainChecker) {
+ checkMaterialize(materialize, query, model, explainChecker,
+ RuleSets.ofList(ImmutableList.<RelOptRule>of()));
+ }
+
+ /** Checks that a given query can use a materialized view with a given
+ * definition. */
+ private void checkMaterialize(String materialize, String query, String model,
+ Function<ResultSet, Void> explainChecker, final RuleSet rules) {
try (final TryThreadLocal.Memo ignored = Prepare.THREAD_TRIM.push(true)) {
MaterializationService.setThreadLocal();
- CalciteAssert.that()
+ CalciteAssert.AssertQuery that = CalciteAssert.that()
.withMaterializations(model, "m0", materialize)
.query(query)
- .enableMaterializations(true)
- .explainMatches("", explainChecker)
+ .enableMaterializations(true);
+
+ // Add any additional rules required for the test
+ if (rules.iterator().hasNext()) {
+ that.withHook(Hook.PROGRAM,
+ new Function<Pair<List<Prepare.Materialization>, Holder<Program>>,
+ Void>() {
+ public Void apply(Pair<List<Prepare.Materialization>, Holder<Program>> pair) {
+ pair.right.set(new Program() {
+ public RelNode run(RelOptPlanner planner, RelNode rel,
+ RelTraitSet requiredOutputTraits) {
+ for (RelOptRule rule : rules) {
+ planner.addRule(rule);
+ }
+ return Programs.standard().run(planner, rel, requiredOutputTraits);
+ }
+ });
+ return null;
+ }
+ });
+ }
+
+ that.explainMatches("", explainChecker)
.sameResultWithMaterializationsDisabled();
}
}
@@ -873,6 +919,16 @@ public class MaterializationTest {
checkMaterialize(m, q);
}
+ @Test public void testJoinMaterialization3() {
+ String q = "select \"empid\" \"deptno\" from \"emps\"\n"
+ + "join \"depts\" using (\"deptno\") where \"empid\" = 1";
+ final String m = "select \"empid\" \"deptno\" from \"emps\"\n"
+ + "join \"depts\" using (\"deptno\")";
+ RuleSet rules = RuleSets.ofList(MaterializedViewJoinRule.INSTANCE_PROJECT,
+ MaterializedViewJoinRule.INSTANCE_TABLE_SCAN);
+ checkMaterializeWithRules(m, q, rules);
+ }
+
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-761">[CALCITE-761]
* Pre-populated materializations</a>. */