You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2014/07/28 20:56:27 UTC
[3/7] git commit: Refactor test infrastructure to allow testing
against heuristic bushy-join optimizer.
Refactor test infrastructure to allow testing against heuristic bushy-join optimizer.
Add enum OptiqAssert.SchemaSpec, to allow more uniform use of various schemas in the test suite.
Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/929f5310
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/929f5310
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/929f5310
Branch: refs/heads/master
Commit: 929f5310ad53b69a8917105501ad651656e09187
Parents: a461539
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jul 21 16:14:16 2014 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Jul 28 11:12:52 2014 -0700
----------------------------------------------------------------------
.../net/hydromatic/optiq/tools/Programs.java | 7 +-
.../org/eigenbase/rel/rules/LoptJoinTree.java | 104 +++++++++++--------
.../rel/rules/LoptOptimizeJoinRule.java | 24 ++---
.../rel/rules/OptimizeBushyJoinRule.java | 58 +++++++++++
.../net/hydromatic/optiq/test/OptiqAssert.java | 104 ++++++++++++-------
.../net/hydromatic/optiq/tools/PlannerTest.java | 72 ++++++++++---
6 files changed, 253 insertions(+), 116 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
index 53a6ac6..83c73cd 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
@@ -113,7 +113,8 @@ public class Programs {
* {@link org.eigenbase.rel.rules.MultiJoinRel} and
* {@link org.eigenbase.rel.rules.LoptOptimizeJoinRule})
* if there are 6 or more joins (7 or more relations). */
- public static Program heuristicJoinOrder(final Collection<RelOptRule> rules) {
+ public static Program heuristicJoinOrder(final Collection<RelOptRule> rules,
+ final boolean bushy) {
return new Program() {
public RelNode run(RelOptPlanner planner, RelNode rel,
RelTraitSet requiredOutputTraits) {
@@ -140,7 +141,9 @@ public class Programs {
CommutativeJoinRule.INSTANCE,
PushJoinThroughJoinRule.LEFT,
PushJoinThroughJoinRule.RIGHT));
- list.add(LoptOptimizeJoinRule.INSTANCE);
+ list.add(bushy
+ ? OptimizeBushyJoinRule.INSTANCE
+ : LoptOptimizeJoinRule.INSTANCE);
final Program program2 = ofRules(list);
program = sequence(program1, program2);
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/main/java/org/eigenbase/rel/rules/LoptJoinTree.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/LoptJoinTree.java b/core/src/main/java/org/eigenbase/rel/rules/LoptJoinTree.java
index a274ab9..0e32b0e 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/LoptJoinTree.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/LoptJoinTree.java
@@ -21,6 +21,8 @@ import java.util.*;
import org.eigenbase.rel.*;
+import com.google.common.base.Preconditions;
+
/**
* Utility class used to store a {@link JoinRelBase} tree and the factors that
* make up the tree.
@@ -34,26 +36,26 @@ import org.eigenbase.rel.*;
public class LoptJoinTree {
//~ Instance fields --------------------------------------------------------
- private BinaryTree factorTree;
- private RelNode joinTree;
- private boolean removableSelfJoin;
+ private final BinaryTree factorTree;
+ private final RelNode joinTree;
+ private final boolean removableSelfJoin;
//~ Constructors -----------------------------------------------------------
/**
- * Creates a jointree consisting of a single node
+ * Creates a join-tree consisting of a single node.
*
* @param joinTree RelNode corresponding to the single node
* @param factorId factor id of the node
*/
public LoptJoinTree(RelNode joinTree, int factorId) {
this.joinTree = joinTree;
- factorTree = new BinaryTree(factorId, this);
+ this.factorTree = new Leaf(factorId, this);
this.removableSelfJoin = false;
}
/**
- * Associates the factor ids with a jointree
+ * Associates the factor ids with a join-tree.
*
* @param joinTree RelNodes corresponding to the join tree
* @param factorTree tree of the factor ids
@@ -70,8 +72,8 @@ public class LoptJoinTree {
}
/**
- * Associates the factor ids with a jointree given the factors corresponding
- * to the left and right subtrees of the join
+ * Associates the factor ids with a join-tree given the factors corresponding
+ * to the left and right subtrees of the join.
*
* @param joinTree RelNodes corresponding to the join tree
* @param leftFactorTree tree of the factor ids for left subtree
@@ -85,7 +87,7 @@ public class LoptJoinTree {
}
/**
- * Associates the factor ids with a jointree given the factors corresponding
+ * Associates the factor ids with a join-tree given the factors corresponding
* to the left and right subtrees of the join. Also indicates whether the
* join is a removable self-join.
*
@@ -99,7 +101,7 @@ public class LoptJoinTree {
BinaryTree leftFactorTree,
BinaryTree rightFactorTree,
boolean removableSelfJoin) {
- factorTree = new BinaryTree(leftFactorTree, rightFactorTree, this);
+ factorTree = new Node(leftFactorTree, rightFactorTree, this);
this.joinTree = joinTree;
this.removableSelfJoin = removableSelfJoin;
}
@@ -111,17 +113,19 @@ public class LoptJoinTree {
}
public LoptJoinTree getLeft() {
+ final Node node = (Node) factorTree;
return new LoptJoinTree(
((JoinRelBase) joinTree).getLeft(),
- factorTree.getLeft(),
- factorTree.getLeft().getParent().isRemovableSelfJoin());
+ node.getLeft(),
+ node.getLeft().getParent().isRemovableSelfJoin());
}
public LoptJoinTree getRight() {
+ final Node node = (Node) factorTree;
return new LoptJoinTree(
((JoinRelBase) joinTree).getRight(),
- factorTree.getRight(),
- factorTree.getRight().getParent().isRemovableSelfJoin());
+ node.getRight(),
+ node.getRight().getParent().isRemovableSelfJoin());
}
public BinaryTree getFactorTree() {
@@ -142,55 +146,63 @@ public class LoptJoinTree {
* Simple binary tree class that stores an id in the leaf nodes and keeps
* track of the parent LoptJoinTree object associated with the binary tree.
*/
- protected class BinaryTree {
- private int id;
- private BinaryTree left;
- private BinaryTree right;
- private LoptJoinTree parent;
+ protected abstract static class BinaryTree {
+ private final LoptJoinTree parent;
- public BinaryTree(int rootId, LoptJoinTree parent) {
- this.id = rootId;
- this.left = null;
- this.right = null;
- this.parent = parent;
+ protected BinaryTree(LoptJoinTree parent) {
+ this.parent = Preconditions.checkNotNull(parent);
}
- public BinaryTree(
- BinaryTree left,
- BinaryTree right,
- LoptJoinTree parent) {
- this.left = left;
- this.right = right;
- this.parent = parent;
+ public LoptJoinTree getParent() {
+ return parent;
}
- public BinaryTree getLeft() {
- return left;
- }
+ public abstract void getTreeOrder(List<Integer> treeOrder);
+ }
- public BinaryTree getRight() {
- return right;
- }
+ /** Binary tree node that has no children. */
+ protected static class Leaf extends BinaryTree {
+ private final int id;
- public LoptJoinTree getParent() {
- return parent;
+ public Leaf(int rootId, LoptJoinTree parent) {
+ super(parent);
+ this.id = rootId;
}
/**
* @return the id associated with a leaf node in a binary tree
*/
public int getId() {
- assert left == null && right == null;
return id;
}
public void getTreeOrder(List<Integer> treeOrder) {
- if ((left == null) || (right == null)) {
- treeOrder.add(id);
- } else {
- left.getTreeOrder(treeOrder);
- right.getTreeOrder(treeOrder);
- }
+ treeOrder.add(id);
+ }
+ }
+
+ /** Binary tree node that has two children. */
+ protected static class Node extends BinaryTree {
+ private BinaryTree left;
+ private BinaryTree right;
+
+ public Node(BinaryTree left, BinaryTree right, LoptJoinTree parent) {
+ super(parent);
+ this.left = Preconditions.checkNotNull(left);
+ this.right = Preconditions.checkNotNull(right);
+ }
+
+ public BinaryTree getLeft() {
+ return left;
+ }
+
+ public BinaryTree getRight() {
+ return right;
+ }
+
+ public void getTreeOrder(List<Integer> treeOrder) {
+ left.getTreeOrder(treeOrder);
+ right.getTreeOrder(treeOrder);
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/main/java/org/eigenbase/rel/rules/LoptOptimizeJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/LoptOptimizeJoinRule.java b/core/src/main/java/org/eigenbase/rel/rules/LoptOptimizeJoinRule.java
index 9e3d122..e758ee1 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/LoptOptimizeJoinRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/LoptOptimizeJoinRule.java
@@ -55,13 +55,13 @@ public class LoptOptimizeJoinRule extends RelOptRule {
// implement RelOptRule
public void onMatch(RelOptRuleCall call) {
- MultiJoinRel multiJoinRel = (MultiJoinRel) call.rels[0];
- LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
+ final MultiJoinRel multiJoinRel = call.rel(0);
+ final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
findRemovableOuterJoins(multiJoin);
- RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
- LoptSemiJoinOptimizer semiJoinOpt =
+ final RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
+ final LoptSemiJoinOptimizer semiJoinOpt =
new LoptSemiJoinOptimizer(multiJoin, rexBuilder);
// determine all possible semijoins
@@ -745,13 +745,10 @@ public class LoptOptimizeJoinRule extends RelOptRule {
// can't add a null-generating factor if its dependent,
// non-null generating factors haven't been added yet
- if (multiJoin.isNullGenerating(factor)) {
- BitSet tmp =
- (BitSet) multiJoin.getOuterJoinFactors(factor).clone();
- tmp.andNot(factorsAdded);
- if (tmp.cardinality() != 0) {
- continue;
- }
+ if (multiJoin.isNullGenerating(factor)
+ && !BitSets.contains(factorsAdded,
+ multiJoin.getOuterJoinFactors(factor))) {
+ continue;
}
// determine the best weight between the current factor
@@ -1838,12 +1835,11 @@ public class LoptOptimizeJoinRule extends RelOptRule {
if (selfJoin) {
return !multiJoin.isLeftFactorInRemovableSelfJoin(
- left.getFactorTree().getId());
+ ((LoptJoinTree.Leaf) left.getFactorTree()).getId());
}
Double leftRowCount = RelMetadataQuery.getRowCount(left.getJoinTree());
- Double rightRowCount =
- RelMetadataQuery.getRowCount(right.getJoinTree());
+ Double rightRowCount = RelMetadataQuery.getRowCount(right.getJoinTree());
// The left side is smaller than the right if it has fewer rows,
// or if it has the same number of rows as the right (excluding
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/main/java/org/eigenbase/rel/rules/OptimizeBushyJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/OptimizeBushyJoinRule.java b/core/src/main/java/org/eigenbase/rel/rules/OptimizeBushyJoinRule.java
new file mode 100644
index 0000000..297ce9c
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/rules/OptimizeBushyJoinRule.java
@@ -0,0 +1,58 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde 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.eigenbase.rel.rules;
+
+import java.util.*;
+
+import org.eigenbase.rel.*;
+import org.eigenbase.relopt.*;
+import org.eigenbase.rex.*;
+import org.eigenbase.util.Util;
+
+/**
+ * Planner rule that finds an approximately optimal ordering for join operators
+ * using a heuristic algorithm.
+ *
+ * <p>It is triggered by the pattern {@link ProjectRel} ({@link MultiJoinRel}).
+ *
+ * <p>It is similar to {@link org.eigenbase.rel.rules.LoptOptimizeJoinRule}.
+ * {@code LoptOptimizeJoinRule} is only capable of producing left-deep joins;
+ * this rule is capable of producing bushy joins.
+ */
+public class OptimizeBushyJoinRule extends RelOptRule {
+ public static final OptimizeBushyJoinRule INSTANCE =
+ new OptimizeBushyJoinRule(RelFactories.DEFAULT_JOIN_FACTORY);
+
+ private final RelFactories.JoinFactory joinFactory;
+
+ /** Creates an OptimizeBushyJoinRule. */
+ public OptimizeBushyJoinRule(RelFactories.JoinFactory joinFactory) {
+ super(operand(MultiJoinRel.class, any()));
+ this.joinFactory = joinFactory;
+ }
+
+ @Override public void onMatch(RelOptRuleCall call) {
+ final MultiJoinRel multiJoinRel = call.rel(0);
+ final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
+
+ final RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
+ Util.discard(multiJoin);
+ }
+}
+
+// End OptimizeBushyJoinRule.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java b/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
index 0a713c1..e23c6b5 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
@@ -518,32 +518,37 @@ public class OptiqAssert {
throw new AssertionError("method " + methodName + " not found");
}
- static OptiqConnection getConnection(String... schema)
- throws ClassNotFoundException, SQLException {
- final List<String> schemaList = Arrays.asList(schema);
- Class.forName("net.hydromatic.optiq.jdbc.Driver");
- String suffix = schemaList.contains("spark") ? "spark=true" : "";
- Connection connection =
- DriverManager.getConnection("jdbc:optiq:" + suffix);
- OptiqConnection optiqConnection =
- connection.unwrap(OptiqConnection.class);
- SchemaPlus rootSchema = optiqConnection.getRootSchema();
- if (schemaList.contains("hr")) {
- rootSchema.add("hr", new ReflectiveSchema(new JdbcTest.HrSchema()));
- }
- if (schemaList.contains("foodmart")) {
- rootSchema.add("foodmart",
+ public static SchemaPlus addSchema(SchemaPlus rootSchema, SchemaSpec schema) {
+ switch (schema) {
+ case REFLECTIVE_FOODMART:
+ return rootSchema.add("foodmart",
new ReflectiveSchema(new JdbcTest.FoodmartSchema()));
- }
- if (schemaList.contains("lingual")) {
- rootSchema.add("SALES",
+ case JDBC_FOODMART:
+ final DataSource dataSource =
+ JdbcSchema.dataSource(
+ CONNECTION_SPEC.url,
+ CONNECTION_SPEC.driver,
+ CONNECTION_SPEC.username,
+ CONNECTION_SPEC.password);
+ return rootSchema.add("foodmart",
+ JdbcSchema.create(rootSchema, "foodmart", dataSource, null,
+ "foodmart"));
+ case CLONE_FOODMART:
+ SchemaPlus foodmart = rootSchema.getSubSchema("foodmart");
+ if (foodmart == null) {
+ foodmart = OptiqAssert.addSchema(rootSchema, SchemaSpec.JDBC_FOODMART);
+ }
+ return rootSchema.add("foodmart2", new CloneSchema(foodmart));
+ case HR:
+ return rootSchema.add("hr",
+ new ReflectiveSchema(new JdbcTest.HrSchema()));
+ case LINGUAL:
+ return rootSchema.add("SALES",
new ReflectiveSchema(new JdbcTest.LingualSchema()));
- }
- if (schemaList.contains("post")) {
+ case POST:
final SchemaPlus post = rootSchema.add("POST", new AbstractSchema());
post.add("EMP",
- ViewTable.viewMacro(
- post,
+ ViewTable.viewMacro(post,
"select * from (values\n"
+ " ('Jane', 10, 'F'),\n"
+ " ('Bob', 10, 'M'),\n"
@@ -557,8 +562,7 @@ public class OptiqAssert {
+ " as t(ename, deptno, gender)",
ImmutableList.<String>of()));
post.add("DEPT",
- ViewTable.viewMacro(
- post,
+ ViewTable.viewMacro(post,
"select * from (values\n"
+ " (10, 'Sales'),\n"
+ " (20, 'Marketing'),\n"
@@ -566,8 +570,7 @@ public class OptiqAssert {
+ " (40, 'Empty')) as t(deptno, dname)",
ImmutableList.<String>of()));
post.add("EMPS",
- ViewTable.viewMacro(
- post,
+ ViewTable.viewMacro(post,
"select * from (values\n"
+ " (100, 'Fred', 10, CAST(NULL AS CHAR(1)), CAST(NULL AS VARCHAR(20)), 40, 25, TRUE, FALSE, DATE '1996-08-03'),\n"
+ " (110, 'Eric', 20, 'M', 'San Francisco', 3, 80, UNKNOWN, FALSE, DATE '2001-01-01'),\n"
@@ -576,6 +579,33 @@ public class OptiqAssert {
+ " (130, 'Alice', 40, 'F', 'Vancouver', 2, CAST(NULL AS INT), FALSE, TRUE, DATE '2007-01-01'))\n"
+ " as t(empno, name, deptno, gender, city, empid, age, slacker, manager, joinedat)",
ImmutableList.<String>of()));
+ return post;
+ default:
+ throw new AssertionError("unknown schema " + schema);
+ }
+ }
+
+ static OptiqConnection getConnection(String... schema)
+ throws ClassNotFoundException, SQLException {
+ final List<String> schemaList = Arrays.asList(schema);
+ Class.forName("net.hydromatic.optiq.jdbc.Driver");
+ String suffix = schemaList.contains("spark") ? "spark=true" : "";
+ Connection connection =
+ DriverManager.getConnection("jdbc:optiq:" + suffix);
+ OptiqConnection optiqConnection =
+ connection.unwrap(OptiqConnection.class);
+ SchemaPlus rootSchema = optiqConnection.getRootSchema();
+ if (schemaList.contains("hr")) {
+ addSchema(rootSchema, SchemaSpec.HR);
+ }
+ if (schemaList.contains("foodmart")) {
+ addSchema(rootSchema, SchemaSpec.REFLECTIVE_FOODMART);
+ }
+ if (schemaList.contains("lingual")) {
+ addSchema(rootSchema, SchemaSpec.LINGUAL);
+ }
+ if (schemaList.contains("post")) {
+ addSchema(rootSchema, SchemaSpec.POST);
}
if (schemaList.contains("metadata")) {
// always present
@@ -602,18 +632,9 @@ public class OptiqAssert {
OptiqConnection optiqConnection =
connection.unwrap(OptiqConnection.class);
final SchemaPlus rootSchema = optiqConnection.getRootSchema();
- final DataSource dataSource =
- JdbcSchema.dataSource(
- CONNECTION_SPEC.url,
- CONNECTION_SPEC.driver,
- CONNECTION_SPEC.username,
- CONNECTION_SPEC.password);
- final SchemaPlus foodmart =
- rootSchema.add("foodmart",
- JdbcSchema.create(rootSchema, "foodmart", dataSource, null,
- "foodmart"));
+ addSchema(rootSchema, SchemaSpec.JDBC_FOODMART);
if (withClone) {
- rootSchema.add("foodmart2", new CloneSchema(foodmart));
+ addSchema(rootSchema, SchemaSpec.CLONE_FOODMART);
}
optiqConnection.setSchema("foodmart2");
return optiqConnection;
@@ -1177,6 +1198,15 @@ public class OptiqAssert {
this.driver = driver;
}
}
+
+ public enum SchemaSpec {
+ REFLECTIVE_FOODMART,
+ JDBC_FOODMART,
+ CLONE_FOODMART,
+ HR,
+ LINGUAL,
+ POST
+ }
}
// End OptiqAssert.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/929f5310/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java b/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
index 75282c7..77e734e 100644
--- a/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/tools/PlannerTest.java
@@ -26,7 +26,6 @@ import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
import net.hydromatic.optiq.rules.java.EnumerableConvention;
import net.hydromatic.optiq.rules.java.JavaRules;
import net.hydromatic.optiq.rules.java.JavaRules.EnumerableProjectRel;
-import net.hydromatic.optiq.test.JdbcTest;
import net.hydromatic.optiq.test.OptiqAssert;
import org.eigenbase.rel.*;
@@ -143,10 +142,13 @@ public class PlannerTest {
ImmutableList.of(stdOpTab,
new ListSqlOperatorTable(
ImmutableList.<SqlOperator>of(new MyCountAggFunction()))));
- Planner planner = Frameworks.getPlanner(Frameworks.newConfigBuilder() //
- .defaultSchema(createHrSchema()) //
- .operatorTable(opTab) //
- .build());
+ final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+ final FrameworkConfig config = Frameworks.newConfigBuilder()
+ .defaultSchema(
+ OptiqAssert.addSchema(rootSchema, OptiqAssert.SchemaSpec.HR))
+ .operatorTable(opTab)
+ .build();
+ final Planner planner = Frameworks.getPlanner(config);
SqlNode parse =
planner.parse("select \"deptno\", my_count(\"empid\") from \"emps\"\n"
+ "group by \"deptno\"");
@@ -175,18 +177,16 @@ public class PlannerTest {
}
}
- private SchemaPlus createHrSchema() {
- return Frameworks.createRootSchema(true).add("hr",
- new ReflectiveSchema(new JdbcTest.HrSchema()));
- }
-
private Planner getPlanner(List<RelTraitDef> traitDefs, Program... programs) {
- return Frameworks.getPlanner(Frameworks.newConfigBuilder() //
- .lex(Lex.ORACLE) //
- .defaultSchema(createHrSchema()) //
- .traitDefs(traitDefs) //
- .programs(programs) //
- .build());
+ final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+ final FrameworkConfig config = Frameworks.newConfigBuilder()
+ .lex(Lex.ORACLE)
+ .defaultSchema(
+ OptiqAssert.addSchema(rootSchema, OptiqAssert.SchemaSpec.HR))
+ .traitDefs(traitDefs)
+ .programs(programs)
+ .build();
+ return Frameworks.getPlanner(config);
}
/** Tests that planner throws an error if you pass to
@@ -443,7 +443,8 @@ public class PlannerTest {
.append(i).append(".\"deptno\" = d")
.append(i - 1).append(".\"deptno\"");
}
- Planner planner = getPlanner(null, Programs.heuristicJoinOrder(RULE_SET));
+ Planner planner =
+ getPlanner(null, Programs.heuristicJoinOrder(RULE_SET, false));
SqlNode parse = planner.parse(buf.toString());
SqlNode validate = planner.validate(parse);
@@ -455,6 +456,43 @@ public class PlannerTest {
"EnumerableJoinRel(condition=[=($3, $0)], joinType=[inner])"));
}
+ /** Plans a 4-table join query on the FoodMart schema. The ideal plan is not
+ * bushy, but nevertheless exercises the bushy-join heuristic optimizer. */
+ @Test public void testAlmostBushy() throws Exception {
+ final String sql = "select *\n"
+ + "from \"sales_fact_1997\" as s\n"
+ + " join \"customer\" as c using (\"customer_id\")\n"
+ + " join \"product\" as p using (\"product_id\")\n"
+ + "where c.\"city\" = 'San Francisco'\n"
+ + "and p.\"brand_name\" = 'Washington'";
+ final String expected = ""
+ + "EnumerableJoinRel(condition=[=($0, $38)], joinType=[inner])\n"
+ + " EnumerableJoinRel(condition=[=($2, $8)], joinType=[inner])\n"
+ + " EnumerableTableAccessRel(table=[[foodmart2, sales_fact_1997]])\n"
+ + " EnumerableFilterRel(condition=[=($9, 'San Francisco')])\n"
+ + " EnumerableTableAccessRel(table=[[foodmart2, customer]])\n"
+ + " EnumerableFilterRel(condition=[=($2, 'Washington')])\n"
+ + " EnumerableTableAccessRel(table=[[foodmart2, product]])\n";
+ final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+ final FrameworkConfig config = Frameworks.newConfigBuilder()
+ .lex(Lex.ORACLE)
+ .defaultSchema(
+ OptiqAssert.addSchema(rootSchema,
+ OptiqAssert.SchemaSpec.CLONE_FOODMART))
+ .traitDefs((List<RelTraitDef>) null)
+ .programs(Programs.heuristicJoinOrder(RULE_SET, true))
+ .build();
+ Planner planner = Frameworks.getPlanner(config);
+ SqlNode parse = planner.parse(sql);
+
+ SqlNode validate = planner.validate(parse);
+ RelNode convert = planner.convert(validate);
+ RelTraitSet traitSet = planner.getEmptyTraitSet()
+ .replace(EnumerableConvention.INSTANCE);
+ RelNode transform = planner.transform(0, traitSet, convert);
+ assertThat(toString(transform), containsString(expected));
+ }
+
/**
* Rule to convert a {@link EnumerableProjectRel} to an
* {@link JdbcProjectRel}.