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/09/05 03:39:39 UTC
[1/2] [OPTIQ-402] Lattice should create materializations on demand
Repository: incubator-optiq
Updated Branches:
refs/heads/master 32e0b6afb -> c3bf95bb5
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java b/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
index 2c473c9..e1cacd2 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/LatticeTest.java
@@ -16,14 +16,23 @@
*/
package net.hydromatic.optiq.test;
+import net.hydromatic.linq4j.function.Function1;
+
+import net.hydromatic.optiq.runtime.Hook;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptUtil;
import org.eigenbase.util.TestUtil;
import org.eigenbase.util.Util;
+import com.google.common.base.Function;
+
import org.junit.Test;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
+import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
@@ -123,8 +132,8 @@ public class LatticeTest {
@Test public void testLatticeInvalidSql() {
modelWithLattice("star",
"select 1 from \"foodmart\".\"sales_fact_1997\" as s\n"
- + "join \"foodmart\".\"product\" as p using (\"product_id\")\n"
- + "join \"foodmart\".\"time_by_day\" as t on s.\"product_id\" = 100")
+ + "join \"foodmart\".\"product\" as p using (\"product_id\")\n"
+ + "join \"foodmart\".\"time_by_day\" as t on s.\"product_id\" = 100")
.connectThrows("only equi-join of columns allowed: 100");
}
@@ -140,39 +149,94 @@ public class LatticeTest {
/** When a lattice is registered, there is a table with the same name.
* It can be used for explain, but not for queries. */
@Test public void testLatticeStarTable() {
+ final AtomicInteger counter = new AtomicInteger();
try {
foodmartModel()
.query("select count(*) from \"adhoc\".\"star\"")
- .convertContains(
- "AggregateRel(group=[{}], EXPR$0=[COUNT()])\n"
- + " ProjectRel(DUMMY=[0])\n"
- + " StarTableScan(table=[[adhoc, star]])\n");
+ .convertMatches(
+ OptiqAssert.checkRel(
+ "AggregateRel(group=[{}], EXPR$0=[COUNT()])\n"
+ + " ProjectRel(DUMMY=[0])\n"
+ + " StarTableScan(table=[[adhoc, star]])\n",
+ counter));
} catch (RuntimeException e) {
assertThat(Util.getStackTrace(e), containsString("CannotPlanException"));
}
+ assertThat(counter.get(), equalTo(1));
}
/** Tests that a 2-way join query can be mapped 4-way join lattice. */
@Test public void testLatticeRecognizeJoin() {
final AtomicInteger counter = new AtomicInteger();
foodmartModel()
- .query(
- "select s.\"unit_sales\", p.\"brand_name\"\n"
- + "from \"foodmart\".\"sales_fact_1997\" as s\n"
- + "join \"foodmart\".\"product\" as p using (\"product_id\")\n")
+ .query(
+ "select s.\"unit_sales\", p.\"brand_name\"\n"
+ + "from \"foodmart\".\"sales_fact_1997\" as s\n"
+ + "join \"foodmart\".\"product\" as p using (\"product_id\")\n")
+ .enableMaterializations(true)
.substitutionMatches(
OptiqAssert.checkRel(
- "ProjectRel(unit_sales=[$1], brand_name=[$3])\n"
- + " JoinRel(condition=[=($0, $2)], joinType=[inner])\n"
- + " ProjectRel(product_id=[$0], unit_sales=[$7])\n"
- + " ProjectRel($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7])\n"
- + " TableAccessRel(table=[[adhoc, star]])\n"
- + " ProjectRel(product_id=[$1], brand_name=[$2])\n"
- + " JdbcTableScan(table=[[foodmart, product]])\n",
+ "ProjectRel(unit_sales=[$7], brand_name=[$10])\n"
+ + " ProjectRel($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7], $f8=[$8], $f9=[$9], $f10=[$10], $f11=[$11], $f12=[$12], $f13=[$13], $f14=[$14], $f15=[$15], $f16=[$16], $f17=[$17], $f18=[$18], $f19=[$19], $f20=[$20], $f21=[$21], $f22=[$22])\n"
+ + " TableAccessRel(table=[[adhoc, star]])\n",
counter));
assertThat(counter.intValue(), equalTo(1));
}
+ /** Tests an aggregate on a 2-way join query can use an aggregate table. */
+ @Test public void testLatticeRecognizeGroupJoin() {
+ final AtomicInteger counter = new AtomicInteger();
+ OptiqAssert.AssertQuery that = foodmartModel()
+ .query(
+ "select distinct p.\"brand_name\", s.\"customer_id\"\n"
+ + "from \"foodmart\".\"sales_fact_1997\" as s\n"
+ + "join \"foodmart\".\"product\" as p using (\"product_id\")\n")
+ .enableMaterializations(true)
+ .substitutionMatches(
+ new Function1<RelNode, Void>() {
+ public Void apply(RelNode relNode) {
+ counter.incrementAndGet();
+ String s = RelOptUtil.toString(relNode);
+ assertThat(s,
+ anyOf(
+ containsString(
+ "AggregateRel(group=[{0, 1}])\n"
+ + " ProjectRel(brand_name=[$10], customer_id=[$2])\n"
+ + " ProjectRel($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7], $f8=[$8], $f9=[$9], $f10=[$10], $f11=[$11], $f12=[$12], $f13=[$13], $f14=[$14], $f15=[$15], $f16=[$16], $f17=[$17], $f18=[$18], $f19=[$19], $f20=[$20], $f21=[$21], $f22=[$22])\n"
+ + " TableAccessRel(table=[[adhoc, star]])\n"),
+ containsString(
+ "AggregateRel(group=[{0, 1}])\n"
+ + " ProjectRel(customer_id=[$2], brand_name=[$10])\n"
+ + " ProjectRel($f0=[$0], $f1=[$1], $f2=[$2], $f3=[$3], $f4=[$4], $f5=[$5], $f6=[$6], $f7=[$7], $f8=[$8], $f9=[$9], $f10=[$10], $f11=[$11], $f12=[$12], $f13=[$13], $f14=[$14], $f15=[$15], $f16=[$16], $f17=[$17], $f18=[$18], $f19=[$19], $f20=[$20], $f21=[$21], $f22=[$22])\n"
+ + " TableAccessRel(table=[[adhoc, star]])\n")));
+ return null;
+ }
+ });
+ assertThat(counter.intValue(), equalTo(2));
+ that.explainContains(
+ "EnumerableCalcRel(expr#0..1=[{inputs}], $f0=[$t1], $f1=[$t0])\n"
+ + " EnumerableTableAccessRel(table=[[adhoc, m{2, 10}]])")
+ .returnsCount(69203);
+
+ // Run the same query again and see whether it uses the same
+ // materialization.
+ that.withHook(
+ Hook.CREATE_MATERIALIZATION,
+ new Function<String, Void>() {
+ public Void apply(String materializationName) {
+ counter.incrementAndGet();
+ return null;
+ }
+ })
+ .returnsCount(69203);
+
+ // Ideally the counter would stay at 2. It increments to 3 because
+ // OptiqAssert.AssertQuery creates a new schema for every request,
+ // and therefore cannot re-use lattices or materializations from the
+ // previous request.
+ assertThat(counter.intValue(), equalTo(3));
+ }
+
private OptiqAssert.AssertThat foodmartModel() {
return modelWithLattice("star",
"select 1 from \"foodmart\".\"sales_fact_1997\" as s\n"
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/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 b2fe555..4cc87ce 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/OptiqAssert.java
@@ -274,11 +274,10 @@ public class OptiqAssert {
return new Function1<ResultSet, Void>() {
public Void apply(ResultSet resultSet) {
try {
- final List<String> expectedList = new ArrayList<String>();
- Collections.addAll(expectedList, lines);
+ final List<String> expectedList = Lists.newArrayList(lines);
Collections.sort(expectedList);
- final List<String> actualList = new ArrayList<String>();
+ final List<String> actualList = Lists.newArrayList();
OptiqAssert.toStringList(resultSet, actualList);
Collections.sort(actualList);
@@ -374,6 +373,9 @@ public class OptiqAssert {
((OptiqConnection) connection).getProperties().setProperty(
OptiqConnectionProperty.MATERIALIZATIONS_ENABLED.camelName(),
Boolean.toString(materializationsEnabled));
+ ((OptiqConnection) connection).getProperties().setProperty(
+ OptiqConnectionProperty.CREATE_MATERIALIZATIONS.camelName(),
+ Boolean.toString(materializationsEnabled));
for (Pair<Hook, Function> hook : hooks) {
closeableList.add(hook.left.addThread(hook.right));
}
@@ -446,6 +448,9 @@ public class OptiqAssert {
((OptiqConnection) connection).getProperties().setProperty(
OptiqConnectionProperty.MATERIALIZATIONS_ENABLED.camelName(),
Boolean.toString(materializationsEnabled));
+ ((OptiqConnection) connection).getProperties().setProperty(
+ OptiqConnectionProperty.CREATE_MATERIALIZATIONS.camelName(),
+ Boolean.toString(materializationsEnabled));
PreparedStatement statement = connection.prepareStatement(sql);
statement.close();
connection.close();
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/test/java/org/eigenbase/test/MockRelOptPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/MockRelOptPlanner.java b/core/src/test/java/org/eigenbase/test/MockRelOptPlanner.java
index 1d46a40..102309b 100644
--- a/core/src/test/java/org/eigenbase/test/MockRelOptPlanner.java
+++ b/core/src/test/java/org/eigenbase/test/MockRelOptPlanner.java
@@ -58,14 +58,6 @@ public class MockRelOptPlanner extends AbstractRelOptPlanner {
return root;
}
- public void addMaterialization(RelOptMaterialization materialization) {
- // ignore - this planner does not support materializations
- }
-
- public void addLattice(RelOptLattice lattice) {
- // ignore - this planner does not support lattices
- }
-
@Override public void clear() {
super.clear();
this.rule = null;
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java b/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java
index 1bd4708..31a9906 100644
--- a/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java
@@ -25,6 +25,7 @@ import org.eigenbase.rel.metadata.ChainedRelMetadataProvider;
import org.eigenbase.rel.metadata.DefaultRelMetadataProvider;
import org.eigenbase.rel.metadata.RelMetadataProvider;
import org.eigenbase.rel.rules.AddRedundantSemiJoinRule;
+import org.eigenbase.rel.rules.AggregateProjectMergeRule;
import org.eigenbase.rel.rules.CoerceInputsRule;
import org.eigenbase.rel.rules.ConvertMultiJoinRule;
import org.eigenbase.rel.rules.ExtractJoinFilterRule;
@@ -871,6 +872,17 @@ public class RelOptRulesTest extends RelOptTestBase {
basePullConstantTroughAggregate();
}
+ @Test public void testAggregateProjectMerge() throws Exception {
+ HepProgram program = new HepProgramBuilder()
+ .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+ .build();
+ checkPlanning(program,
+ "select x, sum(z), y from (\n"
+ + " select deptno as x, empno as y, sal as z, sal * 2 as zz\n"
+ + " from emp)\n"
+ + "group by x, y");
+ }
+
public void transitiveInference() throws Exception {
final DiffRepository diffRepos = getDiffRepos();
String sql = diffRepos.expand(null, "${sql}");
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
index 0232bf9..e96dfb1 100644
--- a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml
@@ -2133,4 +2133,29 @@ ProjectRel(DEPTNO=[$0])
]]>
</Resource>
</TestCase>
+ <TestCase name="testAggregateProjectMerge">
+ <Resource name="sql">
+ <![CDATA[select x, sum(z), y from (
+ select deptno as x, empno as y, sal as z, sal * 2 as zz
+ from emp)
+group by x, y]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+ProjectRel(X=[$0], EXPR$1=[$2], Y=[$1])
+ AggregateRel(group=[{0, 1}], EXPR$1=[SUM($2)])
+ ProjectRel(X=[$0], Y=[$1], Z=[$2])
+ ProjectRel(X=[$7], Y=[$0], Z=[$5], ZZ=[*($5, 2)])
+ TableAccessRel(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+ProjectRel(X=[$0], EXPR$1=[$2], Y=[$1])
+ ProjectRel($f0=[$1], $f1=[$0], $f2=[$2])
+ AggregateRel(group=[{0, 7}], EXPR$1=[SUM($5)])
+ TableAccessRel(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
</Root>
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/mongodb/src/test/java/net/hydromatic/optiq/test/MongoAdapterTest.java
----------------------------------------------------------------------
diff --git a/mongodb/src/test/java/net/hydromatic/optiq/test/MongoAdapterTest.java b/mongodb/src/test/java/net/hydromatic/optiq/test/MongoAdapterTest.java
index edc098d..bb32774 100644
--- a/mongodb/src/test/java/net/hydromatic/optiq/test/MongoAdapterTest.java
+++ b/mongodb/src/test/java/net/hydromatic/optiq/test/MongoAdapterTest.java
@@ -23,6 +23,7 @@ import org.eigenbase.util.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
import org.junit.*;
@@ -133,16 +134,17 @@ public class MongoAdapterTest {
};
}
+ /** Similar to {@link OptiqAssert#checkResultUnordered}, but filters strings
+ * before comparing them. */
static Function1<ResultSet, Void> checkResultUnordered(
final String... lines) {
return new Function1<ResultSet, Void>() {
public Void apply(ResultSet resultSet) {
try {
- final List<String> expectedList = new ArrayList<String>();
- Collections.addAll(expectedList, lines);
+ final List<String> expectedList = Lists.newArrayList(lines);
Collections.sort(expectedList);
- final List<String> actualList = new ArrayList<String>();
+ final List<String> actualList = Lists.newArrayList();
OptiqAssert.toStringList(resultSet, actualList);
for (int i = 0; i < actualList.size(); i++) {
String s = actualList.get(i);
[2/2] git commit: [OPTIQ-402] Lattice should create materializations
on demand
Posted by jh...@apache.org.
[OPTIQ-402] Lattice should create materializations on demand
Project: http://git-wip-us.apache.org/repos/asf/incubator-optiq/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-optiq/commit/c3bf95bb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-optiq/tree/c3bf95bb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-optiq/diff/c3bf95bb
Branch: refs/heads/master
Commit: c3bf95bb539ae78ffba1db82791b660118ca167f
Parents: 32e0b6a
Author: Julian Hyde <jh...@apache.org>
Authored: Tue Sep 2 12:27:25 2014 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Sep 4 17:59:34 2014 -0700
----------------------------------------------------------------------
.../main/java/net/hydromatic/optiq/Schemas.java | 51 ++++++--
.../optiq/config/OptiqConnectionConfig.java | 11 ++
.../optiq/config/OptiqConnectionConfigImpl.java | 87 +++++++++++++
.../optiq/config/OptiqConnectionProperty.java | 5 +-
.../net/hydromatic/optiq/impl/StarTable.java | 21 ++--
.../optiq/impl/jdbc/JdbcImplementor.java | 2 +-
.../optiq/jdbc/OptiqConnectionImpl.java | 51 +-------
.../hydromatic/optiq/materialize/Lattice.java | 66 +++++++++-
.../optiq/materialize/MaterializationActor.java | 34 ++++-
.../materialize/MaterializationService.java | 18 ++-
.../optiq/prepare/OptiqCatalogReader.java | 2 +-
.../optiq/prepare/OptiqMaterializer.java | 2 +-
.../optiq/prepare/OptiqPrepareImpl.java | 8 +-
.../net/hydromatic/optiq/prepare/Prepare.java | 2 +-
.../optiq/prepare/QueryableRelBuilder.java | 2 +-
.../optiq/prepare/RelOptTableImpl.java | 35 ++++--
.../hydromatic/optiq/rules/java/JavaRules.java | 4 +-
.../java/net/hydromatic/optiq/runtime/Hook.java | 3 +
.../net/hydromatic/optiq/tools/Programs.java | 17 +--
.../java/org/eigenbase/rel/AggregateCall.java | 10 ++
.../rel/rules/AggregateProjectMergeRule.java | 123 +++++++++++++++++++
.../rel/rules/AggregateStarTableRule.java | 115 +++++++++++++++++
.../rules/PushAggregateThroughUnionRule.java | 2 +-
.../eigenbase/relopt/AbstractRelOptPlanner.java | 19 ++-
.../java/org/eigenbase/relopt/Contexts.java | 65 ++++++++++
.../org/eigenbase/relopt/RelOptLattice.java | 110 ++++++++++++++++-
.../eigenbase/relopt/RelOptMaterialization.java | 48 +++++---
.../org/eigenbase/relopt/RelOptPlanner.java | 8 +-
.../eigenbase/relopt/SubstitutionVisitor.java | 8 +-
.../org/eigenbase/relopt/hep/HepPlanner.java | 18 +--
.../relopt/volcano/VolcanoPlanner.java | 39 ++++--
.../main/java/org/eigenbase/rex/RexBuilder.java | 4 +-
.../main/java/org/eigenbase/sql/SqlDialect.java | 1 +
.../org/eigenbase/sql2rel/RelFieldTrimmer.java | 7 +-
.../main/java/org/eigenbase/util/XmlOutput.java | 5 +-
.../net/hydromatic/optiq/test/JdbcTest.java | 28 ++++-
.../net/hydromatic/optiq/test/LatticeTest.java | 98 ++++++++++++---
.../net/hydromatic/optiq/test/OptiqAssert.java | 11 +-
.../org/eigenbase/test/MockRelOptPlanner.java | 8 --
.../org/eigenbase/test/RelOptRulesTest.java | 12 ++
.../org/eigenbase/test/RelOptRulesTest.xml | 25 ++++
.../hydromatic/optiq/test/MongoAdapterTest.java | 8 +-
42 files changed, 993 insertions(+), 200 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/Schemas.java b/core/src/main/java/net/hydromatic/optiq/Schemas.java
index 3f279c5..4d9a426 100644
--- a/core/src/main/java/net/hydromatic/optiq/Schemas.java
+++ b/core/src/main/java/net/hydromatic/optiq/Schemas.java
@@ -21,6 +21,8 @@ import net.hydromatic.linq4j.Queryable;
import net.hydromatic.linq4j.expressions.*;
import net.hydromatic.optiq.config.OptiqConnectionConfig;
+import net.hydromatic.optiq.config.OptiqConnectionConfigImpl;
+import net.hydromatic.optiq.config.OptiqConnectionProperty;
import net.hydromatic.optiq.impl.java.JavaTypeFactory;
import net.hydromatic.optiq.jdbc.*;
import net.hydromatic.optiq.materialize.Lattice;
@@ -191,7 +193,8 @@ public final class Schemas {
final List<String> schemaPath, final String sql) {
final OptiqPrepare prepare = OptiqPrepare.DEFAULT_FACTORY.apply();
final OptiqPrepare.Context context =
- makeContext(connection, schema, schemaPath);
+ makeContext(connection, schema, schemaPath,
+ ImmutableMap.<OptiqConnectionProperty, String>of());
OptiqPrepare.Dummy.push(context);
try {
return prepare.parse(context, sql);
@@ -207,7 +210,8 @@ public final class Schemas {
final List<String> schemaPath, final String sql) {
final OptiqPrepare prepare = OptiqPrepare.DEFAULT_FACTORY.apply();
final OptiqPrepare.Context context =
- makeContext(connection, schema, schemaPath);
+ makeContext(connection, schema, schemaPath,
+ ImmutableMap.<OptiqConnectionProperty, String>of());
OptiqPrepare.Dummy.push(context);
try {
return prepare.convert(context, sql);
@@ -219,26 +223,46 @@ public final class Schemas {
/** Prepares a SQL query for execution. For use within Optiq only. */
public static OptiqPrepare.PrepareResult<Object> prepare(
final OptiqConnection connection, final OptiqSchema schema,
- final List<String> schemaPath, final String sql) {
+ final List<String> schemaPath, final String sql,
+ final ImmutableMap<OptiqConnectionProperty, String> map) {
final OptiqPrepare prepare = OptiqPrepare.DEFAULT_FACTORY.apply();
- return prepare.prepareSql(
- makeContext(connection, schema, schemaPath), sql, null, Object[].class,
- -1);
+ final OptiqPrepare.Context context =
+ makeContext(connection, schema, schemaPath, map);
+ OptiqPrepare.Dummy.push(context);
+ try {
+ return prepare.prepareSql(context, sql, null, Object[].class, -1);
+ } finally {
+ OptiqPrepare.Dummy.pop(context);
+ }
}
- private static OptiqPrepare.Context makeContext(
+ public static OptiqPrepare.Context makeContext(
final OptiqConnection connection, final OptiqSchema schema,
- final List<String> schemaPath) {
+ final List<String> schemaPath,
+ final ImmutableMap<OptiqConnectionProperty, String> propValues) {
if (connection == null) {
final OptiqPrepare.Context context0 = OptiqPrepare.Dummy.peek();
- return makeContext(context0.config(), context0.getTypeFactory(),
+ final OptiqConnectionConfig config =
+ mutate(context0.config(), propValues);
+ return makeContext(config, context0.getTypeFactory(),
context0.getDataContext(), schema, schemaPath);
} else {
- return makeContext(connection.config(), connection.getTypeFactory(),
+ final OptiqConnectionConfig config =
+ mutate(connection.config(), propValues);
+ return makeContext(config, connection.getTypeFactory(),
createDataContext(connection), schema, schemaPath);
}
}
+ private static OptiqConnectionConfig mutate(OptiqConnectionConfig config,
+ ImmutableMap<OptiqConnectionProperty, String> propValues) {
+ for (Map.Entry<OptiqConnectionProperty, String> e : propValues.entrySet()) {
+ config =
+ ((OptiqConnectionConfigImpl) config).set(e.getKey(), e.getValue());
+ }
+ return config;
+ }
+
private static OptiqPrepare.Context makeContext(
final OptiqConnectionConfig connectionConfig,
final JavaTypeFactory typeFactory,
@@ -334,6 +358,13 @@ public final class Schemas {
}
}
+ public static OptiqSchema subSchema(OptiqSchema schema, List<String> names) {
+ for (String string : names) {
+ schema = schema.getSubSchema(string, false);
+ }
+ return schema;
+ }
+
/** Dummy data context that has no variables. */
private static class DummyDataContext implements DataContext {
private final OptiqConnection connection;
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
index 33bb4a9..a8a49ab 100644
--- a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfig.java
@@ -24,14 +24,25 @@ import net.hydromatic.avatica.Quoting;
* a method for every property. At some point there will be similar config
* classes for system and statement properties. */
public interface OptiqConnectionConfig extends ConnectionConfig {
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#AUTO_TEMP */
boolean autoTemp();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#MATERIALIZATIONS_ENABLED */
boolean materializationsEnabled();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#CREATE_MATERIALIZATIONS */
+ boolean createMaterializations();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#MODEL */
String model();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#LEX */
Lex lex();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#QUOTING */
Quoting quoting();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#UNQUOTED_CASING */
Casing unquotedCasing();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#QUOTED_CASING */
Casing quotedCasing();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#CASE_SENSITIVE */
boolean caseSensitive();
+ /** @see net.hydromatic.optiq.config.OptiqConnectionProperty#SPARK */
boolean spark();
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
new file mode 100644
index 0000000..719f33b
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionConfigImpl.java
@@ -0,0 +1,87 @@
+/*
+ * 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 net.hydromatic.optiq.config;
+
+import net.hydromatic.avatica.Casing;
+import net.hydromatic.avatica.ConnectionConfigImpl;
+import net.hydromatic.avatica.Quoting;
+
+import java.util.Properties;
+
+/** Implementation of {@link OptiqConnectionConfig}. */
+public class OptiqConnectionConfigImpl extends ConnectionConfigImpl
+ implements OptiqConnectionConfig {
+ public OptiqConnectionConfigImpl(Properties properties) {
+ super(properties);
+ }
+
+ /** Returns a copy of this configuration with one property changed. */
+ public OptiqConnectionConfigImpl set(OptiqConnectionProperty property,
+ String value) {
+ final Properties properties1 = new Properties(properties);
+ properties1.setProperty(property.camelName(), value);
+ return new OptiqConnectionConfigImpl(properties1);
+ }
+
+ public boolean autoTemp() {
+ return OptiqConnectionProperty.AUTO_TEMP.wrap(properties).getBoolean();
+ }
+
+ public boolean materializationsEnabled() {
+ return OptiqConnectionProperty.MATERIALIZATIONS_ENABLED.wrap(properties)
+ .getBoolean();
+ }
+
+ public boolean createMaterializations() {
+ return OptiqConnectionProperty.CREATE_MATERIALIZATIONS.wrap(properties)
+ .getBoolean();
+ }
+
+ public String model() {
+ return OptiqConnectionProperty.MODEL.wrap(properties).getString();
+ }
+
+ public Lex lex() {
+ return OptiqConnectionProperty.LEX.wrap(properties).getEnum(Lex.class);
+ }
+
+ public Quoting quoting() {
+ return OptiqConnectionProperty.QUOTING.wrap(properties)
+ .getEnum(Quoting.class, lex().quoting);
+ }
+
+ public Casing unquotedCasing() {
+ return OptiqConnectionProperty.UNQUOTED_CASING.wrap(properties)
+ .getEnum(Casing.class, lex().unquotedCasing);
+ }
+
+ public Casing quotedCasing() {
+ return OptiqConnectionProperty.QUOTED_CASING.wrap(properties)
+ .getEnum(Casing.class, lex().quotedCasing);
+ }
+
+ public boolean caseSensitive() {
+ return OptiqConnectionProperty.CASE_SENSITIVE.wrap(properties)
+ .getBoolean(lex().caseSensitive);
+ }
+
+ public boolean spark() {
+ return OptiqConnectionProperty.SPARK.wrap(properties).getBoolean();
+ }
+}
+
+// End OptiqConnectionConfigImpl.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
index 7c357a6..ba3089c 100644
--- a/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
+++ b/core/src/main/java/net/hydromatic/optiq/config/OptiqConnectionProperty.java
@@ -31,9 +31,12 @@ public enum OptiqConnectionProperty implements ConnectionProperty {
/** Whether to store query results in temporary tables. */
AUTO_TEMP("autoTemp", Type.BOOLEAN, false),
- /** Whether materializations are enabled. */
+ /** Whether Optiq should use materializations. */
MATERIALIZATIONS_ENABLED("materializationsEnabled", Type.BOOLEAN, true),
+ /** Whether Optiq should create materializations. */
+ CREATE_MATERIALIZATIONS("createMaterializations", Type.BOOLEAN, true),
+
/** URI of the model. */
MODEL("model", Type.STRING, null),
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/impl/StarTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/StarTable.java b/core/src/main/java/net/hydromatic/optiq/impl/StarTable.java
index 1e8b2a3..37d3dc9 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/StarTable.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/StarTable.java
@@ -18,6 +18,7 @@ package net.hydromatic.optiq.impl;
import net.hydromatic.optiq.Table;
import net.hydromatic.optiq.TranslatableTable;
+import net.hydromatic.optiq.materialize.Lattice;
import org.eigenbase.rel.*;
import org.eigenbase.relopt.*;
@@ -27,6 +28,7 @@ import org.eigenbase.sql.validate.SqlValidatorUtil;
import org.eigenbase.util.ImmutableIntList;
import org.eigenbase.util.Pair;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -44,6 +46,8 @@ import java.util.List;
* the materialization are mapped onto the same star table.</p>
*/
public class StarTable extends AbstractTable implements TranslatableTable {
+ public final Lattice lattice;
+
// TODO: we'll also need a list of join conditions between tables. For now
// we assume that join conditions match
public final ImmutableList<Table> tables;
@@ -52,14 +56,14 @@ public class StarTable extends AbstractTable implements TranslatableTable {
public ImmutableIntList fieldCounts;
/** Creates a StarTable. */
- public StarTable(List<Table> tables) {
- super();
- this.tables = ImmutableList.copyOf(tables);
+ private StarTable(Lattice lattice, ImmutableList<Table> tables) {
+ this.lattice = Preconditions.checkNotNull(lattice);
+ this.tables = tables;
}
/** Creates a StarTable and registers it in a schema. */
- public static StarTable of(List<Table> tables) {
- return new StarTable(tables);
+ public static StarTable of(Lattice lattice, List<Table> tables) {
+ return new StarTable(lattice, ImmutableList.copyOf(tables));
}
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
@@ -87,9 +91,8 @@ public class StarTable extends AbstractTable implements TranslatableTable {
}
public StarTable add(Table table) {
- final List<Table> tables1 = new ArrayList<Table>(tables);
- tables1.add(table);
- return of(tables1);
+ return of(lattice,
+ ImmutableList.<Table>builder().addAll(tables).add(table).build());
}
/** Returns the column offset of the first column of {@code table} in this
@@ -115,7 +118,7 @@ public class StarTable extends AbstractTable implements TranslatableTable {
*
* <p>It has infinite cost.
*/
- private static class StarTableScan extends TableAccessRelBase {
+ public static class StarTableScan extends TableAccessRelBase {
public StarTableScan(RelOptCluster cluster, RelOptTable relOptTable) {
super(cluster, cluster.traitSetOf(Convention.NONE), relOptTable);
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcImplementor.java b/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcImplementor.java
index cf38b6f..c4340ae 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcImplementor.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/jdbc/JdbcImplementor.java
@@ -377,7 +377,7 @@ public class JdbcImplementor {
select = asSelect();
clauseList.addAll(this.clauses);
}
- Collections.addAll(clauseList, clauses);
+ clauseList.appendAll(clauses);
Context newContext;
final SqlNodeList selectList = select.getSelectList();
if (selectList != null) {
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
index 2d640b0..b854dca 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
@@ -24,9 +24,8 @@ import net.hydromatic.linq4j.expressions.Expressions;
import net.hydromatic.linq4j.function.Function0;
import net.hydromatic.optiq.*;
-import net.hydromatic.optiq.config.Lex;
import net.hydromatic.optiq.config.OptiqConnectionConfig;
-import net.hydromatic.optiq.config.OptiqConnectionProperty;
+import net.hydromatic.optiq.config.OptiqConnectionConfigImpl;
import net.hydromatic.optiq.impl.AbstractSchema;
import net.hydromatic.optiq.impl.java.JavaTypeFactory;
import net.hydromatic.optiq.prepare.OptiqCatalogReader;
@@ -394,54 +393,6 @@ abstract class OptiqConnectionImpl
}
}
- /** Implementation of {@link OptiqConnectionConfig}. */
- private static class OptiqConnectionConfigImpl extends ConnectionConfigImpl
- implements OptiqConnectionConfig {
- public OptiqConnectionConfigImpl(Properties properties) {
- super(properties);
- }
-
- public boolean autoTemp() {
- return OptiqConnectionProperty.AUTO_TEMP.wrap(properties).getBoolean();
- }
-
- public boolean materializationsEnabled() {
- return OptiqConnectionProperty.MATERIALIZATIONS_ENABLED.wrap(properties)
- .getBoolean();
- }
-
- public String model() {
- return OptiqConnectionProperty.MODEL.wrap(properties).getString();
- }
-
- public Lex lex() {
- return OptiqConnectionProperty.LEX.wrap(properties).getEnum(Lex.class);
- }
-
- public Quoting quoting() {
- return OptiqConnectionProperty.QUOTING.wrap(properties)
- .getEnum(Quoting.class, lex().quoting);
- }
-
- public Casing unquotedCasing() {
- return OptiqConnectionProperty.UNQUOTED_CASING.wrap(properties)
- .getEnum(Casing.class, lex().unquotedCasing);
- }
-
- public Casing quotedCasing() {
- return OptiqConnectionProperty.QUOTED_CASING.wrap(properties)
- .getEnum(Casing.class, lex().quotedCasing);
- }
-
- public boolean caseSensitive() {
- return OptiqConnectionProperty.CASE_SENSITIVE.wrap(properties)
- .getBoolean(lex().caseSensitive);
- }
-
- public boolean spark() {
- return OptiqConnectionProperty.SPARK.wrap(properties).getBoolean();
- }
- }
}
// End OptiqConnectionImpl.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/materialize/Lattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/materialize/Lattice.java b/core/src/main/java/net/hydromatic/optiq/materialize/Lattice.java
index 984b157..89b9565 100644
--- a/core/src/main/java/net/hydromatic/optiq/materialize/Lattice.java
+++ b/core/src/main/java/net/hydromatic/optiq/materialize/Lattice.java
@@ -27,6 +27,12 @@ import net.hydromatic.optiq.util.graph.*;
import org.eigenbase.rel.*;
import org.eigenbase.relopt.RelOptUtil;
import org.eigenbase.rex.*;
+import org.eigenbase.sql.SqlJoin;
+import org.eigenbase.sql.SqlKind;
+import org.eigenbase.sql.SqlNode;
+import org.eigenbase.sql.SqlSelect;
+import org.eigenbase.sql.SqlUtil;
+import org.eigenbase.sql.validate.SqlValidatorUtil;
import org.eigenbase.util.mapping.IntPair;
import com.google.common.base.Preconditions;
@@ -40,6 +46,7 @@ import java.util.*;
*/
public class Lattice {
public final ImmutableList<Node> nodes;
+ public final ImmutableList<List<String>> columns;
private Lattice(List<Node> nodes) {
this.nodes = ImmutableList.copyOf(nodes);
@@ -54,6 +61,16 @@ public class Lattice {
assert nodes.subList(0, i).contains(node.parent);
}
}
+
+ final ImmutableList.Builder<List<String>> builder = ImmutableList.builder();
+ for (Node node : nodes) {
+ if (node.scan != null) {
+ for (String name : node.scan.getRowType().getFieldNames()) {
+ builder.add(ImmutableList.of(node.alias, name));
+ }
+ }
+ }
+ columns = builder.build();
}
/** Creates a Lattice. */
@@ -67,6 +84,10 @@ public class Lattice {
List<int[][]> tempLinks = Lists.newArrayList();
populate(relNodes, tempLinks, parsed.relNode);
+ // Get aliases.
+ List<String> aliases = Lists.newArrayList();
+ populateAliases(((SqlSelect) parsed.sqlNode).getFrom(), aliases, null);
+
// Build a graph.
final DirectedGraph<RelNode, Edge> graph =
DefaultDirectedGraph.create(Edge.FACTORY);
@@ -88,32 +109,52 @@ public class Lattice {
List<Node> nodes = Lists.newArrayList();
Node previous = null;
final Map<RelNode, Node> map = Maps.newIdentityHashMap();
+ int previousColumn = 0;
for (RelNode relNode : TopologicalOrderIterator.of(graph)) {
final List<Edge> edges = graph.getInwardEdges(relNode);
Node node;
+ final int column = previousColumn + relNode.getRowType().getFieldCount();
if (previous == null) {
if (!edges.isEmpty()) {
throw new RuntimeException("root node must not have relationships: "
+ relNode);
}
- node = new Node((TableAccessRelBase) relNode, null, null);
+ node = new Node((TableAccessRelBase) relNode, null, null,
+ previousColumn, column, aliases.get(nodes.size()));
} else {
if (edges.size() != 1) {
throw new RuntimeException(
"child node must have precisely one parent: " + relNode);
}
final Edge edge = edges.get(0);
- node =
- new Node((TableAccessRelBase) relNode, map.get(edge.getSource()),
- edge.pairs);
+ node = new Node((TableAccessRelBase) relNode, map.get(edge.getSource()),
+ edge.pairs, previousColumn, column, aliases.get(nodes.size()));
}
nodes.add(node);
map.put(relNode, node);
previous = node;
+ previousColumn = column;
}
return new Lattice(nodes);
}
+ private static void populateAliases(SqlNode from, List<String> aliases,
+ String current) {
+ if (from instanceof SqlJoin) {
+ SqlJoin join = (SqlJoin) from;
+ populateAliases(join.getLeft(), aliases, null);
+ populateAliases(join.getRight(), aliases, null);
+ } else if (from.getKind() == SqlKind.AS) {
+ populateAliases(SqlUtil.stripAs(from), aliases,
+ SqlValidatorUtil.getAlias(from, -1));
+ } else {
+ if (current == null) {
+ current = SqlValidatorUtil.getAlias(from, -1);
+ }
+ aliases.add(current);
+ }
+ }
+
private static boolean populate(List<RelNode> nodes, List<int[][]> tempLinks,
RelNode rel) {
if (nodes.isEmpty() && rel instanceof ProjectRel) {
@@ -177,7 +218,11 @@ public class Lattice {
for (Node node : nodes) {
tables.add(node.scan.getTable().unwrap(Table.class));
}
- return new StarTable(tables);
+ return StarTable.of(this, tables);
+ }
+
+ public List<String> getColumn(int i) {
+ return columns.get(i);
}
/** Source relation of a lattice.
@@ -189,12 +234,21 @@ public class Lattice {
public final TableAccessRelBase scan;
public final Node parent;
public final ImmutableList<IntPair> link;
+ public final int startCol;
+ public final int endCol;
+ public final String alias;
- public Node(TableAccessRelBase scan, Node parent, List<IntPair> link) {
+ public Node(TableAccessRelBase scan, Node parent, List<IntPair> link,
+ int startCol, int endCol, String alias) {
this.scan = Preconditions.checkNotNull(scan);
this.parent = parent;
this.link = link == null ? null : ImmutableList.copyOf(link);
assert (parent == null) == (link == null);
+ assert startCol >= 0;
+ assert endCol > startCol;
+ this.startCol = startCol;
+ this.endCol = endCol;
+ this.alias = alias;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationActor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationActor.java b/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationActor.java
index 80b2b6a..9ac009f 100644
--- a/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationActor.java
+++ b/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationActor.java
@@ -20,6 +20,9 @@ import net.hydromatic.optiq.jdbc.OptiqRootSchema;
import net.hydromatic.optiq.jdbc.OptiqSchema;
import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.util.Util;
+
+import com.google.common.collect.Maps;
import java.util.*;
@@ -30,8 +33,9 @@ class MaterializationActor {
// Not an actor yet -- TODO make members private and add request/response
// queues
- final Map<MaterializationKey, Materialization> keyMap =
- new HashMap<MaterializationKey, Materialization>();
+ final Map<MaterializationKey, Materialization> keyMap = Maps.newHashMap();
+
+ final Map<QueryKey, MaterializationKey> keyBySql = Maps.newHashMap();
/** A query materialized in a table, so that reading from the table gives the
* same results as executing the query. */
@@ -65,6 +69,32 @@ class MaterializationActor {
this.rowType = rowType;
}
}
+
+ /** A materialization can be re-used if it is the same SQL, on the same
+ * schema, with the same path for resolving functions. */
+ static class QueryKey {
+ final String sql;
+ final OptiqSchema schema;
+ final List<String> path;
+
+ QueryKey(String sql, OptiqSchema schema, List<String> path) {
+ this.sql = sql;
+ this.schema = schema;
+ this.path = path;
+ }
+
+ @Override public boolean equals(Object obj) {
+ return obj == this
+ || obj instanceof QueryKey
+ && sql.equals(((QueryKey) obj).sql)
+ && schema.equals(((QueryKey) obj).schema)
+ && path.equals(((QueryKey) obj).path);
+ }
+
+ @Override public int hashCode() {
+ return Util.hashV(sql, schema, path);
+ }
+ }
}
// End MaterializationActor.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationService.java b/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationService.java
index ec53fca..2bd3973 100644
--- a/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationService.java
+++ b/core/src/main/java/net/hydromatic/optiq/materialize/MaterializationService.java
@@ -24,15 +24,19 @@ import net.hydromatic.linq4j.function.Function1;
import net.hydromatic.linq4j.function.Functions;
import net.hydromatic.optiq.*;
+import net.hydromatic.optiq.config.OptiqConnectionProperty;
import net.hydromatic.optiq.impl.clone.CloneSchema;
import net.hydromatic.optiq.impl.java.JavaTypeFactory;
import net.hydromatic.optiq.jdbc.*;
import net.hydromatic.optiq.prepare.Prepare;
+import net.hydromatic.optiq.runtime.Hook;
import org.eigenbase.reltype.RelDataType;
import org.eigenbase.reltype.RelDataTypeImpl;
import org.eigenbase.util.Pair;
+import com.google.common.collect.ImmutableMap;
+
import java.lang.reflect.Type;
import java.util.*;
@@ -61,6 +65,13 @@ public class MaterializationService {
/** Defines a new materialization. Returns its key. */
public MaterializationKey defineMaterialization(final OptiqSchema schema,
String viewSql, List<String> viewSchemaPath, String tableName) {
+ final MaterializationActor.QueryKey queryKey =
+ new MaterializationActor.QueryKey(viewSql, schema, viewSchemaPath);
+ final MaterializationKey existingKey = actor.keyBySql.get(queryKey);
+ if (existingKey != null) {
+ return existingKey;
+ }
+
final OptiqConnection connection =
MetaImpl.connect(schema.root(), null);
final MaterializationKey key = new MaterializationKey();
@@ -71,8 +82,11 @@ public class MaterializationService {
final Pair<String, Table> pair = schema.getTable(tableName, true);
materializedTable = pair == null ? null : pair.right;
if (materializedTable == null) {
+ final ImmutableMap<OptiqConnectionProperty, String> map =
+ ImmutableMap.of(OptiqConnectionProperty.CREATE_MATERIALIZATIONS,
+ "false");
final OptiqPrepare.PrepareResult<Object> prepareResult =
- Schemas.prepare(connection, schema, viewSchemaPath, viewSql);
+ Schemas.prepare(connection, schema, viewSchemaPath, viewSql, map);
rowType = prepareResult.rowType;
final JavaTypeFactory typeFactory = connection.getTypeFactory();
materializedTable =
@@ -112,6 +126,7 @@ public class MaterializationService {
schema.add(tableName, materializedTable);
}
tableEntry = schema.add(tableName, materializedTable);
+ Hook.CREATE_MATERIALIZATION.run(tableName);
} else {
tableEntry = null;
}
@@ -125,6 +140,7 @@ public class MaterializationService {
new MaterializationActor.Materialization(key, schema.root(),
tableEntry, viewSql, rowType);
actor.keyMap.put(materialization.key, materialization);
+ actor.keyBySql.put(queryKey, materialization.key);
return key;
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/OptiqCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqCatalogReader.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqCatalogReader.java
index a74990e..a000b3a 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqCatalogReader.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqCatalogReader.java
@@ -92,7 +92,7 @@ public class OptiqCatalogReader implements Prepare.CatalogReader,
final Table table = pair.getValue();
final String name2 = pair.getKey();
return RelOptTableImpl.create(this, table.getRowType(typeFactory),
- schema.add(name2, table));
+ schema.add(name2, table), null);
}
return null;
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
index ba2ae80..4e6892f 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqMaterializer.java
@@ -110,7 +110,7 @@ class OptiqMaterializer extends OptiqPrepareImpl.OptiqPreparingStmt {
assert table instanceof StarTable;
RelOptTableImpl starRelOptTable =
RelOptTableImpl.create(catalogReader, table.getRowType(typeFactory),
- starTable);
+ starTable, null);
final RelNode rel3 =
RelOptMaterialization.tryUseStar(rel2, starRelOptTable);
if (rel3 != null) {
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
index 4c19268..a34d80c 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
@@ -122,6 +122,8 @@ public class OptiqPrepareImpl implements OptiqPrepare {
JavaRules.ENUMERABLE_ONE_ROW_RULE,
JavaRules.ENUMERABLE_EMPTY_RULE,
JavaRules.ENUMERABLE_TABLE_FUNCTION_RULE,
+ AggregateStarTableRule.INSTANCE,
+ AggregateStarTableRule.INSTANCE2,
TableAccessRule.INSTANCE,
COMMUTE
? CommutativeJoinRule.INSTANCE
@@ -227,9 +229,13 @@ public class OptiqPrepareImpl implements OptiqPrepare {
/** Creates a query planner and initializes it with a default set of
* rules. */
- protected RelOptPlanner createPlanner(OptiqPrepare.Context prepareContext,
+ protected RelOptPlanner createPlanner(
+ final OptiqPrepare.Context prepareContext,
org.eigenbase.relopt.Context externalContext,
RelOptCostFactory costFactory) {
+ if (externalContext == null) {
+ externalContext = Contexts.withConfig(prepareContext.config());
+ }
final VolcanoPlanner planner =
new VolcanoPlanner(costFactory, externalContext);
planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java b/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
index 8f17eb7..310c219 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/Prepare.java
@@ -124,7 +124,7 @@ public abstract class Prepare {
final JavaTypeFactory typeFactory = context.getTypeFactory();
final RelOptTableImpl starRelOptTable =
RelOptTableImpl.create(catalogReader,
- starTable.getTable().getRowType(typeFactory), starTable);
+ starTable.getTable().getRowType(typeFactory), starTable, null);
planner.addLattice(
new RelOptLattice(lattice.getLattice(), starRelOptTable));
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/QueryableRelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/QueryableRelBuilder.java b/core/src/main/java/net/hydromatic/optiq/prepare/QueryableRelBuilder.java
index e31dc3e..d410b92 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/QueryableRelBuilder.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/QueryableRelBuilder.java
@@ -77,7 +77,7 @@ class QueryableRelBuilder<T> implements QueryableFactory<T> {
.add(tableQueryable.tableName, tableQueryable.table);
final RelOptTableImpl relOptTable =
RelOptTableImpl.create(null, table.getRowType(translator.typeFactory),
- tableEntry);
+ tableEntry, null);
if (table instanceof TranslatableTable) {
return ((TranslatableTable) table).toRel(translator, relOptTable);
} else {
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
index 254565b..d5f47aa 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
@@ -19,6 +19,7 @@ package net.hydromatic.optiq.prepare;
import net.hydromatic.linq4j.expressions.Expression;
import net.hydromatic.optiq.QueryableTable;
+import net.hydromatic.optiq.Schemas;
import net.hydromatic.optiq.Table;
import net.hydromatic.optiq.TranslatableTable;
import net.hydromatic.optiq.jdbc.OptiqSchema;
@@ -32,6 +33,7 @@ import org.eigenbase.relopt.RelOptSchema;
import org.eigenbase.reltype.RelDataType;
import org.eigenbase.sql.SqlAccessType;
import org.eigenbase.sql.validate.SqlMonotonicity;
+import org.eigenbase.util.Util;
import com.google.common.base.Function;
import com.google.common.base.Functions;
@@ -52,17 +54,28 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
private final Function<Class, Expression> expressionFunction;
private final ImmutableList<String> names;
+ /** Estimate for the row count, or null.
+ *
+ * <p>If not null, overrides the estimate from the actual table.
+ *
+ * <p>Useful when a table that contains a materialized query result is being
+ * used to replace a query expression that wildly underestimates the row
+ * count. Now the materialized table can tell the same lie. */
+ private final Double rowCount;
+
private RelOptTableImpl(
RelOptSchema schema,
RelDataType rowType,
List<String> names,
Table table,
- Function<Class, Expression> expressionFunction) {
+ Function<Class, Expression> expressionFunction,
+ Double rowCount) {
this.schema = schema;
this.rowType = rowType;
this.names = ImmutableList.copyOf(names);
this.table = table; // may be null
this.expressionFunction = expressionFunction;
+ this.rowCount = rowCount;
assert expressionFunction != null;
assert rowType != null;
}
@@ -76,13 +89,11 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
final Function<Class, Expression> expressionFunction =
(Function) Functions.constant(expression);
return new RelOptTableImpl(schema, rowType, names, null,
- expressionFunction);
+ expressionFunction, null);
}
- public static RelOptTableImpl create(
- RelOptSchema schema,
- RelDataType rowType,
- final OptiqSchema.TableEntry tableEntry) {
+ public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType,
+ final OptiqSchema.TableEntry tableEntry, Double rowCount) {
Function<Class, Expression> expressionFunction;
if (tableEntry.getTable() instanceof QueryableTable) {
final QueryableTable table = (QueryableTable) tableEntry.getTable();
@@ -100,7 +111,7 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
};
}
return new RelOptTableImpl(schema, rowType, tableEntry.path(),
- tableEntry.getTable(), expressionFunction);
+ tableEntry.getTable(), expressionFunction, rowCount);
}
public static RelOptTableImpl create(
@@ -114,7 +125,7 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
}
};
return new RelOptTableImpl(schema, rowType, ImmutableList.<String>of(),
- table, expressionFunction);
+ table, expressionFunction, null);
}
public <T> T unwrap(Class<T> clazz) {
@@ -124,6 +135,11 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
if (clazz.isInstance(table)) {
return clazz.cast(table);
}
+ if (clazz == OptiqSchema.class) {
+ return clazz.cast(
+ Schemas.subSchema(((OptiqCatalogReader) schema).rootSchema,
+ Util.skipLast(getQualifiedName())));
+ }
return null;
}
@@ -132,6 +148,9 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
}
public double getRowCount() {
+ if (rowCount != null) {
+ return rowCount;
+ }
if (table != null) {
final Double rowCount = table.getStatistic().getRowCount();
if (rowCount != null) {
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
index 16ebfcd..35d3f9f 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
@@ -1281,7 +1281,9 @@ public class JavaRules {
Expressions.call(accumulatorInitializer, "apply"),
accumulatorAdder,
resultSelector))));
- } else if (aggCalls.isEmpty()) {
+ } else if (aggCalls.isEmpty()
+ && groupSet.equals(
+ BitSets.range(child.getRowType().getFieldCount()))) {
builder.add(
Expressions.return_(
null,
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/net/hydromatic/optiq/runtime/Hook.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/runtime/Hook.java b/core/src/main/java/net/hydromatic/optiq/runtime/Hook.java
index c8c7a80..b82aa3d 100644
--- a/core/src/main/java/net/hydromatic/optiq/runtime/Hook.java
+++ b/core/src/main/java/net/hydromatic/optiq/runtime/Hook.java
@@ -56,6 +56,9 @@ public enum Hook {
/** Called to create a Program to optimize the statement. */
PROGRAM,
+ /** Called when materialization is created. */
+ CREATE_MATERIALIZATION,
+
/** Called with a query that has been generated to send to a back-end system.
* The query might be a SQL string (for the JDBC adapter), a list of Mongo
* pipeline expressions (for the MongoDB adapter), et cetera. */
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/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 945334c..cfa2cdb 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
@@ -89,6 +89,8 @@ public class Programs {
OptiqPrepareImpl.COMMUTE
? CommutativeJoinRule.INSTANCE
: MergeProjectRule.INSTANCE,
+ AggregateStarTableRule.INSTANCE,
+ AggregateStarTableRule.INSTANCE2,
PushFilterPastProjectRule.INSTANCE,
PushFilterPastJoinRule.FILTER_ON_JOIN,
RemoveDistinctAggregateRule.INSTANCE,
@@ -150,14 +152,14 @@ public class Programs {
final HepPlanner hepPlanner = new HepPlanner(hepProgram,
null, noDag, null, RelOptCostImpl.FACTORY);
+ List<RelMetadataProvider> list = Lists.newArrayList();
if (metadataProvider != null) {
- List<RelMetadataProvider> list = Lists.newArrayList();
list.add(metadataProvider);
- hepPlanner.registerMetadataProviders(list);
- RelMetadataProvider plannerChain =
- ChainedRelMetadataProvider.of(list);
- rel.getCluster().setMetadataProvider(plannerChain);
}
+ hepPlanner.registerMetadataProviders(list);
+ RelMetadataProvider plannerChain =
+ ChainedRelMetadataProvider.of(list);
+ rel.getCluster().setMetadataProvider(plannerChain);
hepPlanner.setRoot(rel);
return hepPlanner.findBestExp();
@@ -186,13 +188,14 @@ public class Programs {
.addMatchOrder(HepMatchOrder.BOTTOM_UP)
.addRuleInstance(ConvertMultiJoinRule.INSTANCE)
.build();
- final Program program1 = of(hep, false, null);
+ final Program program1 =
+ of(hep, false, new DefaultRelMetadataProvider());
// Create a program that contains a rule to expand a MultiJoinRel
// into heuristically ordered joins.
// We use the rule set passed in, but remove SwapJoinRule and
// PushJoinThroughJoinRule, because they cause exhaustive search.
- final List<RelOptRule> list = new ArrayList<RelOptRule>(rules);
+ final List<RelOptRule> list = Lists.newArrayList(rules);
list.removeAll(
ImmutableList.of(SwapJoinRule.INSTANCE,
CommutativeJoinRule.INSTANCE,
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/rel/AggregateCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/AggregateCall.java b/core/src/main/java/org/eigenbase/rel/AggregateCall.java
index 1732548..93a69ef 100644
--- a/core/src/main/java/org/eigenbase/rel/AggregateCall.java
+++ b/core/src/main/java/org/eigenbase/rel/AggregateCall.java
@@ -178,6 +178,16 @@ public class AggregateCall {
}
/**
+ * Creates an equivalent AggregateCall with new argument ordinals.
+ *
+ * @param args Arguments
+ * @return AggregateCall that suits new inputs and GROUP BY columns
+ */
+ public AggregateCall copy(List<Integer> args) {
+ return new AggregateCall(aggregation, distinct, args, type, name);
+ }
+
+ /**
* Creates equivalent AggregateCall that is adapted to a new input types
* and/or number of columns in GROUP BY.
*
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/rel/rules/AggregateProjectMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/AggregateProjectMergeRule.java b/core/src/main/java/org/eigenbase/rel/rules/AggregateProjectMergeRule.java
new file mode 100644
index 0000000..c97a8c9
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/rules/AggregateProjectMergeRule.java
@@ -0,0 +1,123 @@
+/*
+ * 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.eigenbase.rel.rules;
+
+import java.util.BitSet;
+import java.util.List;
+
+import org.eigenbase.rel.AggregateCall;
+import org.eigenbase.rel.AggregateRelBase;
+import org.eigenbase.rel.ProjectRelBase;
+import org.eigenbase.rel.RelFactories;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelOptUtil;
+import org.eigenbase.rex.RexInputRef;
+import org.eigenbase.rex.RexNode;
+
+import net.hydromatic.optiq.util.BitSets;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * Planner rule that recognizes a {@link org.eigenbase.rel.AggregateRelBase}
+ * on top of a {@link org.eigenbase.rel.ProjectRelBase} and if possible
+ * aggregate through the project or removes the project.
+ *
+ * <p>This is only possible when the grouping expressions and arguments to
+ * the aggregate functions are field references (i.e. not expressions).
+ *
+ * <p>In some cases, this rule has the effect of trimming: the aggregate will
+ * use fewer columns than the project did.
+ */
+public class AggregateProjectMergeRule extends RelOptRule {
+ public static final AggregateProjectMergeRule INSTANCE =
+ new AggregateProjectMergeRule();
+
+ /** Private constructor. */
+ private AggregateProjectMergeRule() {
+ super(
+ operand(AggregateRelBase.class,
+ operand(ProjectRelBase.class, any())));
+ }
+
+ public void onMatch(RelOptRuleCall call) {
+ final AggregateRelBase aggregate = call.rel(0);
+ final ProjectRelBase project = call.rel(1);
+ RelNode x = apply(aggregate, project);
+ if (x != null) {
+ call.transformTo(x);
+ }
+ }
+
+ public static RelNode apply(AggregateRelBase aggregate,
+ ProjectRelBase project) {
+ final List<Integer> newKeys = Lists.newArrayList();
+ for (int key : BitSets.toIter(aggregate.getGroupSet())) {
+ final RexNode rex = project.getProjects().get(key);
+ if (rex instanceof RexInputRef) {
+ newKeys.add(((RexInputRef) rex).getIndex());
+ } else {
+ // Cannot handle "GROUP BY expression"
+ return null;
+ }
+ }
+
+ final ImmutableList.Builder<AggregateCall> aggCalls =
+ ImmutableList.builder();
+ for (AggregateCall aggregateCall : aggregate.getAggCallList()) {
+ final ImmutableList.Builder<Integer> newArgs = ImmutableList.builder();
+ for (int arg : aggregateCall.getArgList()) {
+ final RexNode rex = project.getProjects().get(arg);
+ if (rex instanceof RexInputRef) {
+ newArgs.add(((RexInputRef) rex).getIndex());
+ } else {
+ // Cannot handle "AGG(expression)"
+ return null;
+ }
+ }
+ aggCalls.add(aggregateCall.copy(newArgs.build()));
+ }
+
+ final BitSet newGroupSet = BitSets.of(newKeys);
+ final AggregateRelBase newAggregate =
+ aggregate.copy(aggregate.getTraitSet(), project.getChild(), newGroupSet,
+ aggCalls.build());
+
+ // Add a project if the group set is not in the same order or
+ // contains duplicates.
+ RelNode rel = newAggregate;
+ if (!BitSets.toList(newGroupSet).equals(newKeys)) {
+ final List<Integer> posList = Lists.newArrayList();
+ for (int newKey : newKeys) {
+ posList.add(BitSets.toList(newGroupSet).indexOf(newKey));
+ }
+ for (int i = newAggregate.getGroupSet().cardinality();
+ i < newAggregate.getRowType().getFieldCount(); i++) {
+ posList.add(i);
+ }
+ rel = RelOptUtil.createProject(RelFactories.DEFAULT_PROJECT_FACTORY,
+ rel, posList);
+ }
+
+ return rel;
+ }
+}
+
+// End AggregateProjectMergeRule.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/rel/rules/AggregateStarTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/eigenbase/rel/rules/AggregateStarTableRule.java
new file mode 100644
index 0000000..2edadfe
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/rules/AggregateStarTableRule.java
@@ -0,0 +1,115 @@
+/*
+ * 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.eigenbase.rel.rules;
+
+import org.eigenbase.rel.AggregateRelBase;
+import org.eigenbase.rel.ProjectRelBase;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptLattice;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelOptRuleOperand;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelOptUtil;
+
+import net.hydromatic.optiq.impl.StarTable;
+import net.hydromatic.optiq.jdbc.OptiqSchema;
+import net.hydromatic.optiq.prepare.RelOptTableImpl;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Planner rule that matches an {@link org.eigenbase.rel.AggregateRelBase} on
+ * top of a {@link net.hydromatic.optiq.impl.StarTable.StarTableScan}.
+ *
+ * <p>This pattern indicates that an aggregate table may exist. The rule asks
+ * the star table for an aggregate table at the required level of aggregation.
+ */
+public class AggregateStarTableRule extends RelOptRule {
+ public static final AggregateStarTableRule INSTANCE =
+ new AggregateStarTableRule(
+ operand(AggregateRelBase.class,
+ some(operand(StarTable.StarTableScan.class, none()))),
+ "AggregateStarTableRule");
+
+ public static final AggregateStarTableRule INSTANCE2 =
+ new AggregateStarTableRule(
+ operand(AggregateRelBase.class,
+ operand(ProjectRelBase.class,
+ operand(StarTable.StarTableScan.class, none()))),
+ "AggregateStarTableRule:project") {
+ @Override
+ public void onMatch(RelOptRuleCall call) {
+ final AggregateRelBase aggregate = call.rel(0);
+ final ProjectRelBase project = call.rel(1);
+ final StarTable.StarTableScan scan = call.rel(2);
+ final RelNode rel =
+ AggregateProjectMergeRule.apply(aggregate, project);
+ final AggregateRelBase aggregate2;
+ final ProjectRelBase project2;
+ if (rel instanceof AggregateRelBase) {
+ project2 = null;
+ aggregate2 = (AggregateRelBase) rel;
+ } else if (rel instanceof ProjectRelBase) {
+ project2 = (ProjectRelBase) rel;
+ aggregate2 = (AggregateRelBase) project2.getChild();
+ } else {
+ return;
+ }
+ apply(call, project2, aggregate2, scan);
+ }
+ };
+
+ private AggregateStarTableRule(RelOptRuleOperand operand,
+ String description) {
+ super(operand, description);
+ }
+
+ @Override
+ public void onMatch(RelOptRuleCall call) {
+ final AggregateRelBase aggregate = call.rel(0);
+ final StarTable.StarTableScan scan = call.rel(1);
+ apply(call, null, aggregate, scan);
+ }
+
+ protected void apply(RelOptRuleCall call, ProjectRelBase postProject,
+ AggregateRelBase aggregate, StarTable.StarTableScan scan) {
+ final RelOptCluster cluster = scan.getCluster();
+ final RelOptTable table = scan.getTable();
+ final RelOptLattice lattice = call.getPlanner().getLattice(table);
+ OptiqSchema.TableEntry aggregateTable =
+ lattice.getAggregate(call.getPlanner(), aggregate.getGroupSet(),
+ aggregate.getAggCallList());
+ if (aggregateTable == null) {
+ return;
+ }
+ System.out.println(aggregateTable);
+ final double rowCount = aggregate.getRows();
+ final RelOptTable aggregateRelOptTable =
+ RelOptTableImpl.create(table.getRelOptSchema(),
+ aggregateTable.getTable().getRowType(cluster.getTypeFactory()),
+ aggregateTable, rowCount);
+ RelNode rel = aggregateRelOptTable.toRel(RelOptUtil.getContext(cluster));
+ if (postProject != null) {
+ rel = postProject.copy(postProject.getTraitSet(), ImmutableList.of(rel));
+ }
+ call.transformTo(rel);
+ }
+}
+
+// End AggregateStarTableRule.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/rel/rules/PushAggregateThroughUnionRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/PushAggregateThroughUnionRule.java b/core/src/main/java/org/eigenbase/rel/rules/PushAggregateThroughUnionRule.java
index a225a77..a48fb40 100644
--- a/core/src/main/java/org/eigenbase/rel/rules/PushAggregateThroughUnionRule.java
+++ b/core/src/main/java/org/eigenbase/rel/rules/PushAggregateThroughUnionRule.java
@@ -151,7 +151,7 @@ public class PushAggregateThroughUnionRule extends RelOptRule {
SqlAggFunction af = (SqlAggFunction) aggFun;
final AggregateRelBase.AggCallBinding binding =
new AggregateRelBase.AggCallBinding(typeFactory, af,
- Collections.singletonList(origCall.getType()),
+ Collections.singletonList(origCall.getType()),
nGroupCols);
// count(any) is always not null, however nullability of sum might
// depend on the number of columns in GROUP BY.
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/AbstractRelOptPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/AbstractRelOptPlanner.java b/core/src/main/java/org/eigenbase/relopt/AbstractRelOptPlanner.java
index 0a76545..9823b74 100644
--- a/core/src/main/java/org/eigenbase/relopt/AbstractRelOptPlanner.java
+++ b/core/src/main/java/org/eigenbase/relopt/AbstractRelOptPlanner.java
@@ -63,7 +63,8 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner {
private final Set<RelTrait> traits = new HashSet<RelTrait>();
- private final Context context;
+ /** External context. Never null. */
+ protected final Context context;
private Executor executor;
@@ -76,6 +77,9 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner {
Context context) {
assert costFactory != null;
this.costFactory = costFactory;
+ if (context == null) {
+ context = Contexts.empty();
+ }
this.context = context;
// In case no one calls setCancelFlag, set up a
@@ -190,6 +194,19 @@ public abstract class AbstractRelOptPlanner implements RelOptPlanner {
return this;
}
+ public void addMaterialization(RelOptMaterialization materialization) {
+ // ignore - this planner does not support materializations
+ }
+
+ public void addLattice(RelOptLattice lattice) {
+ // ignore - this planner does not support lattices
+ }
+
+ public RelOptLattice getLattice(RelOptTable table) {
+ // this planner does not support lattices
+ return null;
+ }
+
// implement RelOptPlanner
public void registerSchema(RelOptSchema schema) {
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/Contexts.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/Contexts.java b/core/src/main/java/org/eigenbase/relopt/Contexts.java
new file mode 100644
index 0000000..54dddaa
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/relopt/Contexts.java
@@ -0,0 +1,65 @@
+/*
+ * 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.eigenbase.relopt;
+
+import net.hydromatic.optiq.config.OptiqConnectionConfig;
+
+/**
+ * Utilities for {@link Context}.
+ */
+public class Contexts {
+ public static final EmptyContext EMPTY_CONTEXT = new EmptyContext();
+
+ private Contexts() {}
+
+ /** Returns a context that contains a
+ * {@link net.hydromatic.optiq.config.OptiqConnectionConfig}. */
+ public static Context withConfig(OptiqConnectionConfig config) {
+ return new ConfigContext(config);
+ }
+
+ /** Returns a context that returns null for all inquiries. */
+ public static Context empty() {
+ return EMPTY_CONTEXT;
+ }
+
+ /** Context that contains a
+ * {@link net.hydromatic.optiq.config.OptiqConnectionConfig}. */
+ private static class ConfigContext implements Context {
+ private OptiqConnectionConfig config;
+
+ public ConfigContext(OptiqConnectionConfig config) {
+ this.config = config;
+ }
+
+ public <T> T unwrap(Class<T> clazz) {
+ if (clazz.isInstance(config)) {
+ return clazz.cast(config);
+ }
+ return null;
+ }
+ }
+
+ /** Empty context. */
+ static class EmptyContext implements Context {
+ public <T> T unwrap(Class<T> clazz) {
+ return null;
+ }
+ }
+}
+
+// End Contexts.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/RelOptLattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/RelOptLattice.java b/core/src/main/java/org/eigenbase/relopt/RelOptLattice.java
index b85234e..a41202b 100644
--- a/core/src/main/java/org/eigenbase/relopt/RelOptLattice.java
+++ b/core/src/main/java/org/eigenbase/relopt/RelOptLattice.java
@@ -16,11 +16,22 @@
*/
package org.eigenbase.relopt;
+import java.util.BitSet;
import java.util.List;
+import org.eigenbase.rel.AggregateCall;
import org.eigenbase.rel.RelNode;
+import org.eigenbase.reltype.RelDataTypeField;
+import org.eigenbase.sql.SqlDialect;
+import org.eigenbase.util.mapping.IntPair;
+import net.hydromatic.optiq.config.OptiqConnectionConfig;
+import net.hydromatic.optiq.jdbc.OptiqSchema;
import net.hydromatic.optiq.materialize.Lattice;
+import net.hydromatic.optiq.materialize.MaterializationKey;
+import net.hydromatic.optiq.materialize.MaterializationService;
+import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
+import net.hydromatic.optiq.util.BitSets;
import com.google.common.collect.Lists;
@@ -29,9 +40,7 @@ import com.google.common.collect.Lists;
*/
public class RelOptLattice {
private final Lattice lattice;
- private final RelOptTable starRelOptTable;
- private final List<RelOptMaterialization> materializations =
- Lists.newArrayList();
+ public final RelOptTable starRelOptTable;
public RelOptLattice(Lattice lattice, RelOptTable starRelOptTable) {
this.lattice = lattice;
@@ -52,6 +61,101 @@ public class RelOptLattice {
public RelNode rewrite(RelNode node) {
return RelOptMaterialization.tryUseStar(node, starRelOptTable);
}
+
+ /** Retrieves a materialized table that will satisfy an aggregate query on
+ * the star table.
+ *
+ * <p>The current implementation creates a materialization and populates it.
+ *
+ * <p>Future implementations might return materializations at a different
+ * level of aggregation, from which the desired result can be obtained by
+ * rolling up.
+ *
+ * @param planner Current planner
+ * @param groupSet Grouping key
+ * @param aggCallList Aggregate functions
+ * @return Materialized table
+ */
+ public OptiqSchema.TableEntry getAggregate(RelOptPlanner planner,
+ BitSet groupSet, List<AggregateCall> aggCallList) {
+ final OptiqConnectionConfig config =
+ planner.getContext().unwrap(OptiqConnectionConfig.class);
+ if (config == null || !config.createMaterializations()) {
+ return null;
+ }
+ String sql = sql(starRelOptTable, groupSet, aggCallList);
+ final MaterializationService service = MaterializationService.instance();
+ final OptiqSchema schema = starRelOptTable.unwrap(OptiqSchema.class);
+ final MaterializationKey materializationKey = service
+ .defineMaterialization(schema, sql, schema.path(null), "m" + groupSet);
+ return service.checkValid(materializationKey);
+ }
+
+ private String sql(RelOptTable starRelOptTable, BitSet groupSet,
+ List<AggregateCall> aggCallList) {
+ BitSet columns = (BitSet) groupSet.clone();
+ for (AggregateCall call : aggCallList) {
+ for (int arg : call.getArgList()) {
+ columns.set(arg);
+ }
+ }
+ // Figure out which nodes are needed. Use a node if its columns are used
+ // or if has a child whose columns are used.
+ List<Lattice.Node> usedNodes = Lists.newArrayList();
+ for (Lattice.Node node : lattice.nodes) {
+ if (BitSets.range(node.startCol, node.endCol).intersects(columns)) {
+ use(usedNodes, node);
+ }
+ }
+ final SqlDialect dialect = SqlDialect.DatabaseProduct.OPTIQ.getDialect();
+ final StringBuilder buf = new StringBuilder();
+ buf.append("SELECT DISTINCT ");
+ int k = 0;
+ for (int i : BitSets.toIter(groupSet)) {
+ final RelDataTypeField field =
+ starRelOptTable.getRowType().getFieldList().get(i);
+ if (k++ > 0) {
+ buf.append(", ");
+ }
+ dialect.quoteIdentifier(buf, field.getName());
+ }
+ buf.append("\nFROM ");
+ for (Lattice.Node node : usedNodes) {
+ if (node.parent != null) {
+ buf.append("\nJOIN ");
+ }
+ dialect.quoteIdentifier(buf, node.scan.getTable().getQualifiedName());
+ buf.append(" AS ");
+ dialect.quoteIdentifier(buf, node.alias);
+ if (node.parent != null) {
+ buf.append(" ON ");
+ k = 0;
+ for (IntPair pair : node.link) {
+ if (k++ > 0) {
+ buf.append(" AND ");
+ }
+ dialect.quoteIdentifier(buf,
+ lattice.getColumn(node.parent.startCol + pair.source));
+ buf.append(" = ");
+ dialect.quoteIdentifier(buf,
+ lattice.getColumn(node.startCol + pair.target));
+ }
+ }
+ }
+ if (OptiqPrepareImpl.DEBUG) {
+ System.out.println("Lattice SQL:\n" + buf);
+ }
+ return buf.toString();
+ }
+
+ private void use(List<Lattice.Node> usedNodes, Lattice.Node node) {
+ if (!usedNodes.contains(node)) {
+ if (node.parent != null) {
+ use(usedNodes, node.parent);
+ }
+ usedNodes.add(node);
+ }
+ }
}
// End RelOptLattice.java
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/RelOptMaterialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/RelOptMaterialization.java b/core/src/main/java/org/eigenbase/relopt/RelOptMaterialization.java
index 74939b6..c3bf2a2 100644
--- a/core/src/main/java/org/eigenbase/relopt/RelOptMaterialization.java
+++ b/core/src/main/java/org/eigenbase/relopt/RelOptMaterialization.java
@@ -17,15 +17,20 @@
package org.eigenbase.relopt;
import org.eigenbase.rel.*;
+import org.eigenbase.rel.metadata.DefaultRelMetadataProvider;
+import org.eigenbase.rel.rules.MergeProjectRule;
import org.eigenbase.rel.rules.PullUpProjectsAboveJoinRule;
-import org.eigenbase.relopt.hep.HepPlanner;
-import org.eigenbase.relopt.hep.HepProgram;
import org.eigenbase.sql.SqlExplainLevel;
import org.eigenbase.util.Util;
import org.eigenbase.util.mapping.Mappings;
import net.hydromatic.optiq.Table;
import net.hydromatic.optiq.impl.StarTable;
+import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
+import net.hydromatic.optiq.tools.Program;
+import net.hydromatic.optiq.tools.Programs;
+
+import com.google.common.collect.ImmutableList;
/**
* Records that a particular query is materialized by a particular table.
@@ -76,8 +81,10 @@ public class RelOptMaterialization {
starRelOptTable.getRowType().getFieldCount(),
0, 0, relOptTable.getRowType().getFieldCount());
- return RelOptUtil.createProject(
- new TableAccessRel(scan.getCluster(), starRelOptTable),
+ final RelOptCluster cluster = scan.getCluster();
+ final RelNode scan2 =
+ starRelOptTable.toRel(RelOptUtil.getContext(cluster));
+ return RelOptUtil.createProject(scan2,
Mappings.asList(mapping.inverse()));
}
return scan;
@@ -182,21 +189,24 @@ public class RelOptMaterialization {
* as close to leaves as possible.
*/
public static RelNode toLeafJoinForm(RelNode rel) {
- HepProgram program = HepProgram.builder()
- .addRuleInstance(PullUpProjectsAboveJoinRule.RIGHT_PROJECT)
- .addRuleInstance(PullUpProjectsAboveJoinRule.LEFT_PROJECT)
- .build();
- final HepPlanner planner =
- new HepPlanner(program, //
- rel.getCluster().getPlanner().getContext());
- planner.setRoot(rel);
- System.out.println(
- RelOptUtil.dumpPlan(
- "before", rel, false, SqlExplainLevel.DIGEST_ATTRIBUTES));
- final RelNode rel2 = planner.findBestExp();
- System.out.println(
- RelOptUtil.dumpPlan(
- "after", rel2, false, SqlExplainLevel.DIGEST_ATTRIBUTES));
+ final Program program = Programs.hep(
+ ImmutableList.of(
+ PullUpProjectsAboveJoinRule.RIGHT_PROJECT,
+ PullUpProjectsAboveJoinRule.LEFT_PROJECT,
+ MergeProjectRule.INSTANCE),
+ false,
+ new DefaultRelMetadataProvider());
+ if (OptiqPrepareImpl.DEBUG) {
+ System.out.println(
+ RelOptUtil.dumpPlan(
+ "before", rel, false, SqlExplainLevel.DIGEST_ATTRIBUTES));
+ }
+ final RelNode rel2 = program.run(null, rel, null);
+ if (OptiqPrepareImpl.DEBUG) {
+ System.out.println(
+ RelOptUtil.dumpPlan(
+ "after", rel2, false, SqlExplainLevel.DIGEST_ATTRIBUTES));
+ }
return rel2;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/RelOptPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/RelOptPlanner.java b/core/src/main/java/org/eigenbase/relopt/RelOptPlanner.java
index 4c9f3c8..5f0048a 100644
--- a/core/src/main/java/org/eigenbase/relopt/RelOptPlanner.java
+++ b/core/src/main/java/org/eigenbase/relopt/RelOptPlanner.java
@@ -100,7 +100,8 @@ public interface RelOptPlanner {
/**
* Provides the Context created when this planner was constructed.
*
- * @return Either null or an externally defined context.
+ * @return Never null; either an externally defined context, or a dummy
+ * context that returns null for each requested interface
*/
Context getContext();
@@ -164,6 +165,11 @@ public interface RelOptPlanner {
void addLattice(RelOptLattice lattice);
/**
+ * Retrieves a lattice, given its star table.
+ */
+ RelOptLattice getLattice(RelOptTable table);
+
+ /**
* Finds the most efficient expression to implement this query.
*
* @throws CannotPlanException if cannot find a plan
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/SubstitutionVisitor.java b/core/src/main/java/org/eigenbase/relopt/SubstitutionVisitor.java
index 9dfd5c6..adac55a 100644
--- a/core/src/main/java/org/eigenbase/relopt/SubstitutionVisitor.java
+++ b/core/src/main/java/org/eigenbase/relopt/SubstitutionVisitor.java
@@ -1120,9 +1120,7 @@ public class SubstitutionVisitor {
return Lists.transform(aggCallList,
new Function<AggregateCall, AggregateCall>() {
public AggregateCall apply(AggregateCall call) {
- return new AggregateCall(call.getAggregation(), call.isDistinct(),
- Mappings.apply2(mapping, call.getArgList()), call.getType(),
- call.name);
+ return call.copy(Mappings.apply2(mapping, call.getArgList()));
}
});
}
@@ -1791,9 +1789,7 @@ public class SubstitutionVisitor {
}
}
- /** Based on
- * {@link RemoveTrivialProjectRule#strip(org.eigenbase.rel.ProjectRelBase)}.
- */
+ /** Based on {@link RemoveTrivialProjectRule#strip}. */
public static MutableRel strip(MutableProject project) {
return isTrivial(project) ? project.getChild() : project;
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/hep/HepPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/hep/HepPlanner.java b/core/src/main/java/org/eigenbase/relopt/hep/HepPlanner.java
index 05b09c2..ab94fe7 100644
--- a/core/src/main/java/org/eigenbase/relopt/hep/HepPlanner.java
+++ b/core/src/main/java/org/eigenbase/relopt/hep/HepPlanner.java
@@ -128,14 +128,6 @@ public class HepPlanner extends AbstractRelOptPlanner {
return root;
}
- public void addMaterialization(RelOptMaterialization materialization) {
- // ignore - this planner does not support materializations
- }
-
- public void addLattice(RelOptLattice lattice) {
- // ignore - this planner does not support lattices
- }
-
// implement RelOptPlanner
public boolean addRule(RelOptRule rule) {
boolean added = allRules.add(rule);
@@ -933,15 +925,9 @@ public class HepPlanner extends AbstractRelOptPlanner {
assertNoCycles();
- Iterator<HepRelVertex> bfsIter =
- new BreadthFirstIterator<HepRelVertex, DefaultEdge>(
- graph,
- root);
-
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
sb.append("\nBreadth-first from root: {\n");
- while (bfsIter.hasNext()) {
- HepRelVertex vertex = bfsIter.next();
+ for (HepRelVertex vertex : BreadthFirstIterator.of(graph, root)) {
sb.append(" ")
.append(vertex)
.append(" = ");
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/relopt/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/relopt/volcano/VolcanoPlanner.java b/core/src/main/java/org/eigenbase/relopt/volcano/VolcanoPlanner.java
index e8b740b..23f37c0 100644
--- a/core/src/main/java/org/eigenbase/relopt/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/eigenbase/relopt/volcano/VolcanoPlanner.java
@@ -33,12 +33,15 @@ import org.eigenbase.util.*;
import net.hydromatic.linq4j.expressions.Expressions;
+import net.hydromatic.optiq.config.OptiqConnectionConfig;
import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
import net.hydromatic.optiq.runtime.Hook;
import net.hydromatic.optiq.runtime.Spaces;
import net.hydromatic.optiq.util.graph.*;
import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.*;
import static org.eigenbase.util.Stacks.*;
@@ -190,7 +193,9 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
private final List<RelOptMaterialization> materializations =
Lists.newArrayList();
- private final List<RelOptLattice> lattices = Lists.newArrayList();
+ /** Map of lattices by the qualified name of their star table. */
+ private final Map<List<String>, RelOptLattice> latticeByName =
+ Maps.newLinkedHashMap();
final Map<RelNode, Provenance> provenanceMap =
new HashMap<RelNode, Provenance>();
@@ -268,12 +273,17 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
return root;
}
- public void addMaterialization(RelOptMaterialization materialization) {
+ @Override public void addMaterialization(
+ RelOptMaterialization materialization) {
materializations.add(materialization);
}
- public void addLattice(RelOptLattice lattice) {
- lattices.add(lattice);
+ @Override public void addLattice(RelOptLattice lattice) {
+ latticeByName.put(lattice.starRelOptTable.getQualifiedName(), lattice);
+ }
+
+ @Override public RelOptLattice getLattice(RelOptTable table) {
+ return latticeByName.get(table.getQualifiedName());
}
private void useLattice(RelOptLattice lattice, RelNode rel) {
@@ -321,8 +331,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
.addRuleInstance(MergeProjectRule.INSTANCE)
.build();
- final HepPlanner hepPlanner = new HepPlanner(program, //
- getContext());
+ final HepPlanner hepPlanner = new HepPlanner(program, getContext());
hepPlanner.setRoot(target);
target = hepPlanner.findBestExp();
@@ -334,6 +343,13 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
}
private void useApplicableMaterializations() {
+ // Avoid using materializations while populating materializations!
+ final OptiqConnectionConfig config =
+ context.unwrap(OptiqConnectionConfig.class);
+ if (config == null || !config.materializationsEnabled()) {
+ return;
+ }
+
// Given materializations:
// T = Emps Join Depts
// T2 = T Group by C1
@@ -376,9 +392,16 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
final List<Pair<RelOptLattice, RelNode>> latticeUses = Lists.newArrayList();
final Set<List<String>> queryTableNames =
Sets.newHashSet(Iterables.transform(queryTables, GET_QUALIFIED_NAME));
- for (RelOptLattice lattice : lattices) {
+ // 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(originalRoot);
+ RelNode rel2 = lattice.rewrite(leafJoinRoot.get());
if (rel2 != null) {
latticeUses.add(Pair.of(lattice, rel2));
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rex/RexBuilder.java b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
index 4f569ac..d393a16 100644
--- a/core/src/main/java/org/eigenbase/rex/RexBuilder.java
+++ b/core/src/main/java/org/eigenbase/rex/RexBuilder.java
@@ -258,9 +258,7 @@ public class RexBuilder {
final List<Integer> args = aggCall.getArgList();
final List<Integer> nullableArgs = nullableArgs(args, aggArgTypes);
if (!nullableArgs.equals(args)) {
- aggCall = new AggregateCall(aggCall.getAggregation(),
- aggCall.isDistinct(), nullableArgs,
- aggCall.getType(), aggCall.getName());
+ aggCall = aggCall.copy(nullableArgs);
}
}
RexNode rex = aggCallMapping.get(aggCall);
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/sql/SqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql/SqlDialect.java b/core/src/main/java/org/eigenbase/sql/SqlDialect.java
index db0aac6..5c4d9fc 100644
--- a/core/src/main/java/org/eigenbase/sql/SqlDialect.java
+++ b/core/src/main/java/org/eigenbase/sql/SqlDialect.java
@@ -483,6 +483,7 @@ public class SqlDialect {
INFORMIX("Informix", null),
INGRES("Ingres", null),
LUCIDDB("LucidDB", "\""),
+ OPTIQ("Apache Optiq", "\""),
INTERBASE("Interbase", null),
PHOENIX("Phoenix", "\""),
POSTGRESQL("PostgreSQL", "\""),
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
index b0e3ccd..39cb8b4 100644
--- a/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/eigenbase/sql2rel/RelFieldTrimmer.java
@@ -783,12 +783,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
for (AggregateCall aggCall : aggregate.getAggCallList()) {
if (fieldsUsed.get(j)) {
AggregateCall newAggCall =
- new AggregateCall(
- aggCall.getAggregation(),
- aggCall.isDistinct(),
- Mappings.apply2(inputMapping, aggCall.getArgList()),
- aggCall.getType(),
- aggCall.getName());
+ aggCall.copy(Mappings.apply2(inputMapping, aggCall.getArgList()));
if (newAggCall.equals(aggCall)) {
newAggCall = aggCall; // immutable -> canonize to save space
}
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/main/java/org/eigenbase/util/XmlOutput.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/util/XmlOutput.java b/core/src/main/java/org/eigenbase/util/XmlOutput.java
index d3389e5..cd529e8 100644
--- a/core/src/main/java/org/eigenbase/util/XmlOutput.java
+++ b/core/src/main/java/org/eigenbase/util/XmlOutput.java
@@ -19,6 +19,8 @@ package org.eigenbase.util;
import java.io.*;
import java.util.*;
+import com.google.common.collect.Lists;
+
/**
* Streaming XML output.
*
@@ -612,8 +614,7 @@ public class XmlOutput {
public StringEscaper getMutableClone() {
StringEscaper clone = clone();
if (clone.translationVector == null) {
- clone.translationVector = new ArrayList<String>();
- Collections.addAll(clone.translationVector, clone.translationTable);
+ clone.translationVector = Lists.newArrayList(clone.translationTable);
clone.translationTable = null;
}
return clone;
http://git-wip-us.apache.org/repos/asf/incubator-optiq/blob/c3bf95bb/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
index 9c624d5..471f973 100644
--- a/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
+++ b/core/src/test/java/net/hydromatic/optiq/test/JdbcTest.java
@@ -2902,7 +2902,20 @@ public class JdbcTest {
+ "from \"hr\".\"emps\"\n")
.returnsUnordered(
"deptno=10",
- "deptno=20")
+ "deptno=20");
+ }
+
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/OPTIQ-397">OPTIQ-397</a>,
+ * "SELECT DISTINCT *" gives ClassCastException at runtime". */
+ @Ignore("OPTIQ-397")
+ @Test public void testSelectDistinctStar() {
+ OptiqAssert.that()
+ .with(OptiqAssert.Config.REGULAR)
+ .query(
+ "select distinct *\n"
+ + "from \"hr\".\"emps\"\n")
+ .returnsCount(5)
.planContains(".distinct(");
}
@@ -2931,7 +2944,18 @@ public class JdbcTest {
+ "group by \"deptno\"")
.returnsUnordered(
"deptno=10",
- "deptno=20")
+ "deptno=20");
+ }
+
+ /** Same result (and plan) as {@link #testSelectDistinct}. */
+ @Test public void testGroupByNoAggregatesAllColumns() {
+ OptiqAssert.that()
+ .with(OptiqAssert.Config.REGULAR)
+ .query(
+ "select \"deptno\"\n"
+ + "from \"hr\".\"emps\"\n"
+ + "group by \"deptno\", \"empid\", \"name\", \"salary\", \"commission\"")
+ .returnsCount(4)
.planContains(".distinct(");
}