This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit bb3889e860cc7472e31ba39ac6d3d5a0e3c59463 Author: Julian Hyde <[email protected]> AuthorDate: Tue Sep 24 14:03:32 2019 -0700 [CALCITE-3369] In LatticeSuggester, recommend lattices based on UNION queries --- .../calcite/materialize/LatticeSuggester.java | 30 +++++++++++----- .../calcite/materialize/LatticeSuggesterTest.java | 41 +++++++++++++++++++--- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java b/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java index 30f337e..d752196 100644 --- a/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java +++ b/core/src/main/java/org/apache/calcite/materialize/LatticeSuggester.java @@ -28,6 +28,7 @@ import org.apache.calcite.rel.core.AggregateCall; import org.apache.calcite.rel.core.Filter; import org.apache.calcite.rel.core.Join; import org.apache.calcite.rel.core.Project; +import org.apache.calcite.rel.core.SetOp; import org.apache.calcite.rel.core.Sort; import org.apache.calcite.rel.core.TableScan; import org.apache.calcite.rel.rules.FilterJoinRule; @@ -133,10 +134,14 @@ public class LatticeSuggester { final RelNode r2 = planner.findBestExp(); final Query q = new Query(space); - final Frame frame = frame(q, r2); - if (frame == null) { - return ImmutableList.of(); - } + final List<Frame> frameList = new ArrayList<>(); + frames(frameList, q, r2); + final List<Lattice> lattices = new ArrayList<>(); + frameList.forEach(frame -> addFrame(q, frame, lattices)); + return ImmutableList.copyOf(lattices); + } + + private void addFrame(Query q, Frame frame, List<Lattice> lattices) { final AttributedDirectedGraph<TableRef, StepRef> g = AttributedDirectedGraph.create(new StepRef.Factory()); final Multimap<Pair<TableRef, TableRef>, IntPair> map = @@ -163,7 +168,7 @@ public class LatticeSuggester { // If the join graph is cyclic, we can't use it. final Set<TableRef> cycles = new CycleDetector<>(g).findCycles(); if (!cycles.isEmpty()) { - return ImmutableList.of(); + return; } // Translate the query graph to mutable nodes @@ -205,7 +210,6 @@ public class LatticeSuggester { } // Transcribe the hierarchy of mutable nodes to immutable nodes - final List<Lattice> lattices = new ArrayList<>(); for (MutableNode rootNode : rootNodes) { if (rootNode.isCyclic()) { continue; @@ -221,7 +225,7 @@ public class LatticeSuggester { for (ColRef arg : measure.arguments) { if (arg == null) { // Cannot handle expressions, e.g. "sum(x + 1)" yet - return ImmutableList.of(); + return; } } latticeBuilder.addMeasure( @@ -263,7 +267,6 @@ public class LatticeSuggester { final Lattice lattice1 = findMatch(lattice0, rootNode); lattices.add(lattice1); } - return ImmutableList.copyOf(lattices); } /** Derives the alias of an expression that is the argument to a measure. @@ -399,6 +402,17 @@ public class LatticeSuggester { return c3; } + private void frames(List<Frame> frames, final Query q, RelNode r) { + if (r instanceof SetOp) { + r.getInputs().forEach(input -> frames(frames, q, input)); + } else { + final Frame frame = frame(q, r); + if (frame != null) { + frames.add(frame); + } + } + } + private Frame frame(final Query q, RelNode r) { if (r instanceof Sort) { final Sort sort = (Sort) r; diff --git a/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java b/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java index c13898b..a7163ce 100644 --- a/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java +++ b/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java @@ -49,6 +49,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.EnumSet; import java.util.List; @@ -707,16 +708,46 @@ public class LatticeSuggesterTest { assertThat(t.addQuery(q), isGraphs(g, "[]")); } + @Test public void testUnion() throws Exception { + checkUnion("union"); + checkUnion("union all"); + checkUnion("intersect"); + checkUnion("except"); + } + + private void checkUnion(String setOp) throws Exception { + final Tester t = new Tester().foodmart().withEvolve(true); + final String q = "select \"t\".\"time_id\"\n" + + "from \"time_by_day\" as \"t\",\n" + + " \"sales_fact_1997\" as \"s\"\n" + + "where \"s\".\"time_id\" = \"t\".\"time_id\"\n" + + setOp + "\n" + + "select min(\"unit_sales\")\n" + + "from \"sales_fact_1997\" as \"s\" join \"product\" as \"p\"\n" + + " using (\"product_id\")\n" + + "group by \"s\".\"customer_id\""; + + // Adding a query generates two lattices + final List<Lattice> latticeList = t.addQuery(q); + assertThat(latticeList.size(), is(2)); + + // But because of 'evolve' flag, the lattices are merged into a single + // lattice + final String g = "sales_fact_1997 (product:product_id time_by_day:time_id)"; + final String measures = "[MIN(sales_fact_1997.unit_sales)]"; + assertThat(t.s.getLatticeSet(), isGraphs(g, measures)); + } + /** Creates a matcher that matches query graphs to strings. */ - private BaseMatcher<List<Lattice>> isGraphs( + private BaseMatcher<Collection<Lattice>> isGraphs( String... strings) { final List<String> expectedList = Arrays.asList(strings); - return new BaseMatcher<List<Lattice>>() { + return new BaseMatcher<Collection<Lattice>>() { public boolean matches(Object item) { //noinspection unchecked - return item instanceof List - && ((List) item).size() * 2 == expectedList.size() - && allEqual((List) item, expectedList); + return item instanceof Collection + && ((Collection<Object>) item).size() * 2 == expectedList.size() + && allEqual(ImmutableList.copyOf((Collection) item), expectedList); } private boolean allEqual(List<Lattice> items,
