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 2019/09/24 19:12:23 UTC
[calcite] branch master updated: [CALCITE-3365] Don't require use
of JdbcSchema in QuerySqlStatisticProvider (Lindsey Meyer)
This is an automated email from the ASF dual-hosted git repository.
jhyde pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 3005ff9 [CALCITE-3365] Don't require use of JdbcSchema in QuerySqlStatisticProvider (Lindsey Meyer)
3005ff9 is described below
commit 3005ff97ddb6173ac339b45affb0b4da5d406345
Author: Lindsey Meyer <la...@users.noreply.github.com>
AuthorDate: Thu Sep 19 10:53:29 2019 -0700
[CALCITE-3365] Don't require use of JdbcSchema in QuerySqlStatisticProvider (Lindsey Meyer)
Add a test case (Julian Hyde)
Close apache/calcite#1467
---
.../org/apache/calcite/adapter/jdbc/JdbcTable.java | 10 +++++
.../statistic/QuerySqlStatisticProvider.java | 43 ++++++++++---------
.../calcite/materialize/LatticeSuggesterTest.java | 31 ++++++++++++++
.../org/apache/calcite/test/CalciteAssert.java | 49 ++++++++++++++++++++++
4 files changed, 111 insertions(+), 22 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
index 6285501..c21d8b0 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
@@ -100,6 +100,16 @@ public class JdbcTable extends AbstractQueryableTable
return jdbcTableType;
}
+ @Override public <C> C unwrap(Class<C> aClass) {
+ if (aClass.isInstance(jdbcSchema.getDataSource())) {
+ return aClass.cast(jdbcSchema.getDataSource());
+ } else if (aClass.isInstance(jdbcSchema.dialect)) {
+ return aClass.cast(jdbcSchema.dialect);
+ } else {
+ return super.unwrap(aClass);
+ }
+ }
+
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
if (protoRowType == null) {
try {
diff --git a/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java b/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java
index 2f4af9d..c15426c 100644
--- a/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java
+++ b/core/src/main/java/org/apache/calcite/statistic/QuerySqlStatisticProvider.java
@@ -16,10 +16,8 @@
*/
package org.apache.calcite.statistic;
-import org.apache.calcite.adapter.jdbc.JdbcRules;
-import org.apache.calcite.adapter.jdbc.JdbcSchema;
-import org.apache.calcite.adapter.jdbc.JdbcTable;
import org.apache.calcite.materialize.SqlStatisticProvider;
+import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptSchema;
import org.apache.calcite.plan.RelOptTable;
@@ -82,16 +80,16 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
}
public double tableCardinality(RelOptTable table) {
- final JdbcTable jdbcTable = table.unwrap(JdbcTable.class);
- return withBuilder(jdbcTable.jdbcSchema,
- (cluster, relOptSchema, jdbcSchema, relBuilder) -> {
+ final SqlDialect dialect = table.unwrap(SqlDialect.class);
+ final DataSource dataSource = table.unwrap(DataSource.class);
+ return withBuilder(
+ (cluster, relOptSchema, relBuilder) -> {
// Generate:
// SELECT COUNT(*) FROM `EMP`
relBuilder.push(table.toRel(ViewExpanders.simpleContext(cluster)))
.aggregate(relBuilder.groupKey(), relBuilder.count());
- final String sql = toSql(relBuilder.build(), jdbcSchema.dialect);
- final DataSource dataSource = jdbcSchema.getDataSource();
+ final String sql = toSql(relBuilder.build(), dialect);
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
@@ -111,9 +109,10 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
public boolean isForeignKey(RelOptTable fromTable, List<Integer> fromColumns,
RelOptTable toTable, List<Integer> toColumns) {
- final JdbcTable jdbcTable = fromTable.unwrap(JdbcTable.class);
- return withBuilder(jdbcTable.jdbcSchema,
- (cluster, relOptSchema, jdbcSchema, relBuilder) -> {
+ final SqlDialect dialect = fromTable.unwrap(SqlDialect.class);
+ final DataSource dataSource = fromTable.unwrap(DataSource.class);
+ return withBuilder(
+ (cluster, relOptSchema, relBuilder) -> {
// EMP(DEPTNO) is a foreign key to DEPT(DEPTNO) if the following
// query returns 0:
//
@@ -135,8 +134,7 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
.minus(false, 2)
.aggregate(relBuilder.groupKey(), relBuilder.count());
- final String sql = toSql(relBuilder.build(), jdbcSchema.dialect);
- final DataSource dataSource = jdbcTable.jdbcSchema.getDataSource();
+ final String sql = toSql(relBuilder.build(), dialect);
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
@@ -155,9 +153,10 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
}
public boolean isKey(RelOptTable table, List<Integer> columns) {
- final JdbcTable jdbcTable = table.unwrap(JdbcTable.class);
- return withBuilder(jdbcTable.jdbcSchema,
- (cluster, relOptSchema, jdbcSchema, relBuilder) -> {
+ final SqlDialect dialect = table.unwrap(SqlDialect.class);
+ final DataSource dataSource = table.unwrap(DataSource.class);
+ return withBuilder(
+ (cluster, relOptSchema, relBuilder) -> {
// The collection of columns ['DEPTNO'] is a key for 'EMP' if the
// following query returns no rows:
//
@@ -174,9 +173,8 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
.filter(
relBuilder.call(SqlStdOperatorTable.GREATER_THAN,
Util.last(relBuilder.fields()), relBuilder.literal(1)));
- final String sql = toSql(relBuilder.build(), jdbcSchema.dialect);
+ final String sql = toSql(relBuilder.build(), dialect);
- final DataSource dataSource = jdbcSchema.getDataSource();
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
@@ -201,21 +199,22 @@ public class QuerySqlStatisticProvider implements SqlStatisticProvider {
return sql;
}
- private <R> R withBuilder(JdbcSchema jdbcSchema, BuilderAction<R> action) {
+ private <R> R withBuilder(BuilderAction<R> action) {
return Frameworks.withPlanner(
(cluster, relOptSchema, rootSchema) -> {
final RelBuilder relBuilder =
- JdbcRules.JDBC_BUILDER.create(cluster, relOptSchema);
- return action.apply(cluster, relOptSchema, jdbcSchema, relBuilder);
+ RelBuilder.proto(Contexts.of()).create(cluster, relOptSchema);
+ return action.apply(cluster, relOptSchema, relBuilder);
});
}
/** Performs an action with a {@link RelBuilder}.
*
* @param <R> Result type */
+ @FunctionalInterface
private interface BuilderAction<R> {
R apply(RelOptCluster cluster, RelOptSchema relOptSchema,
- JdbcSchema jdbcSchema, RelBuilder relBuilder);
+ RelBuilder relBuilder);
}
}
diff --git a/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java b/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java
index 8c691f2..c13898b 100644
--- a/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java
+++ b/core/src/test/java/org/apache/calcite/materialize/LatticeSuggesterTest.java
@@ -676,6 +676,37 @@ public class LatticeSuggesterTest {
+ " [foodmart, customer], +($2, 2):+($0, 1))])"));
}
+ /** Tests that we can run the suggester against non-JDBC schemas.
+ *
+ * <p>{@link org.apache.calcite.test.CalciteAssert.SchemaSpec#FAKE_FOODMART}
+ * is not based on {@link org.apache.calcite.adapter.jdbc.JdbcSchema} or
+ * {@link org.apache.calcite.adapter.jdbc.JdbcTable} but can provide a
+ * {@link javax.sql.DataSource}
+ * and {@link SqlDialect} for executing statistics queries.
+ *
+ * <p>The query has a join, and so we have to execute statistics queries
+ * to deduce the direction of the foreign key.
+ */
+ @Test public void testFoodmartSimpleJoin() throws Exception {
+ checkFoodmartSimpleJoin(CalciteAssert.SchemaSpec.JDBC_FOODMART);
+ checkFoodmartSimpleJoin(CalciteAssert.SchemaSpec.FAKE_FOODMART);
+ }
+
+ private void checkFoodmartSimpleJoin(CalciteAssert.SchemaSpec schemaSpec)
+ throws Exception {
+ final FrameworkConfig config = Frameworks.newConfigBuilder()
+ .defaultSchema(Tester.schemaFrom(schemaSpec))
+ .statisticProvider(QuerySqlStatisticProvider.SILENT_CACHING_INSTANCE)
+ .build();
+ final Tester t = new Tester(config);
+ final String q = "select *\n"
+ + "from \"time_by_day\" as \"t\",\n"
+ + " \"sales_fact_1997\" as \"s\"\n"
+ + "where \"s\".\"time_id\" = \"t\".\"time_id\"\n";
+ final String g = "sales_fact_1997 (time_by_day:time_id)";
+ assertThat(t.addQuery(q), isGraphs(g, "[]"));
+ }
+
/** Creates a matcher that matches query graphs to strings. */
private BaseMatcher<List<Lattice>> isGraphs(
String... strings) {
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 201a10e..151102d 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -35,6 +35,8 @@ import org.apache.calcite.model.ModelHandler;
import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.runtime.GeoFunctions;
@@ -42,10 +44,14 @@ import org.apache.calcite.runtime.Hook;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.TableFunction;
+import org.apache.calcite.schema.Wrapper;
import org.apache.calcite.schema.impl.AbstractSchema;
+import org.apache.calcite.schema.impl.AbstractTable;
import org.apache.calcite.schema.impl.TableFunctionImpl;
import org.apache.calcite.schema.impl.ViewTable;
import org.apache.calcite.schema.impl.ViewTableMacro;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.sql.validate.SqlValidatorException;
import org.apache.calcite.tools.FrameworkConfig;
@@ -878,6 +884,48 @@ public class CalciteAssert {
ImmutableList.<String>of(), ImmutableList.of("POST", "TICKER"),
null));
return post;
+ case FAKE_FOODMART:
+ // Similar to FOODMART, but not based on JdbcSchema.
+ // Contains 2 tables that do not extend JdbcTable.
+ // They redirect requests for SqlDialect and DataSource to the real JDBC
+ // FOODMART, and this allows statistics queries to be executed.
+ foodmart = addSchemaIfNotExists(rootSchema, SchemaSpec.JDBC_FOODMART);
+ final Wrapper salesTable = (Wrapper) foodmart.getTable("sales_fact_1997");
+ SchemaPlus fake =
+ rootSchema.add(schema.schemaName, new AbstractSchema());
+ fake.add("time_by_day", new AbstractTable() {
+ public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return typeFactory.builder()
+ .add("time_id", SqlTypeName.INTEGER)
+ .add("the_year", SqlTypeName.INTEGER)
+ .build();
+ }
+
+ public <C> C unwrap(Class<C> aClass) {
+ if (aClass.isAssignableFrom(SqlDialect.class)
+ || aClass.isAssignableFrom(DataSource.class)) {
+ return salesTable.unwrap(aClass);
+ }
+ return super.unwrap(aClass);
+ }
+ });
+ fake.add("sales_fact_1997", new AbstractTable() {
+ public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return typeFactory.builder()
+ .add("time_id", SqlTypeName.INTEGER)
+ .add("customer_id", SqlTypeName.INTEGER)
+ .build();
+ }
+
+ public <C> C unwrap(Class<C> aClass) {
+ if (aClass.isAssignableFrom(SqlDialect.class)
+ || aClass.isAssignableFrom(DataSource.class)) {
+ return salesTable.unwrap(aClass);
+ }
+ return super.unwrap(aClass);
+ }
+ });
+ return fake;
case AUX:
SchemaPlus aux =
rootSchema.add(schema.schemaName, new AbstractSchema());
@@ -1882,6 +1930,7 @@ public class CalciteAssert {
/** Specification for common test schemas. */
public enum SchemaSpec {
REFLECTIVE_FOODMART("foodmart"),
+ FAKE_FOODMART("foodmart"),
JDBC_FOODMART("foodmart"),
CLONE_FOODMART("foodmart2"),
JDBC_FOODMART_WITH_LATTICE("lattice"),