calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maryann...@apache.org
Subject calcite git commit: [CALCITE-1500] Decouple materialization and lattice substitution from VolcanoPlanner
Date Mon, 30 Jan 2017 22:28:13 GMT
Repository: calcite
Updated Branches:
  refs/heads/master 0187cfc53 -> 972b5de52


[CALCITE-1500] Decouple materialization and lattice substitution from VolcanoPlanner


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/972b5de5
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/972b5de5
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/972b5de5

Branch: refs/heads/master
Commit: 972b5de52dcacfa5d04d8af57716d23144dd93cc
Parents: 0187cfc
Author: maryannxue <maryann.xue@gmail.com>
Authored: Mon Jan 30 14:28:06 2017 -0800
Committer: maryannxue <maryann.xue@gmail.com>
Committed: Mon Jan 30 14:28:06 2017 -0800

----------------------------------------------------------------------
 .../calcite/plan/RelOptMaterialization.java     |   8 +-
 .../calcite/plan/RelOptMaterializations.java    | 234 +++++++++++++++++++
 .../org/apache/calcite/plan/RelOptPlanner.java  |   8 +-
 .../org/apache/calcite/plan/hep/HepPlanner.java |   4 +
 .../apache/calcite/plan/volcano/RuleQueue.java  |   3 +
 .../calcite/plan/volcano/VolcanoPlanner.java    | 233 ++++--------------
 .../calcite/prepare/CalcitePrepareImpl.java     |   2 +-
 .../org/apache/calcite/prepare/PlannerImpl.java |   6 +-
 .../org/apache/calcite/prepare/Prepare.java     |  26 +--
 .../rules/MaterializedViewFilterScanRule.java   |   3 +-
 .../rel/rules/MaterializedViewJoinRule.java     |   3 +-
 .../java/org/apache/calcite/tools/Program.java  |   8 +-
 .../java/org/apache/calcite/tools/Programs.java |  55 ++++-
 .../rel/rel2sql/RelToSqlConverterTest.java      |   6 +-
 .../calcite/test/MaterializationTest.java       |  30 +++
 .../apache/calcite/test/MockRelOptPlanner.java  |   7 +
 16 files changed, 409 insertions(+), 227 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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 7759e7b..b65af48 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
@@ -207,7 +207,9 @@ public class RelOptMaterialization {
             AggregateFilterTransposeRule.INSTANCE),
         false,
         DefaultRelMetadataProvider.INSTANCE);
-    return program.run(null, rel2, null);
+    return program.run(null, rel2, null,
+        ImmutableList.<RelOptMaterialization>of(),
+        ImmutableList.<RelOptLattice>of());
   }
 
   /** A table scan and optional project mapping and filter condition. */
@@ -282,7 +284,9 @@ public class RelOptMaterialization {
           RelOptUtil.dumpPlan("before", rel, SqlExplainFormat.TEXT,
               SqlExplainLevel.DIGEST_ATTRIBUTES));
     }
-    final RelNode rel2 = program.run(null, rel, null);
+    final RelNode rel2 = program.run(null, rel, null,
+        ImmutableList.<RelOptMaterialization>of(),
+        ImmutableList.<RelOptLattice>of());
     if (CalcitePrepareImpl.DEBUG) {
       System.out.println(
           RelOptUtil.dumpPlan("after", rel2, SqlExplainFormat.TEXT,

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
new file mode 100644
index 0000000..7778122
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
@@ -0,0 +1,234 @@
+/*
+ * 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.plan.hep.HepPlanner;
+import org.apache.calcite.plan.hep.HepProgram;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
+import org.apache.calcite.rel.rules.ProjectMergeRule;
+import org.apache.calcite.rel.rules.ProjectRemoveRule;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.graph.DefaultDirectedGraph;
+import org.apache.calcite.util.graph.DefaultEdge;
+import org.apache.calcite.util.graph.DirectedGraph;
+import org.apache.calcite.util.graph.Graphs;
+import org.apache.calcite.util.graph.TopologicalOrderIterator;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <code>MaterializationOptUtil</code> defines static utility methods for using
+ * materialized views and lattices for queries.
+ */
+public abstract class RelOptMaterializations {
+
+  /**
+   * Returns a list of RelNode transformed from all possible combination of
+   * materialized view uses. Big queries will likely have more than one
+   * transformed RelNode, e.g., (t1 group by c1) join (t2 group by c2).
+   * @param rel               the original RelNode
+   * @param materializations  the materialized view list
+   * @return the list of transformed RelNode together with their corresponding
+   *         materialized views used in the transformation.
+   */
+  public static List<Pair<RelNode, List<RelOptMaterialization>>> useMaterializedViews(
+      final RelNode rel, List<RelOptMaterialization> materializations) {
+    final List<RelOptMaterialization> applicableMaterializations =
+        getApplicableMaterializations(rel, materializations);
+    final List<Pair<RelNode, List<RelOptMaterialization>>> applied =
+        new ArrayList<>();
+    applied.add(
+        Pair.<RelNode, List<RelOptMaterialization>>of(
+            rel, ImmutableList.<RelOptMaterialization>of()));
+    for (RelOptMaterialization m : applicableMaterializations) {
+      int count = applied.size();
+      for (int i = 0; i < count; i++) {
+        Pair<RelNode, List<RelOptMaterialization>> current = applied.get(i);
+        List<RelNode> sub = substitute(current.left, m);
+        if (!sub.isEmpty()) {
+          ImmutableList.Builder<RelOptMaterialization> builder =
+              ImmutableList.builder();
+          builder.addAll(current.right);
+          builder.add(m);
+          List<RelOptMaterialization> uses = builder.build();
+          for (RelNode rel2 : sub) {
+            applied.add(Pair.of(rel2, uses));
+          }
+        }
+      }
+    }
+
+    return applied.subList(1, applied.size());
+  }
+
+  /**
+   * Returns a list of RelNode transformed from all possible lattice uses.
+   * @param rel       the original RelNode
+   * @param lattices  the lattice list
+   * @return the list of transformed RelNode together with their corresponding
+   *         lattice used in the transformation.
+   */
+  public static List<Pair<RelNode, RelOptLattice>> useLattices(
+      final RelNode rel, List<RelOptLattice> lattices) {
+    final Set<RelOptTable> queryTables = RelOptUtil.findTables(rel);
+    // Use a lattice if the query uses at least the central (fact) table of the
+    // lattice.
+    final List<Pair<RelNode, RelOptLattice>> latticeUses = Lists.newArrayList();
+    final Set<List<String>> queryTableNames =
+        Sets.newHashSet(Iterables.transform(queryTables, GET_QUALIFIED_NAME));
+    // Remember leaf-join form of root so we convert at most once.
+    final Supplier<RelNode> leafJoinRoot = Suppliers.memoize(
+        new Supplier<RelNode>() {
+          public RelNode get() {
+            return RelOptMaterialization.toLeafJoinForm(rel);
+          }
+        });
+    for (RelOptLattice lattice : lattices) {
+      if (queryTableNames.contains(lattice.rootTable().getQualifiedName())) {
+        RelNode rel2 = lattice.rewrite(leafJoinRoot.get());
+        if (rel2 != null) {
+          if (CalcitePrepareImpl.DEBUG) {
+            System.out.println("use lattice:\n"
+                + RelOptUtil.toString(rel2));
+          }
+          latticeUses.add(Pair.of(rel2, lattice));
+        }
+      }
+    }
+
+    return latticeUses;
+  }
+
+  /**
+   * Returns a list of materializations that can potentially be used by the query.
+   */
+  public static List<RelOptMaterialization> getApplicableMaterializations(
+      RelNode rel, List<RelOptMaterialization> materializations) {
+    DirectedGraph<List<String>, DefaultEdge> usesGraph =
+        DefaultDirectedGraph.create();
+    final Map<List<String>, RelOptMaterialization> qnameMap = new HashMap<>();
+    for (RelOptMaterialization materialization : materializations) {
+      // If materialization is a tile in a lattice, we will deal with it shortly.
+      if (materialization.table != null
+          && materialization.starTable == null) {
+        final List<String> qname = materialization.table.getQualifiedName();
+        qnameMap.put(qname, materialization);
+        for (RelOptTable usedTable
+            : RelOptUtil.findTables(materialization.queryRel)) {
+          usesGraph.addVertex(qname);
+          usesGraph.addVertex(usedTable.getQualifiedName());
+          usesGraph.addEdge(usedTable.getQualifiedName(), qname);
+        }
+      }
+    }
+
+    // Use a materialization if uses at least one of the tables are used by
+    // the query. (Simple rule that includes some materializations we won't
+    // actually use.)
+    // For example, given materializations:
+    //   T = Emps Join Depts
+    //   T2 = T Group by C1
+    // the graph will contain
+    //   (T, Emps), (T, Depts), (T2, T)
+    // and therefore we can deduce T2 uses Emps.
+    final Graphs.FrozenGraph<List<String>, DefaultEdge> frozenGraph =
+        Graphs.makeImmutable(usesGraph);
+    final Set<RelOptTable> queryTablesUsed = RelOptUtil.findTables(rel);
+    final List<RelOptMaterialization> applicableMaterializations = Lists.newArrayList();
+    for (List<String> qname : TopologicalOrderIterator.of(usesGraph)) {
+      RelOptMaterialization materialization = qnameMap.get(qname);
+      if (materialization != null
+          && usesTable(materialization.table, queryTablesUsed, frozenGraph)) {
+        applicableMaterializations.add(materialization);
+      }
+    }
+    return applicableMaterializations;
+  }
+
+  private static final Function<RelOptTable, List<String>> GET_QUALIFIED_NAME =
+      new Function<RelOptTable, List<String>>() {
+        public List<String> apply(RelOptTable relOptTable) {
+          return relOptTable.getQualifiedName();
+        }
+      };
+
+  private static List<RelNode> substitute(
+      RelNode root, RelOptMaterialization materialization) {
+    // First, if the materialization is in terms of a star table, rewrite
+    // the query in terms of the star table.
+    if (materialization.starTable != null) {
+      RelNode newRoot = RelOptMaterialization.tryUseStar(root,
+          materialization.starRelOptTable);
+      if (newRoot != null) {
+        root = newRoot;
+      }
+    }
+
+    // Push filters to the bottom, and combine projects on top.
+    RelNode target = materialization.queryRel;
+    HepProgram program =
+        new HepProgramBuilder()
+            .addRuleInstance(FilterProjectTransposeRule.INSTANCE)
+            .addRuleInstance(ProjectMergeRule.INSTANCE)
+            .addRuleInstance(ProjectRemoveRule.INSTANCE)
+            .build();
+
+    final HepPlanner hepPlanner = new HepPlanner(program);
+    hepPlanner.setRoot(target);
+    target = hepPlanner.findBestExp();
+
+    hepPlanner.setRoot(root);
+    root = hepPlanner.findBestExp();
+
+    return new MaterializedViewSubstitutionVisitor(target, root)
+            .go(materialization.tableRel);
+  }
+
+  /**
+   * Returns whether {@code table} uses one or more of the tables in
+   * {@code usedTables}.
+   */
+  private static boolean usesTable(
+      RelOptTable table,
+      Set<RelOptTable> usedTables,
+      Graphs.FrozenGraph<List<String>, DefaultEdge> usesGraph) {
+    for (RelOptTable queryTable : usedTables) {
+      if (usesGraph.getShortestPath(
+          queryTable.getQualifiedName(),
+          table.getQualifiedName()) != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+// End RelOptMaterializations.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java b/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java
index f4ba090..8791785 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptPlanner.java
@@ -76,11 +76,17 @@ public interface RelOptPlanner {
   List<RelTraitDef> getRelTraitDefs();
 
   /**
-   * Removes all internal state, including all rules.
+   * Removes all internal state, including all registered rules,
+   * materialized views, and lattices.
    */
   void clear();
 
   /**
+   * Returns the list of all registered rules.
+   */
+  List<RelOptRule> getRules();
+
+  /**
    * Registers a rule.
    *
    * <p>If the rule has already been registered, does nothing.

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
index 1473f8f..70ab6b9 100644
--- a/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/hep/HepPlanner.java
@@ -155,6 +155,10 @@ public class HepPlanner extends AbstractRelOptPlanner {
     return root;
   }
 
+  public List<RelOptRule> getRules() {
+    return ImmutableList.copyOf(allRules);
+  }
+
   // implement RelOptPlanner
   public boolean addRule(RelOptRule rule) {
     boolean added = allRules.add(rule);

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
index dca56a5..70b5133 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/RuleQueue.java
@@ -144,6 +144,9 @@ class RuleQueue {
   public void clear() {
     this.subsetImportances.clear();
     this.boostedSubsets.clear();
+    for (PhaseMatchList matchList : matchListMap.values()) {
+      matchList.clear();
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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 15dbb98..e415944 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
@@ -23,12 +23,12 @@ import org.apache.calcite.plan.AbstractRelOptPlanner;
 import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.ConventionTraitDef;
-import org.apache.calcite.plan.MaterializedViewSubstitutionVisitor;
 import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptCostFactory;
 import org.apache.calcite.plan.RelOptLattice;
 import org.apache.calcite.plan.RelOptListener;
 import org.apache.calcite.plan.RelOptMaterialization;
+import org.apache.calcite.plan.RelOptMaterializations;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -39,9 +39,6 @@ import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTrait;
 import org.apache.calcite.plan.RelTraitDef;
 import org.apache.calcite.plan.RelTraitSet;
-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.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelVisitor;
@@ -55,10 +52,8 @@ import org.apache.calcite.rel.rules.AggregateProjectMergeRule;
 import org.apache.calcite.rel.rules.AggregateRemoveRule;
 import org.apache.calcite.rel.rules.CalcRemoveRule;
 import org.apache.calcite.rel.rules.FilterJoinRule;
-import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
 import org.apache.calcite.rel.rules.JoinAssociateRule;
 import org.apache.calcite.rel.rules.JoinCommuteRule;
-import org.apache.calcite.rel.rules.ProjectMergeRule;
 import org.apache.calcite.rel.rules.ProjectRemoveRule;
 import org.apache.calcite.rel.rules.SemiJoinRule;
 import org.apache.calcite.rel.rules.SortRemoveRule;
@@ -70,17 +65,8 @@ import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.SaffronProperties;
 import org.apache.calcite.util.Util;
-import org.apache.calcite.util.graph.DefaultDirectedGraph;
-import org.apache.calcite.util.graph.DefaultEdge;
-import org.apache.calcite.util.graph.DirectedGraph;
-import org.apache.calcite.util.graph.Graphs;
-import org.apache.calcite.util.graph.TopologicalOrderIterator;
-
-import com.google.common.base.Function;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
+
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Lists;
@@ -117,13 +103,6 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
 
   protected static final double COST_IMPROVEMENT = .5;
 
-  private static final Function<RelOptTable, List<String>> GET_QUALIFIED_NAME =
-      new Function<RelOptTable, List<String>>() {
-        public List<String> apply(RelOptTable relOptTable) {
-          return relOptTable.getQualifiedName();
-        }
-      };
-
   //~ Instance fields --------------------------------------------------------
 
   protected RelSubset root;
@@ -359,30 +338,33 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     return latticeByName.get(table.getQualifiedName());
   }
 
-  private void useLattice(RelOptLattice lattice, RelNode rel) {
-    Hook.SUB.run(rel);
-    registerImpl(rel, root.set);
-  }
-
-  private List<RelNode> useMaterialization(RelNode root,
-      RelOptMaterialization materialization, boolean firstRun) {
-    // Try to rewrite the original root query in terms of the materialized
-    // query. If that is possible, register the remnant query as equivalent
-    // to the root.
-    //
+  private void registerMaterializations() {
+    // Avoid using materializations while populating materializations!
+    final CalciteConnectionConfig config =
+        context.unwrap(CalciteConnectionConfig.class);
+    if (config == null || !config.materializationsEnabled()) {
+      return;
+    }
 
-    // This call modifies originalRoot. Doesn't look like originalRoot should be mutable though.
-    // Need to check.
-    List<RelNode> sub = substitute(root, materialization);
-    if (!sub.isEmpty()) {
-      for (RelNode rel : sub) {
-        Hook.SUB.run(rel);
-        registerImpl(rel, this.root.set);
-      }
-      return sub;
+    // Register rels using materialized views.
+    final List<Pair<RelNode, List<RelOptMaterialization>>> materializationUses =
+        RelOptMaterializations.useMaterializedViews(originalRoot, materializations);
+    for (Pair<RelNode, List<RelOptMaterialization>> use : materializationUses) {
+      RelNode rel = use.left;
+      Hook.SUB.run(rel);
+      registerImpl(rel, root.set);
     }
 
-    if (firstRun) {
+    // Register table rels of materialized views that cannot find a substitution
+    // in root rel transformation but can potentially be useful.
+    final Set<RelOptMaterialization> applicableMaterializations =
+        new HashSet<>(
+            RelOptMaterializations.getApplicableMaterializations(
+                originalRoot, materializations));
+    for (Pair<RelNode, List<RelOptMaterialization>> use : materializationUses) {
+      applicableMaterializations.removeAll(use.right);
+    }
+    for (RelOptMaterialization materialization : applicableMaterializations) {
       RelSubset subset = registerImpl(materialization.queryRel, null);
       RelNode tableRel2 =
           RelOptUtil.createCastRel(
@@ -391,157 +373,16 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
               true);
       registerImpl(tableRel2, subset.set);
     }
-    return ImmutableList.of();
-  }
-
-  private List<RelNode> substitute(
-      RelNode root, RelOptMaterialization materialization) {
-    // First, if the materialization is in terms of a star table, rewrite
-    // the query in terms of the star table.
-    if (materialization.starTable != null) {
-      RelNode newRoot = RelOptMaterialization.tryUseStar(root,
-          materialization.starRelOptTable);
-      if (newRoot != null) {
-        root = newRoot;
-      }
-    }
-
-    // Push filters to the bottom, and combine projects on top.
-    RelNode target = materialization.queryRel;
-    HepProgram program =
-        new HepProgramBuilder()
-            .addRuleInstance(FilterProjectTransposeRule.INSTANCE)
-            .addRuleInstance(ProjectMergeRule.INSTANCE)
-            .addRuleInstance(ProjectRemoveRule.INSTANCE)
-            .build();
-
-    final HepPlanner hepPlanner = new HepPlanner(program, getContext());
-    hepPlanner.setRoot(target);
-    target = hepPlanner.findBestExp();
-
-    hepPlanner.setRoot(root);
-    root = hepPlanner.findBestExp();
-
-    return new MaterializedViewSubstitutionVisitor(target, root)
-            .go(materialization.tableRel);
-  }
-
-  // Register all possible combinations of materialization substitution.
-  // Useful for big queries, e.g.
-  //   (t1 group by c1) join (t2 group by c2).
-  private void useMaterializations(RelNode root,
-      List<RelOptMaterialization> materializations) {
-    List<RelNode> applied = Lists.newArrayList(root);
-    for (RelOptMaterialization m : materializations) {
-      int count = applied.size();
-      for (int i = 0; i < count; i++) {
-        List<RelNode> sub = useMaterialization(applied.get(i), m, i == 0);
-        applied.addAll(sub);
-      }
-    }
-  }
 
-  private void useApplicableMaterializations() {
-    // Avoid using materializations while populating materializations!
-    final CalciteConnectionConfig config =
-        context.unwrap(CalciteConnectionConfig.class);
-    if (config == null || !config.materializationsEnabled()) {
-      return;
-    }
-
-    // Given materializations:
-    //   T = Emps Join Depts
-    //   T2 = T Group by C1
-    // graph will contain
-    //   (T, Emps), (T, Depts), (T2, T)
-    // and therefore we can deduce T2 uses Emps.
-    final List<RelOptMaterialization> applicableMaterializations =
-        getApplicableMaterializations(originalRoot, materializations);
-    useMaterializations(originalRoot, applicableMaterializations);
-    final Set<RelOptTable> queryTables = RelOptUtil.findTables(originalRoot);
-
-    // Use a lattice if the query uses at least the central (fact) table of the
-    // lattice.
-    final List<Pair<RelOptLattice, RelNode>> latticeUses = Lists.newArrayList();
-    final Set<List<String>> queryTableNames =
-        Sets.newHashSet(Iterables.transform(queryTables, GET_QUALIFIED_NAME));
-    // Remember leaf-join form of root so we convert at most once.
-    final Supplier<RelNode> leafJoinRoot = Suppliers.memoize(
-        new Supplier<RelNode>() {
-          public RelNode get() {
-            return RelOptMaterialization.toLeafJoinForm(originalRoot);
-          }
-        });
-    for (RelOptLattice lattice : latticeByName.values()) {
-      if (queryTableNames.contains(lattice.rootTable().getQualifiedName())) {
-        RelNode rel2 = lattice.rewrite(leafJoinRoot.get());
-        if (rel2 != null) {
-          if (CalcitePrepareImpl.DEBUG) {
-            System.out.println("use lattice:\n"
-                + RelOptUtil.toString(rel2));
-          }
-          latticeUses.add(Pair.of(lattice, rel2));
-        }
-      }
-    }
+    // Register rels using lattices.
+    final List<Pair<RelNode, RelOptLattice>> latticeUses =
+        RelOptMaterializations.useLattices(
+            originalRoot, ImmutableList.copyOf(latticeByName.values()));
     if (!latticeUses.isEmpty()) {
-      useLattice(latticeUses.get(0).left, latticeUses.get(0).right);
-    }
-  }
-
-  public static List<RelOptMaterialization> getApplicableMaterializations(RelNode root,
-      List<RelOptMaterialization> materializations) {
-    DirectedGraph<List<String>, DefaultEdge> usesGraph =
-        DefaultDirectedGraph.create();
-    final Map<List<String>, RelOptMaterialization> qnameMap = new HashMap<>();
-    for (RelOptMaterialization materialization : materializations) {
-      // If materialization is a tile in a lattice, we will deal with it shortly.
-      if (materialization.table != null
-          && materialization.starTable == null) {
-        final List<String> qname = materialization.table.getQualifiedName();
-        qnameMap.put(qname, materialization);
-        for (RelOptTable usedTable
-            : RelOptUtil.findTables(materialization.queryRel)) {
-          usesGraph.addVertex(qname);
-          usesGraph.addVertex(usedTable.getQualifiedName());
-          usesGraph.addEdge(usedTable.getQualifiedName(), qname);
-        }
-      }
+      RelNode rel = latticeUses.get(0).left;
+      Hook.SUB.run(rel);
+      registerImpl(rel, root.set);
     }
-
-    // Use a materialization if uses at least one of the tables are used by
-    // the query. (Simple rule that includes some materializations we won't
-    // actually use.)
-    final Graphs.FrozenGraph<List<String>, DefaultEdge> frozenGraph =
-        Graphs.makeImmutable(usesGraph);
-    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);
-      if (materialization != null
-          && usesTable(materialization.table, queryTablesUsed, frozenGraph)) {
-        applicableMaterializations.add(materialization);
-      }
-    }
-    return applicableMaterializations;
-  }
-
-  /**
-   * Returns whether {@code table} uses one or more of the tables in
-   * {@code usedTables}.
-   */
-  private static boolean usesTable(
-      RelOptTable table,
-      Set<RelOptTable> usedTables,
-      Graphs.FrozenGraph<List<String>, DefaultEdge> usesGraph) {
-    for (RelOptTable queryTable : usedTables) {
-      if (usesGraph.getShortestPath(
-          queryTable.getQualifiedName(),
-          table.getQualifiedName()) != null) {
-        return true;
-      }
-    }
-    return false;
   }
 
   /**
@@ -598,6 +439,12 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     this.relImportances.clear();
     this.ruleQueue.clear();
     this.ruleNames.clear();
+    this.materializations.clear();
+    this.latticeByName.clear();
+  }
+
+  public List<RelOptRule> getRules() {
+    return ImmutableList.copyOf(ruleSet);
   }
 
   public boolean addRule(RelOptRule rule) {
@@ -742,7 +589,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
    */
   public RelNode findBestExp() {
     ensureRootConverters();
-    useApplicableMaterializations();
+    registerMaterializations();
     int cumulativeTicks = 0;
     for (VolcanoPlannerPhase phase : VolcanoPlannerPhase.values()) {
       setInitialImportance();

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index ee7d4ee..7f43801 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -193,7 +193,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
           "values 1",
           "VALUES 1");
 
-  private static final List<RelOptRule> ENUMERABLE_RULES =
+  public static final List<RelOptRule> ENUMERABLE_RULES =
       ImmutableList.of(
           EnumerableRules.ENUMERABLE_JOIN_RULE,
           EnumerableRules.ENUMERABLE_MERGE_JOIN_RULE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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 a143e1a..7ad7f3c 100644
--- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
@@ -21,6 +21,8 @@ import org.apache.calcite.config.CalciteConnectionConfig;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptLattice;
+import org.apache.calcite.plan.RelOptMaterialization;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptSchema;
 import org.apache.calcite.plan.RelOptTable.ViewExpander;
@@ -307,7 +309,9 @@ public class PlannerImpl implements Planner {
             rel.getCluster().getMetadataProvider(),
             rel.getCluster().getPlanner()));
     Program program = programs.get(ruleSetIndex);
-    return program.run(planner, rel, requiredOutputTraits);
+    return program.run(planner, rel, requiredOutputTraits,
+        ImmutableList.<RelOptMaterialization>of(),
+        ImmutableList.<RelOptLattice>of());
   }
 
   /** Stage of a statement in the query-preparation lifecycle. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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 5de9b35..988b37b 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -53,7 +53,6 @@ import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.tools.Program;
 import org.apache.calcite.tools.Programs;
 import org.apache.calcite.util.Holder;
-import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.TryThreadLocal;
 import org.apache.calcite.util.trace.CalciteTimingTracer;
 import org.apache.calcite.util.trace.CalciteTrace;
@@ -64,6 +63,7 @@ import com.google.common.collect.ImmutableList;
 import org.slf4j.Logger;
 
 import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -127,35 +127,32 @@ public abstract class Prepare {
       final List<CalciteSchema.LatticeEntry> lattices) {
     final RelOptPlanner planner = root.rel.getCluster().getPlanner();
 
-    // Add a project to the root. Even if the project is trivial, it informs
-    // rules firing on the relational expression below it which of the fields
-    // are used. SemiJoinRule, for instance.
-    planner.setRoot(root.project(true));
-
-    final RelTraitSet desiredTraits = getDesiredRootTraitSet(root);
-    final Program program = getProgram();
-
     final DataContext dataContext = context.getDataContext();
     planner.setExecutor(new RexExecutorImpl(dataContext));
 
+    final List<RelOptMaterialization> materializationList = new ArrayList<>();
     for (Materialization materialization : materializations) {
-      planner.addMaterialization(
+      materializationList.add(
           new RelOptMaterialization(materialization.tableRel,
               materialization.queryRel,
               materialization.starRelOptTable));
     }
 
+    final List<RelOptLattice> latticeList = new ArrayList<>();
     for (CalciteSchema.LatticeEntry lattice : lattices) {
       final CalciteSchema.TableEntry starTable = lattice.getStarTable();
       final JavaTypeFactory typeFactory = context.getTypeFactory();
       final RelOptTableImpl starRelOptTable =
           RelOptTableImpl.create(catalogReader,
               starTable.getTable().getRowType(typeFactory), starTable, null);
-      planner.addLattice(
+      latticeList.add(
           new RelOptLattice(lattice.getLattice(), starRelOptTable));
     }
 
-    final RelNode rootRel4 = program.run(planner, root.rel, desiredTraits);
+    final RelTraitSet desiredTraits = getDesiredRootTraitSet(root);
+    final Program program = getProgram();
+    final RelNode rootRel4 = program.run(
+        planner, root.rel, desiredTraits, materializationList, latticeList);
     if (LOGGER.isDebugEnabled()) {
       LOGGER.debug("Plan after physical tweaks: {}",
           RelOptUtil.toString(rootRel4, SqlExplainLevel.ALL_ATTRIBUTES));
@@ -164,11 +161,10 @@ public abstract class Prepare {
     return root.withRel(rootRel4);
   }
 
-  private Program getProgram() {
+  protected Program getProgram() {
     // Allow a test to override the default program.
-    final List<Materialization> materializations = ImmutableList.of();
     final Holder<Program> holder = Holder.of(null);
-    Hook.PROGRAM.run(Pair.of(materializations, holder));
+    Hook.PROGRAM.run(holder);
     if (holder.get() != null) {
       return holder.get();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewFilterScanRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewFilterScanRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewFilterScanRule.java
index 852d724..f1775aa 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewFilterScanRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewFilterScanRule.java
@@ -18,6 +18,7 @@ package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.MaterializedViewSubstitutionVisitor;
 import org.apache.calcite.plan.RelOptMaterialization;
+import org.apache.calcite.plan.RelOptMaterializations;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -78,7 +79,7 @@ public class MaterializedViewFilterScanRule extends RelOptRule {
       RelNode root = filter.copy(filter.getTraitSet(),
           Collections.singletonList((RelNode) scan));
       List<RelOptMaterialization> applicableMaterializations =
-          VolcanoPlanner.getApplicableMaterializations(root, materializations);
+          RelOptMaterializations.getApplicableMaterializations(root, materializations);
       for (RelOptMaterialization materialization : applicableMaterializations) {
         if (RelOptUtil.areRowTypesEqual(scan.getRowType(),
             materialization.queryRel.getRowType(), false)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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
index 4c0a84b..f6fb009 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MaterializedViewJoinRule.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.RelOptMaterialization;
+import org.apache.calcite.plan.RelOptMaterializations;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -153,7 +154,7 @@ public class MaterializedViewJoinRule extends RelOptRule {
             : ImmutableList.<RelOptMaterialization>of();
     if (!materializations.isEmpty()) {
       List<RelOptMaterialization> applicableMaterializations =
-          VolcanoPlanner.getApplicableMaterializations(join, materializations);
+          RelOptMaterializations.getApplicableMaterializations(join, materializations);
 
       // Prepare a planner to convert views to MultiJoins
       HepPlanner hepPlanner =

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/tools/Program.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Program.java b/core/src/main/java/org/apache/calcite/tools/Program.java
index 1bfa4cf..d59ea3f 100644
--- a/core/src/main/java/org/apache/calcite/tools/Program.java
+++ b/core/src/main/java/org/apache/calcite/tools/Program.java
@@ -16,10 +16,14 @@
  */
 package org.apache.calcite.tools;
 
+import org.apache.calcite.plan.RelOptLattice;
+import org.apache.calcite.plan.RelOptMaterialization;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 
+import java.util.List;
+
 /**
  * Program that transforms a relational expression into another relational
  * expression.
@@ -31,7 +35,9 @@ import org.apache.calcite.rel.RelNode;
  */
 public interface Program {
   RelNode run(RelOptPlanner planner, RelNode rel,
-      RelTraitSet requiredOutputTraits);
+      RelTraitSet requiredOutputTraits,
+      List<RelOptMaterialization> materializations,
+      List<RelOptLattice> lattices);
 }
 
 // End Program.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/main/java/org/apache/calcite/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java b/core/src/main/java/org/apache/calcite/tools/Programs.java
index 6bf65e4..4c552af 100644
--- a/core/src/main/java/org/apache/calcite/tools/Programs.java
+++ b/core/src/main/java/org/apache/calcite/tools/Programs.java
@@ -20,6 +20,8 @@ import org.apache.calcite.adapter.enumerable.EnumerableRules;
 import org.apache.calcite.config.CalciteConnectionConfig;
 import org.apache.calcite.interpreter.NoneToBindableConverterRule;
 import org.apache.calcite.plan.RelOptCostImpl;
+import org.apache.calcite.plan.RelOptLattice;
+import org.apache.calcite.plan.RelOptMaterialization;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptUtil;
@@ -191,7 +193,9 @@ public class Programs {
       final RelMetadataProvider metadataProvider) {
     return new Program() {
       public RelNode run(RelOptPlanner planner, RelNode rel,
-          RelTraitSet requiredOutputTraits) {
+          RelTraitSet requiredOutputTraits,
+          List<RelOptMaterialization> materializations,
+          List<RelOptLattice> lattices) {
         final HepPlanner hepPlanner = new HepPlanner(hepProgram,
             null, noDag, null, RelOptCostImpl.FACTORY);
 
@@ -220,7 +224,9 @@ public class Programs {
       final boolean bushy, final int minJoinCount) {
     return new Program() {
       public RelNode run(RelOptPlanner planner, RelNode rel,
-          RelTraitSet requiredOutputTraits) {
+          RelTraitSet requiredOutputTraits,
+          List<RelOptMaterialization> materializations,
+          List<RelOptLattice> lattices) {
         final int joinCount = RelOptUtil.countJoins(rel);
         final Program program;
         if (joinCount < minJoinCount) {
@@ -252,7 +258,8 @@ public class Programs {
 
           program = sequence(program1, program2);
         }
-        return program.run(planner, rel, requiredOutputTraits);
+        return program.run(
+            planner, rel, requiredOutputTraits, materializations, lattices);
       }
     };
   }
@@ -276,7 +283,9 @@ public class Programs {
   public static Program getProgram() {
     return new Program() {
       public RelNode run(RelOptPlanner planner, RelNode rel,
-          RelTraitSet requiredOutputTraits) {
+          RelTraitSet requiredOutputTraits,
+          List<RelOptMaterialization> materializations,
+          List<RelOptLattice> lattices) {
         return null;
       }
     };
@@ -293,7 +302,18 @@ public class Programs {
     final Program program1 =
         new Program() {
           public RelNode run(RelOptPlanner planner, RelNode rel,
-              RelTraitSet requiredOutputTraits) {
+              RelTraitSet requiredOutputTraits,
+              List<RelOptMaterialization> materializations,
+              List<RelOptLattice> lattices) {
+            planner.setRoot(rel);
+
+            for (RelOptMaterialization materialization : materializations) {
+              planner.addMaterialization(materialization);
+            }
+            for (RelOptLattice lattice : lattices) {
+              planner.addLattice(lattice);
+            }
+
             final RelNode rootRel2 =
                 rel.getTraitSet().equals(requiredOutputTraits)
                 ? rel
@@ -327,11 +347,19 @@ public class Programs {
     }
 
     public RelNode run(RelOptPlanner planner, RelNode rel,
-        RelTraitSet requiredOutputTraits) {
+        RelTraitSet requiredOutputTraits,
+        List<RelOptMaterialization> materializations,
+        List<RelOptLattice> lattices) {
       planner.clear();
       for (RelOptRule rule : ruleSet) {
         planner.addRule(rule);
       }
+      for (RelOptMaterialization materialization : materializations) {
+        planner.addMaterialization(materialization);
+      }
+      for (RelOptLattice lattice : lattices) {
+        planner.addLattice(lattice);
+      }
       if (!rel.getTraitSet().equals(requiredOutputTraits)) {
         rel = planner.changeTraits(rel, requiredOutputTraits);
       }
@@ -351,9 +379,12 @@ public class Programs {
     }
 
     public RelNode run(RelOptPlanner planner, RelNode rel,
-        RelTraitSet requiredOutputTraits) {
+        RelTraitSet requiredOutputTraits,
+        List<RelOptMaterialization> materializations,
+        List<RelOptLattice> lattices) {
       for (Program program : programs) {
-        rel = program.run(planner, rel, requiredOutputTraits);
+        rel = program.run(
+            planner, rel, requiredOutputTraits, materializations, lattices);
       }
       return rel;
     }
@@ -368,7 +399,9 @@ public class Programs {
    * {@link TrimFieldsProgram} after this program. */
   private static class DecorrelateProgram implements Program {
     public RelNode run(RelOptPlanner planner, RelNode rel,
-        RelTraitSet requiredOutputTraits) {
+        RelTraitSet requiredOutputTraits,
+        List<RelOptMaterialization> materializations,
+        List<RelOptLattice> lattices) {
       final CalciteConnectionConfig config =
           planner.getContext().unwrap(CalciteConnectionConfig.class);
       if (config != null && config.forceDecorrelate()) {
@@ -381,7 +414,9 @@ public class Programs {
   /** Program that trims fields. */
   private static class TrimFieldsProgram implements Program {
     public RelNode run(RelOptPlanner planner, RelNode rel,
-        RelTraitSet requiredOutputTraits) {
+        RelTraitSet requiredOutputTraits,
+        List<RelOptMaterialization> materializations,
+        List<RelOptLattice> lattices) {
       final RelBuilder relBuilder =
           RelFactories.LOGICAL_BUILDER.create(rel.getCluster(), null);
       return new RelFieldTrimmer(null, relBuilder).trim(rel);

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 7050f72..1eb80e1 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.rel.rel2sql;
 
+import org.apache.calcite.plan.RelOptLattice;
+import org.apache.calcite.plan.RelOptMaterialization;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelTraitDef;
 import org.apache.calcite.plan.hep.HepPlanner;
@@ -579,7 +581,9 @@ public class RelToSqlConverterTest {
           FlatLists.append(transforms, new Function<RelNode, RelNode>() {
             public RelNode apply(RelNode r) {
               Program program = Programs.of(ruleSet);
-              return program.run(relOptPlanner, r, r.getTraitSet());
+              return program.run(relOptPlanner, r, r.getTraitSet(),
+                  ImmutableList.<RelOptMaterialization>of(),
+                  ImmutableList.<RelOptLattice>of());
             }
           }));
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/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 b89854f..f6ec85d 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -916,6 +916,36 @@ public class MaterializationTest {
     checkMaterializeWithRules(m, q, rules);
   }
 
+  @Test public void testSubQuery() {
+    String q = "select \"empid\", \"deptno\", \"salary\" from \"emps\" e1\n"
+            + "where \"empid\" = (\n"
+            + "  select max(\"empid\") from \"emps\"\n"
+            + "  where \"deptno\" = e1.\"deptno\")";
+    final String m = "select \"empid\", \"deptno\" from \"emps\"\n"
+            + "";
+    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_TRIM.push(true)) {
+      MaterializationService.setThreadLocal();
+      CalciteAssert.that()
+          .withMaterializations(JdbcTest.HR_MODEL, "m0", m)
+          .query(q)
+          .enableMaterializations(true)
+          .explainMatches("", new Function<ResultSet, Void>() {
+            public Void apply(ResultSet s) {
+              try {
+                final String actual = Util.toLinux(CalciteAssert.toString(s));
+                final String scan = "EnumerableTableScan(table=[[hr, m0]])";
+                assertTrue(actual + " should have had two occurrences of " + scan,
+                    StringUtils.countMatches(actual, scan) == 2);
+                return null;
+              } catch (SQLException e) {
+                throw new RuntimeException(e);
+              }
+            }
+          })
+          .sameResultWithMaterializationsDisabled();
+    }
+  }
+
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-761">[CALCITE-761]
    * Pre-populated materializations</a>. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/972b5de5/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java b/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java
index ce61d21..0462e38 100644
--- a/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java
+++ b/core/src/test/java/org/apache/calcite/test/MockRelOptPlanner.java
@@ -28,6 +28,8 @@ import org.apache.calcite.rex.RexExecutorImpl;
 import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.util.Pair;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -71,6 +73,11 @@ public class MockRelOptPlanner extends AbstractRelOptPlanner {
     this.rule = null;
   }
 
+  public List<RelOptRule> getRules() {
+    return rule == null
+        ? ImmutableList.<RelOptRule>of() : ImmutableList.of(rule);
+  }
+
   public boolean addRule(RelOptRule rule) {
     assert this.rule == null
         : "MockRelOptPlanner only supports a single rule";


Mime
View raw message