[CALCITE-1966] Allow normal views to act as materialization table (Christian Beikov)
Close apache/calcite#527 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/6d2fc4ec Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/6d2fc4ec Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/6d2fc4ec Branch: refs/heads/branch-1.14 Commit: 6d2fc4ecf1f2640b6ccfc0dc890737503dbc0734 Parents: e152592 Author: Christian Beikov <[email protected]> Authored: Thu Aug 24 16:21:40 2017 +0200 Committer: Julian Hyde <[email protected]> Committed: Tue Aug 29 10:14:31 2017 -0700 ---------------------------------------------------------------------- .../materialize/MaterializationService.java | 6 ++++ .../calcite/plan/RelOptMaterialization.java | 6 ++-- .../calcite/plan/RelOptMaterializations.java | 13 ++++--- .../calcite/prepare/CalcitePrepareImpl.java | 3 +- .../org/apache/calcite/prepare/Prepare.java | 4 ++- .../org/apache/calcite/test/CalciteAssert.java | 25 +++++++++----- .../calcite/test/MaterializationTest.java | 36 +++++++++++++++++--- .../java/org/apache/calcite/util/Smalls.java | 11 ++++++ 8 files changed, 80 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java index 1b9fd88..3625a02 100644 --- a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java +++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java @@ -118,14 +118,20 @@ public class MaterializationService { final CalciteConnection connection = CalciteMetaImpl.connect(schema.root(), null); CalciteSchema.TableEntry tableEntry; + // If the user says the materialization exists, first try to find a table + // with the name and if none can be found, lookup a view in the schema if (existing) { tableEntry = schema.getTable(suggestedTableName, true); + if (tableEntry == null) { + tableEntry = schema.getTableBasedOnNullaryFunction(suggestedTableName, true); + } } else { tableEntry = null; } if (tableEntry == null) { tableEntry = schema.getTableBySql(viewSql); } + RelDataType rowType = null; if (tableEntry == null) { Table table = tableFactory.createTable(schema, viewSql, viewSchemaPath); http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java index b65af48..6e1af24 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java @@ -56,14 +56,14 @@ public class RelOptMaterialization { public final RelNode tableRel; public final RelOptTable starRelOptTable; public final StarTable starTable; - public final RelOptTable table; + public final List<String> qualifiedTableName; public final RelNode queryRel; /** * Creates a RelOptMaterialization. */ public RelOptMaterialization(RelNode tableRel, RelNode queryRel, - RelOptTable starRelOptTable) { + RelOptTable starRelOptTable, List<String> qualifiedTableName) { this.tableRel = RelOptUtil.createCastRel(tableRel, queryRel.getRowType(), false); this.starRelOptTable = starRelOptTable; @@ -73,7 +73,7 @@ public class RelOptMaterialization { this.starTable = starRelOptTable.unwrap(StarTable.class); assert starTable != null; } - this.table = tableRel.getTable(); + this.qualifiedTableName = qualifiedTableName; this.queryRel = queryRel; } http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java index 1e3a9e9..ce0c033 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java +++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java @@ -138,9 +138,9 @@ public abstract class RelOptMaterializations { final Map<List<String>, RelOptMaterialization> qnameMap = new HashMap<>(); for (RelOptMaterialization materialization : materializations) { // If materialization is a tile in a lattice, we will deal with it shortly. - if (materialization.table != null + if (materialization.qualifiedTableName != null && materialization.starTable == null) { - final List<String> qname = materialization.table.getQualifiedName(); + final List<String> qname = materialization.qualifiedTableName; qnameMap.put(qname, materialization); for (RelOptTable usedTable : RelOptUtil.findTables(materialization.queryRel)) { @@ -167,7 +167,7 @@ public abstract class RelOptMaterializations { for (List<String> qname : TopologicalOrderIterator.of(usesGraph)) { RelOptMaterialization materialization = qnameMap.get(qname); if (materialization != null - && usesTable(materialization.table, queryTablesUsed, frozenGraph)) { + && usesTable(materialization.qualifiedTableName, queryTablesUsed, frozenGraph)) { applicableMaterializations.add(materialization); } } @@ -218,13 +218,12 @@ public abstract class RelOptMaterializations { * {@code usedTables}. */ private static boolean usesTable( - RelOptTable table, + List<String> qualifiedName, Set<RelOptTable> usedTables, Graphs.FrozenGraph<List<String>, DefaultEdge> usesGraph) { for (RelOptTable queryTable : usedTables) { - if (usesGraph.getShortestPath( - queryTable.getQualifiedName(), - table.getQualifiedName()) != null) { + if (usesGraph.getShortestPath(queryTable.getQualifiedName(), qualifiedName) + != null) { return true; } } http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java index 72fa04b..17b6e3a 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java @@ -742,7 +742,8 @@ public class CalcitePrepareImpl implements CalcitePrepare { .setQuotedCasing(config.quotedCasing()) .setUnquotedCasing(config.unquotedCasing()) .setQuoting(config.quoting()) - .setConformance(config.conformance()); + .setConformance(config.conformance()) + .setCaseSensitive(config.caseSensitive()); final SqlParserImplFactory parserFactory = config.parserFactory(SqlParserImplFactory.class, null); if (parserFactory != null) { http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/main/java/org/apache/calcite/prepare/Prepare.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java index 29af1ef..8cb09c3 100644 --- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java +++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java @@ -143,10 +143,12 @@ public abstract class Prepare { final List<RelOptMaterialization> materializationList = new ArrayList<>(); for (Materialization materialization : materializations) { + List<String> qualifiedTableName = materialization.materializedTable.path(); materializationList.add( new RelOptMaterialization(materialization.tableRel, materialization.queryRel, - materialization.starRelOptTable)); + materialization.starRelOptTable, + qualifiedTableName)); } final List<RelOptLattice> latticeList = new ArrayList<>(); http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/test/java/org/apache/calcite/test/CalciteAssert.java ---------------------------------------------------------------------- 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 76a464a..490c7e5 100644 --- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java +++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java @@ -925,8 +925,13 @@ public class CalciteAssert { return with("model", "inline:" + model); } - /** Adds materializations to the schema. */ public final AssertThat withMaterializations(String model, + final String... materializations) { + return withMaterializations(model, false, materializations); + } + + /** Adds materializations to the schema. */ + public final AssertThat withMaterializations(String model, final boolean existing, final String... materializations) { return withMaterializations(model, new Function<JsonBuilder, List<Object>>() { @@ -937,7 +942,9 @@ public class CalciteAssert { String table = materializations[i++]; final Map<String, Object> map = builder.map(); map.put("table", table); - map.put("view", table + "v"); + if (!existing) { + map.put("view", table + "v"); + } String sql = materializations[i]; final String sql2 = sql.replaceAll("`", "\""); map.put("sql", sql2); @@ -957,15 +964,17 @@ public class CalciteAssert { "materializations: " + builder.toJsonString(list); final String model2; if (model.contains("defaultSchema: 'foodmart'")) { - model2 = model.replace("]", - ", { name: 'mat', " + int endIndex = model.lastIndexOf(']'); + model2 = model.substring(0, endIndex) + + ", \n{ name: 'mat', " + buf + "}\n" - + "]"); + + "]" + + model.substring(endIndex + 1); } else if (model.contains("type: ")) { - model2 = model.replace("type: ", - buf + ",\n" - + "type: "); + model2 = model.replaceFirst("type: ", + java.util.regex.Matcher.quoteReplacement(buf + ",\n" + + "type: ")); } else { throw new AssertionError("do not know where to splice"); } http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/test/java/org/apache/calcite/test/MaterializationTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java index fb96721..6a3bcc9 100644 --- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java +++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java @@ -197,14 +197,22 @@ public class MaterializationTest { RuleSets.ofList(ImmutableList.<RelOptRule>of())); } + + private void checkMaterialize(String materialize, String query, String model, + Function<ResultSet, Void> explainChecker, final RuleSet rules) { + checkThatMaterialize(materialize, query, "m0", false, model, explainChecker, + rules).sameResultWithMaterializationsDisabled(); + } + /** Checks that a given query can use a materialized view with a given * definition. */ - private void checkMaterialize(String materialize, String query, String model, + private CalciteAssert.AssertQuery checkThatMaterialize(String materialize, + String query, String name, boolean existing, String model, Function<ResultSet, Void> explainChecker, final RuleSet rules) { try (final TryThreadLocal.Memo ignored = Prepare.THREAD_TRIM.push(true)) { MaterializationService.setThreadLocal(); CalciteAssert.AssertQuery that = CalciteAssert.that() - .withMaterializations(model, "m0", materialize) + .withMaterializations(model, existing, name, materialize) .query(query) .enableMaterializations(true); @@ -220,8 +228,7 @@ public class MaterializationTest { }); } - that.explainMatches("", explainChecker) - .sameResultWithMaterializationsDisabled(); + return that.explainMatches("", explainChecker); } } @@ -1797,6 +1804,23 @@ public class MaterializationTest { HR_FKUK_MODEL); } + @Test public void testViewMaterialization() { + checkThatMaterialize( + "select \"depts\".\"name\"\n" + + "from \"emps\"\n" + + "join \"depts\" on (\"emps\".\"deptno\" = \"depts\".\"deptno\")", + "select \"depts\".\"name\"\n" + + "from \"depts\"\n" + + "join \"emps\" on (\"emps\".\"deptno\" = \"depts\".\"deptno\")", + "matview", + true, + HR_FKUK_MODEL, + CalciteAssert.checkResultContains( + "EnumerableValues(tuples=[[{ 'noname' }]])"), + RuleSets.ofList(ImmutableList.<RelOptRule>of())) + .returnsValue("noname"); + } + @Test public void testSubQuery() { String q = "select \"empid\", \"deptno\", \"salary\" from \"emps\" e1\n" + "where \"empid\" = (\n" @@ -2131,6 +2155,10 @@ public class MaterializationTest { public TranslatableTable view(String s) { return Smalls.view(s); } + + public TranslatableTable matview() { + return Smalls.strView("noname"); + } } } http://git-wip-us.apache.org/repos/asf/calcite/blob/6d2fc4ec/core/src/test/java/org/apache/calcite/util/Smalls.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/util/Smalls.java b/core/src/test/java/org/apache/calcite/util/Smalls.java index b3e64ca..39f0263 100644 --- a/core/src/test/java/org/apache/calcite/util/Smalls.java +++ b/core/src/test/java/org/apache/calcite/util/Smalls.java @@ -349,6 +349,17 @@ public class Smalls { }, "values (1), (3), " + s, ImmutableList.<String>of(), Arrays.asList("view")); } + public static TranslatableTable strView(String s) { + return new ViewTable(Object.class, + new RelProtoDataType() { + public RelDataType apply(RelDataTypeFactory typeFactory) { + return typeFactory.builder().add("c", SqlTypeName.VARCHAR, 100) + .build(); + } + }, "values (" + SqlDialect.CALCITE.quoteStringLiteral(s) + ")", + ImmutableList.<String>of(), Arrays.asList("view")); + } + public static TranslatableTable str(Object o, Object p) { assertThat(RexLiteral.validConstant(o, Litmus.THROW), is(true)); assertThat(RexLiteral.validConstant(p, Litmus.THROW), is(true));
