diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/CloneSchema.java b/core/src/main/java/org/apache/calcite/adapter/clone/CloneSchema.java index 65bf217269f..b5779778900 100644 --- a/core/src/main/java/org/apache/calcite/adapter/clone/CloneSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/clone/CloneSchema.java @@ -35,6 +35,8 @@ import org.apache.calcite.schema.Schemas; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.impl.AbstractSchema; +import org.apache.calcite.schema.lookup.LikePattern; +import org.apache.calcite.schema.lookup.Lookup; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcBaseSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcBaseSchema.java new file mode 100644 index 00000000000..5d1614bb159 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcBaseSchema.java @@ -0,0 +1,94 @@ +/* + * 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.apache.calcite.adapter.jdbc; + +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.rel.type.RelProtoDataType; +import org.apache.calcite.schema.Function; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.schema.SchemaVersion; +import org.apache.calcite.schema.Schemas; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.lookup.LikePattern; +import org.apache.calcite.schema.lookup.Lookup; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +/** + * Base class for JDBC schemas. + */ +public abstract class JdbcBaseSchema implements Schema { + + public abstract Lookup tables(); + + + @Deprecated @Override public @Nullable Table getTable(String name) { + return tables().get(name); + } + + @Deprecated @Override public Set getTableNames() { + return tables().getNames(LikePattern.any()); + } + + public abstract Lookup subSchemas(); + + @Deprecated @Override public @Nullable Schema getSubSchema(String name) { + return subSchemas().get(name); + } + + @Deprecated @Override public Set getSubSchemaNames() { + return subSchemas().getNames(LikePattern.any()); + } + + + @Override public @Nullable RelProtoDataType getType(String name) { + return null; + } + + @Override public Set getTypeNames() { + return Collections.emptySet(); + } + + @Override public final Collection getFunctions(String name) { + return Collections.emptyList(); + } + + @Override public final Set getFunctionNames() { + return Collections.emptySet(); + } + + @Override public Expression getExpression(@Nullable final SchemaPlus parentSchema, + final String name) { + requireNonNull(parentSchema, "parentSchema"); + return Schemas.subSchemaExpression(parentSchema, name, getClass()); + } + + @Override public boolean isMutable() { + return false; + } + + @Override public Schema snapshot(final SchemaVersion version) { + return this; + } +} diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java index 9329e2a829e..f40b9c9a011 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcCatalogSchema.java @@ -16,33 +16,31 @@ */ package org.apache.calcite.adapter.jdbc; -import com.google.common.collect.ImmutableSet; - import org.apache.calcite.DataContext; import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.schema.lookup.LikePattern; -import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.Schemas; +import org.apache.calcite.schema.Table; import org.apache.calcite.schema.Wrapper; -import org.apache.calcite.schema.impl.AbstractSchema; -import org.apache.calcite.schema.lookup.CachingLookup; import org.apache.calcite.schema.lookup.IgnoreCaseLookup; +import org.apache.calcite.schema.lookup.LikePattern; +import org.apache.calcite.schema.lookup.LoadingCacheLookup; +import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.sql.SqlDialect; import org.apache.calcite.sql.SqlDialectFactory; import org.apache.calcite.sql.SqlDialectFactoryImpl; import org.apache.calcite.util.BuiltInMethod; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableSet; import org.checkerframework.checker.nullness.qual.Nullable; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Map; import java.util.Set; import java.util.function.Supplier; import javax.sql.DataSource; @@ -57,11 +55,11 @@ * an instance of {@link JdbcSchema}. * *

This schema is lazy: it does not compute the list of schema names until - * the first call to {@link #getSubSchemaMap()}. Then it creates a - * {@link JdbcSchema} for each schema name. Each JdbcSchema will populate its + * the first call to {@link #subSchemas()} and {@link Lookup#get(String)}. Then it creates a + * {@link JdbcSchema} for this schema name. Each JdbcSchema will populate its * tables on demand. */ -public class JdbcCatalogSchema extends AbstractSchema implements Wrapper { +public class JdbcCatalogSchema extends JdbcBaseSchema implements Wrapper { final DataSource dataSource; public final SqlDialect dialect; final JdbcConvention convention; @@ -80,12 +78,11 @@ public JdbcCatalogSchema(DataSource dataSource, SqlDialect dialect, this.dialect = requireNonNull(dialect, "dialect"); this.convention = requireNonNull(convention, "convention"); this.catalog = catalog; - this.subSchemas = new CachingLookup<>(new IgnoreCaseLookup() { - @Override - public @Nullable JdbcSchema get(String name) { + this.subSchemas = new LoadingCacheLookup<>(new IgnoreCaseLookup() { + @Override public @Nullable JdbcSchema get(String name) { try (Connection connection = dataSource.getConnection(); - ResultSet resultSet = - connection.getMetaData().getSchemas(catalog, name)) { + ResultSet resultSet = + connection.getMetaData().getSchemas(catalog, name)) { while (resultSet.next()) { final String schemaName = requireNonNull(resultSet.getString(1), @@ -98,15 +95,15 @@ public JdbcCatalogSchema(DataSource dataSource, SqlDialect dialect, return null; } - @Override - public Set getNames(LikePattern pattern) { + @Override public Set getNames(LikePattern pattern) { final ImmutableSet.Builder builder = ImmutableSet.builder(); try (Connection connection = dataSource.getConnection(); - ResultSet resultSet = - connection.getMetaData().getSchemas(catalog, pattern.pattern)) { + ResultSet resultSet = + connection.getMetaData().getSchemas(catalog, pattern.pattern)) { while (resultSet.next()) { - builder.add(requireNonNull(resultSet.getString(1), + builder.add( + requireNonNull(resultSet.getString(1), "got null schemaName from the database")); } } catch (SQLException e) { @@ -145,6 +142,10 @@ public static JdbcCatalogSchema create( return new JdbcCatalogSchema(dataSource, dialect, convention, catalog); } + @Override public Lookup

tables() { + return Lookup.empty(); + } + @Override public Lookup subSchemas() { return subSchemas; } @@ -157,10 +158,6 @@ private String computeDefaultSchemaName() { } } - @Override protected Map getSubSchemaMap() { - throw new UnsupportedOperationException("getSubSchemaMap"); - } - /** Returns the name of the default sub-schema. */ public @Nullable String getDefaultSubSchemaName() { return defaultSchemaName.get(); diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java index abd89ed8a85..cfebea44316 100644 --- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java +++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java @@ -26,7 +26,13 @@ import org.apache.calcite.rel.type.RelDataTypeImpl; import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.rel.type.RelProtoDataType; -import org.apache.calcite.schema.*; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.SchemaFactory; +import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.schema.SchemaVersion; +import org.apache.calcite.schema.Schemas; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.lookup.IgnoreCaseLookup; import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.schema.lookup.Lookup; @@ -39,9 +45,6 @@ import org.apache.calcite.util.Util; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; import com.google.common.collect.Ordering; import org.checkerframework.checker.nullness.qual.Nullable; @@ -54,7 +57,6 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; @@ -78,27 +80,23 @@ * queries against this schema are executed against those tables, pushing down * as much as possible of the query logic to SQL. */ -public class JdbcSchema implements Schema, Wrapper { +public class JdbcSchema extends JdbcBaseSchema implements Schema, Wrapper { private static final Logger LOGGER = LoggerFactory.getLogger(JdbcSchema.class); - public class TableNotFoundException extends RuntimeException { - } final DataSource dataSource; final @Nullable String catalog; final @Nullable String schema; public final SqlDialect dialect; final JdbcConvention convention; private final Lookup
tables = new IgnoreCaseLookup
() { - @Override - public @Nullable Table get(String name) { + @Override public @Nullable Table get(String name) { try (Stream s = getMetaTableStream(name)) { - return s.findFirst().map(it -> jdbcTableMapper(it) ).orElse(null); + return s.findFirst().map(it -> jdbcTableMapper(it)).orElse(null); } } - @Override - public Set getNames(LikePattern pattern) { - try( Stream s = getMetaTableStream(pattern.pattern)) { + @Override public Set getNames(LikePattern pattern) { + try (Stream s = getMetaTableStream(pattern.pattern)) { return s.map(it -> it.tableName).collect(Collectors.toSet()); } } @@ -254,19 +252,6 @@ public DataSource getDataSource() { return Schemas.subSchemaExpression(parentSchema, name, JdbcSchema.class); } - protected Multimap getFunctions() { - // TODO: populate map from JDBC metadata - return ImmutableMultimap.of(); - } - - @Override public final Collection getFunctions(String name) { - return getFunctions().get(name); // never null - } - - @Override public final Set getFunctionNames() { - return getFunctions().keySet(); - } - private Stream getMetaTableStream(String tableNamePattern) { final Pair<@Nullable String, @Nullable String> catalogSchema = getCatalogSchema(); final Stream tableDefs; @@ -276,7 +261,8 @@ private Stream getMetaTableStream(String tableNamePattern) { connection = dataSource.getConnection(); final List tableDefList = new ArrayList<>(); final DatabaseMetaData metaData = connection.getMetaData(); - resultSet = metaData.getTables(catalogSchema.left, catalogSchema.right, tableNamePattern, null); + resultSet = + metaData.getTables(catalogSchema.left, catalogSchema.right, tableNamePattern, null); tableDefs = asStream(connection, resultSet) .map(JdbcSchema::metaDataMapper); } catch (SQLException e) { @@ -288,35 +274,36 @@ private Stream getMetaTableStream(String tableNamePattern) { } private static Stream asStream(Connection connection, ResultSet resultSet) { - return StreamSupport.stream(new Spliterators.AbstractSpliterator( - Long.MAX_VALUE, Spliterator.ORDERED) { - @Override - public boolean tryAdvance(Consumer action) { - try { - if(!resultSet.next()) return false; - action.accept(resultSet); - return true; - } catch(SQLException ex) { - throw new RuntimeException(ex); - } - } - }, false).onClose(() -> { - close(connection, null, resultSet); - }) ; + return StreamSupport.stream( + new Spliterators.AbstractSpliterator( + Long.MAX_VALUE, Spliterator.ORDERED) { + @Override public boolean tryAdvance(Consumer action) { + try { + if (!resultSet.next()) { + return false; + } + action.accept(resultSet); + return true; + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + } + }, false).onClose(() -> close(connection, null, resultSet)); } private JdbcTable jdbcTableMapper(MetaImpl.MetaTable tableDef) { - return new JdbcTable(this, tableDef.tableCat, tableDef.tableSchem, - tableDef.tableName, getTableType(tableDef.tableType)); + return new JdbcTable(this, tableDef.tableCat, tableDef.tableSchem, tableDef.tableName, + getTableType(tableDef.tableType)); } - private static MetaImpl.MetaTable metaDataMapper(ResultSet resultSet){ - try { - return new MetaImpl.MetaTable(intern(resultSet.getString(1)), intern(resultSet.getString(2)), intern(resultSet.getString(3)), - intern(resultSet.getString(4))); - } catch (SQLException e) { - throw new RuntimeException(e); - } + private static MetaImpl.MetaTable metaDataMapper(ResultSet resultSet) { + try { + return new MetaImpl.MetaTable(intern(resultSet.getString(1)), intern(resultSet.getString(2)), + intern(resultSet.getString(3)), + intern(resultSet.getString(4))); + } catch (SQLException e) { + throw new RuntimeException(e); + } } private static String intern(@Nullable String string) { @@ -325,6 +312,7 @@ private static String intern(@Nullable String string) { } return string.intern(); } + private static TableType getTableType(String tableTypeName) { // Clean up table type. In particular, this ensures that 'SYSTEM TABLE', // returned by Phoenix among others, maps to TableType.SYSTEM_TABLE. @@ -337,11 +325,11 @@ private static TableType getTableType(String tableTypeName) { // not filter them as we keep all the other table types. final String tableTypeName2 = tableTypeName == null - ? null - : tableTypeName.toUpperCase(Locale.ROOT).replace(' ', '_'); + ? null + : tableTypeName.toUpperCase(Locale.ROOT).replace(' ', '_'); final TableType tableType = Util.enumVal(TableType.OTHER, tableTypeName2); - if (tableType == TableType.OTHER && tableTypeName2 != null) { + if (tableType == TableType.OTHER && tableTypeName2 != null) { LOGGER.info("Unknown table type: {}", tableTypeName2); } return tableType; @@ -355,7 +343,7 @@ private static List version(DatabaseMetaData metaData) throws SQLExcept /** Returns a pair of (catalog, schema) for the current connection. */ private Pair<@Nullable String, @Nullable String> getCatalogSchema() { - try(Connection connection = dataSource.getConnection()) { + try (Connection connection = dataSource.getConnection()) { final DatabaseMetaData metaData = connection.getMetaData(); final List version41 = ImmutableList.of(4, 1); // JDBC 4.1 String catalog = this.catalog; @@ -377,7 +365,7 @@ private static List version(DatabaseMetaData metaData) throws SQLExcept && metaData.getDatabaseProductName().equals("PostgreSQL")) { final String sql = "select current_database(), current_schema()"; try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(sql)) { + ResultSet resultSet = statement.executeQuery(sql)) { if (resultSet.next()) { catalog = resultSet.getString(1); schema = resultSet.getString(2); @@ -390,10 +378,6 @@ private static List version(DatabaseMetaData metaData) throws SQLExcept } } - @Deprecated @Override public @Nullable Table getTable(String name) { - return tables.get(name); - } - RelProtoDataType getRelDataType(String catalogName, String schemaName, String tableName) throws SQLException { Connection connection = null; @@ -519,32 +503,6 @@ private static RelDataType parseTypeString(RelDataTypeFactory typeFactory, } } - @Deprecated @Override public Set getTableNames() { - return tables.getNames(LikePattern.any()); - } - - protected Map getTypes() { - // TODO: populate map from JDBC metadata - return ImmutableMap.of(); - } - - @Override public @Nullable RelProtoDataType getType(String name) { - return getTypes().get(name); - } - - @Override public Set getTypeNames() { - //noinspection RedundantCast - return (Set) getTypes().keySet(); - } - - @Deprecated @Override public @Nullable Schema getSubSchema(String name) { - return subSchemas.get(name); - } - - @Deprecated @Override public Set getSubSchemaNames() { - return subSchemas.getNames(LikePattern.any()); - } - @Override public @Nullable T unwrap(Class clazz) { if (clazz.isInstance(this)) { return clazz.cast(this); diff --git a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java index 367200ebf6f..61d033f033c 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java @@ -16,15 +16,15 @@ */ package org.apache.calcite.jdbc; +import org.apache.calcite.linq4j.function.Function1; import org.apache.calcite.rel.type.RelProtoDataType; import org.apache.calcite.schema.Function; -import org.apache.calcite.schema.lookup.LikePattern; -import org.apache.calcite.schema.lookup.Lookup; -import org.apache.calcite.schema.lookup.Named; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.SchemaVersion; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.TableMacro; +import org.apache.calcite.schema.lookup.CachedLookup; +import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.util.NameMap; import org.apache.calcite.util.NameMultimap; import org.apache.calcite.util.NameSet; @@ -37,7 +37,6 @@ import java.util.Collection; import java.util.List; -import java.util.Set; import static org.apache.calcite.linq4j.Nullness.castNonNull; @@ -46,14 +45,17 @@ * functions and sub-schemas. */ class CachingCalciteSchema extends CalciteSchema { + private final ImmutableList> caches; private final Cached implicitFunctionCache; private final Cached implicitTypeCache; + private boolean cache = true; /** Creates a CachingCalciteSchema. */ CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, String name) { - this(parent, schema, name, null, null, null, null, null, null, null, null); + this(parent, schema, name, null, null, null, null, null, null, null, null, + new LookupDecorator()); } @SuppressWarnings({"argument.type.incompatible", "return.type.incompatible"}) @@ -66,9 +68,11 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, @Nullable NameMultimap functionMap, @Nullable NameSet functionNames, @Nullable NameMap nullaryFunctionMap, - @Nullable List> path) { + @Nullable List> path, + LookupDecorator lookupDecorator) { super(parent, schema, name, subSchemaMap, tableMap, latticeMap, typeMap, - functionMap, functionNames, nullaryFunctionMap, path); + functionMap, functionNames, nullaryFunctionMap, path, lookupDecorator); + this.caches = lookupDecorator.cacheBuilder.build(); this.implicitFunctionCache = new AbstractCached() { @Override public NameSet build() { @@ -89,6 +93,7 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, if (cache == this.cache) { return; } + enableCaches(cache); final long now = System.currentTimeMillis(); implicitFunctionCache.enable(now, cache); this.cache = cache; @@ -99,7 +104,7 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, } @Override protected @Nullable CalciteSchema createSubSchema(Schema schema, String name) { - return new CachingCalciteSchema(this, schema, name); + return new CachingCalciteSchema(this, schema, name); } /** Adds a child schema of this schema. */ @@ -197,7 +202,7 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, CalciteSchema snapshot = new CachingCalciteSchema(parent, schema.snapshot(version), name, null, tableMap, latticeMap, typeMap, - functionMap, functionNames, nullaryFunctionMap, getPath()); + functionMap, functionNames, nullaryFunctionMap, getPath(), new LookupDecorator()); for (CalciteSchema subSchema : subSchemaMap.map().values()) { CalciteSchema subSchemaSnapshot = subSchema.snapshot(snapshot, version); snapshot.subSchemaMap.put(subSchema.name, subSchemaSnapshot); @@ -205,6 +210,14 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, return snapshot; } + @Override public boolean removeTable(String name) { + if (cache) { + enableCaches(false); + enableCaches(true); + } + return super.removeTable(name); + } + @Override public boolean removeFunction(String name) { if (cache) { final long now = System.nanoTime(); @@ -214,6 +227,12 @@ private CachingCalciteSchema(@Nullable CalciteSchema parent, Schema schema, return super.removeFunction(name); } + private void enableCaches(final boolean cache) { + for (CachedLookup lookupCache : caches) { + lookupCache.enable(cache); + } + } + /** Strategy for caching the value of an object and re-creating it if its * value is out of date as of a given timestamp. * @@ -257,4 +276,17 @@ private abstract class AbstractCached implements Cached { } } + /** + * This class is used to decorate lookups with a cache. + */ + private static class LookupDecorator implements Function1, Lookup> { + + final ImmutableList.Builder> cacheBuilder = ImmutableList.builder(); + + @Override public Lookup apply(final Lookup lookup) { + CachedLookup cachedLookup = new CachedLookup(lookup); + cacheBuilder.add(cachedLookup); + return cachedLookup; + } + } } diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java index 4f6765ea262..5d3811eacfd 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java @@ -17,11 +17,17 @@ package org.apache.calcite.jdbc; import org.apache.calcite.linq4j.function.Experimental; -import org.apache.calcite.linq4j.function.Predicate1; +import org.apache.calcite.linq4j.function.Function1; import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.materialize.Lattice; import org.apache.calcite.rel.type.RelProtoDataType; -import org.apache.calcite.schema.*; +import org.apache.calcite.schema.Function; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.schema.SchemaVersion; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.TableMacro; +import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.impl.MaterializedViewTable; import org.apache.calcite.schema.impl.StarTable; import org.apache.calcite.schema.lookup.LikePattern; @@ -45,8 +51,6 @@ import java.util.NavigableMap; import java.util.NavigableSet; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; @@ -76,7 +80,6 @@ public abstract class CalciteSchema { private @Nullable List> path; - protected CalciteSchema(@Nullable CalciteSchema parent, Schema schema, String name, @Nullable NameMap subSchemaMap, @@ -87,14 +90,35 @@ protected CalciteSchema(@Nullable CalciteSchema parent, Schema schema, @Nullable NameSet functionNames, @Nullable NameMap nullaryFunctionMap, @Nullable List> path) { + this(parent, schema, name, subSchemaMap, tableMap, latticeMap, typeMap, functionMap, + functionNames, nullaryFunctionMap, path, l -> l); + } + + protected CalciteSchema(@Nullable CalciteSchema parent, Schema schema, + String name, + @Nullable NameMap subSchemaMap, + @Nullable NameMap tableMap, + @Nullable NameMap latticeMap, + @Nullable NameMap typeMap, + @Nullable NameMultimap functionMap, + @Nullable NameSet functionNames, + @Nullable NameMap nullaryFunctionMap, + @Nullable List> path, + Function1, Lookup> lookupDecorator) { this.parent = parent; this.schema = schema; this.name = name; this.tableMap = tableMap != null ? tableMap : new NameMap<>(); - this.tables = Lookup.concat(Lookup.of(this.tableMap),schema.tables().map((s,n) -> tableEntry(n,s))); + this.tables = + Lookup.concat(Lookup.of(this.tableMap), + (Lookup) lookupDecorator.apply( + schema.tables().map((s, n) -> tableEntry(n, s)))); this.latticeMap = latticeMap != null ? latticeMap : new NameMap<>(); this.subSchemaMap = subSchemaMap != null ? subSchemaMap : new NameMap<>(); - this.subSchemas = Lookup.concat(Lookup.of(this.subSchemaMap),schema.subSchemas().map((s,n) -> createSubSchema(s,n))); + this.subSchemas = + Lookup.concat(Lookup.of(this.subSchemaMap), + (Lookup) lookupDecorator.apply( + schema.subSchemas().map((s, n) -> createSubSchema(s, n)))); if (functionMap == null) { this.functionMap = new NameMultimap<>(); this.functionNames = new NameSet(); @@ -115,8 +139,8 @@ protected CalciteSchema(@Nullable CalciteSchema parent, Schema schema, this.path = path; } - /** Creates a sub-schema with a given name that is defined implicitly */ - protected abstract @Nullable CalciteSchema createSubSchema( Schema schema, String name); + /** Creates a sub-schema with a given name that is defined implicitly. */ + protected abstract @Nullable CalciteSchema createSubSchema(Schema schema, String name); /** Returns a type with a given name that is defined implicitly * (that is, by the underlying {@link Schema} object, not explicitly @@ -259,7 +283,7 @@ public List path(@Nullable String name) { /** Returns a table with the given name. Does not look for views. */ public final @Nullable TableEntry getTable(String tableName, boolean caseSensitive) { - return Lookup.get(tables,tableName,caseSensitive); + return Lookup.get(tables, tableName, caseSensitive); } public String getName() { @@ -313,14 +337,14 @@ public NavigableMap getLatticeMap() { /** Returns the set of all table names. Includes implicit and explicit tables * and functions with zero parameters. */ + public final Set getTableNames() { + return getTableNames(LikePattern.any()); + } + + /** Returns the set of filtered table names. Includes implicit and explicit tables + * and functions with zero parameters. */ public final Set getTableNames(LikePattern pattern) { - Predicate1 predicate = pattern.matcher(); - return Stream.concat( - tableMap.map().keySet() - .stream() - .filter(entry -> predicate.apply(entry)), - schema.tables().getNames(pattern).stream()) - .collect(Collectors.toSet()); + return tables.getNames(pattern); } /** Returns the set of all types names. */ @@ -595,29 +619,6 @@ protected LatticeEntry(CalciteSchema schema, String name) { /** Implementation of {@link SchemaPlus} based on a * {@link org.apache.calcite.jdbc.CalciteSchema}. */ private class SchemaPlusImpl implements SchemaPlus { - private final Lookup
tables; - private final Lookup subSchemas; - private SchemaPlusImpl() { - this.tables = new Lookup
() { - @Override - public @Nullable Table get(String name) { - final TableEntry entry = CalciteSchema.this.getTable(name,true); - return entry == null ? null : entry.getTable(); - } - - @Override - public @Nullable Named
getIgnoreCase(String name) { - final TableEntry entry = CalciteSchema.this.getTable(name,false); - return entry == null ? null : new Named<>(entry.name, entry.getTable()); - } - - @Override - public Set getNames(LikePattern pattern) { - return CalciteSchema.this.getTableNames(pattern); - } - }; - this.subSchemas = CalciteSchema.this.subSchemas.map((schema,name) -> schema.plus()); - } CalciteSchema calciteSchema() { return CalciteSchema.this; } @@ -651,19 +652,19 @@ CalciteSchema calciteSchema() { } @Override public Lookup
tables() { - return tables; + return CalciteSchema.this.tables.map((table, name) -> table.getTable()); } @Override public Lookup subSchemas() { - return subSchemas; + return CalciteSchema.this.subSchemas.map((schema, name) -> schema.plus()); } - @Deprecated @Override public @Nullable Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { final TableEntry entry = CalciteSchema.this.getTable(name, true); return entry == null ? null : entry.getTable(); } - @Deprecated @Override public Set getTableNames() { + @Override public Set getTableNames() { return CalciteSchema.this.getTableNames(LikePattern.any()); } @@ -684,13 +685,12 @@ CalciteSchema calciteSchema() { return CalciteSchema.this.getFunctionNames(); } - @Deprecated @Override public @Nullable SchemaPlus getSubSchema(String name) { - return subSchemas.get(name); + @Override public @Nullable SchemaPlus getSubSchema(String name) { + return subSchemas().get(name); } - @Deprecated @Override public Set getSubSchemaNames() { - //noinspection RedundantCast - return subSchemas.getNames(LikePattern.any()); + @Override public Set getSubSchemaNames() { + return subSchemas().getNames(LikePattern.any()); } @Override public SchemaPlus add(String name, Schema schema) { diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java index 6c4cd1c02c1..0e247c21fbc 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java @@ -34,6 +34,7 @@ import org.apache.calcite.schema.TableMacro; import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.impl.ScalarFunctionImpl; +import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlKind; diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java index de3d1fa968d..a68994d0e28 100644 --- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java @@ -34,10 +34,21 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rel.type.RelProtoDataType; import org.apache.calcite.rel.type.RelRecordType; -import org.apache.calcite.schema.*; +import org.apache.calcite.schema.ColumnStrategy; +import org.apache.calcite.schema.ModifiableTable; +import org.apache.calcite.schema.Path; +import org.apache.calcite.schema.ScannableTable; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.schema.SchemaVersion; +import org.apache.calcite.schema.Schemas; +import org.apache.calcite.schema.StreamableTable; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.TemporalTable; +import org.apache.calcite.schema.TranslatableTable; +import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.schema.lookup.Lookup; -import org.apache.calcite.schema.lookup.Named; import org.apache.calcite.sql.SqlAccessType; import org.apache.calcite.sql.validate.SqlModality; import org.apache.calcite.sql.validate.SqlMonotonicity; @@ -427,7 +438,7 @@ private static class MySchemaPlus implements SchemaPlus { this.parent = parent; this.name = name; this.schema = schema; - this.subSchemas = schema.subSchemas().map((s,key) -> new MySchemaPlus(this, key, s)); + this.subSchemas = schema.subSchemas().map((s, key) -> new MySchemaPlus(this, key, s)); } public static MySchemaPlus create(Path path) { @@ -501,15 +512,16 @@ public static MySchemaPlus create(Path path) { @Override public @Nullable Lookup
tables() { return schema.tables(); } + @Override public @Nullable Lookup subSchemas() { return subSchemas; } - @Deprecated @Override public @Nullable Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { return tables().get(name); } - @Deprecated @Override public Set getTableNames() { + @Override public Set getTableNames() { return schema.tables().getNames(LikePattern.any()); } @@ -530,7 +542,7 @@ public static MySchemaPlus create(Path path) { return schema.getFunctionNames(); } - @Deprecated @Override public Set getSubSchemaNames() { + @Override public Set getSubSchemaNames() { return schema.subSchemas().getNames(LikePattern.any()); } diff --git a/core/src/main/java/org/apache/calcite/schema/Schema.java b/core/src/main/java/org/apache/calcite/schema/Schema.java index 409ef238ea5..bd0dcd32616 100644 --- a/core/src/main/java/org/apache/calcite/schema/Schema.java +++ b/core/src/main/java/org/apache/calcite/schema/Schema.java @@ -18,9 +18,7 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.type.RelProtoDataType; - -import org.apache.calcite.schema.lookup.SimpleLookup; - +import org.apache.calcite.schema.lookup.CompatibilityLookup; import org.apache.calcite.schema.lookup.LikePattern; import org.apache.calcite.schema.lookup.Lookup; @@ -63,38 +61,40 @@ public interface Schema { /** - * Returns a lookup object to find tables + * Returns a lookup object to find tables. * * @return Lookup */ default Lookup
tables() { - return new SimpleLookup
(this::getTable, this::getTableNames); + return new CompatibilityLookup<>(this::getTable, this::getTableNames); } + /** + * Returns a lookup object to find sub schemas. + * + * @return Lookup + */ default Lookup subSchemas() { - return new SimpleLookup(this::getSubSchema, this::getSubSchemaNames); + return new CompatibilityLookup<>(this::getSubSchema, this::getSubSchemaNames); } /** * Returns a table with a given name, or null if not found. * - * @deprecated - * Please use {@link Schema#tables()} and {@link Lookup#get(String)} instead. + *

Please use {@link Schema#tables()} and {@link Lookup#get(String)} instead. * * @param name Table name * @return Table, or null */ - @Deprecated - @Nullable Table getTable(String name) ; + @Nullable Table getTable(String name); /** * Returns the names of the tables in this schema. * - * @deprecated - * Please use {@link Schema#tables()} and {@link Lookup#getNames(LikePattern)} instead. + *

Please use {@link Schema#tables()} and {@link Lookup#getNames(LikePattern)} instead. + * * @return Names of the tables in this schema */ - @Deprecated Set getTableNames(); /** @@ -131,22 +131,20 @@ default Lookup subSchemas() { /** * Returns a sub-schema with a given name, or null. * + *

Please use {@link Schema#subSchemas()} and {@link Lookup#get(String)} instead. + * * @param name Sub-schema name - * @deprecated - * Please use {@link Schema#subSchemas()} ()} and {@link Lookup#get(String)} instead. * @return Sub-schema with a given name, or null */ - @Deprecated @Nullable Schema getSubSchema(String name); /** * Returns the names of this schema's child schemas. * - * @deprecated - * Please use {@link Schema#subSchemas()} ()} and {@link Lookup#getNames(LikePattern)} instead. + *

Please use {@link Schema#subSchemas()} and {@link Lookup#getNames(LikePattern)} instead. + * * @return Names of this schema's child schemas */ - @Deprecated Set getSubSchemaNames(); /** diff --git a/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java b/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java index 3161ef5e9a2..0c8f687fc24 100644 --- a/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java +++ b/core/src/main/java/org/apache/calcite/schema/SchemaPlus.java @@ -18,11 +18,10 @@ import org.apache.calcite.materialize.Lattice; import org.apache.calcite.rel.type.RelProtoDataType; +import org.apache.calcite.schema.lookup.Lookup; import com.google.common.collect.ImmutableList; -import org.apache.calcite.schema.lookup.Lookup; - import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -48,6 +47,9 @@ */ public interface SchemaPlus extends Schema { + /** + * Returns a lookup object to find sub schemas. + */ Lookup subSchemas(); /** * Returns the parent schema, or null if this schema has no parent. @@ -63,7 +65,7 @@ public interface SchemaPlus extends Schema { String getName(); // override with stricter return - @Deprecated @Override @Nullable SchemaPlus getSubSchema(String name); + @Override @Nullable SchemaPlus getSubSchema(String name); /** Adds a schema as a sub-schema of this schema, and returns the wrapped * object. */ diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java index 43eae233322..661498f7d5d 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/AbstractSchema.java @@ -18,16 +18,20 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.type.RelProtoDataType; -import org.apache.calcite.schema.*; +import org.apache.calcite.schema.Function; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.SchemaFactory; +import org.apache.calcite.schema.SchemaPlus; +import org.apache.calcite.schema.SchemaVersion; +import org.apache.calcite.schema.Schemas; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.lookup.CompatibilityLookup; +import org.apache.calcite.schema.lookup.Lookup; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; -import org.apache.calcite.schema.lookup.Lookup; - -import org.apache.calcite.schema.lookup.SimpleLookup; - import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -55,8 +59,9 @@ */ public class AbstractSchema implements Schema { - private Lookup

tables = new SimpleLookup
(this::getTable, this::getTableNames); - private Lookup subSchemas = new SimpleLookup(this::getSubSchema, this::getSubSchemaNames); + private Lookup
tables = new CompatibilityLookup<>(this::getTable, this::getTableNames); + private Lookup subSchemas = + new CompatibilityLookup<>(this::getSubSchema, this::getSubSchemaNames); public AbstractSchema() { } @@ -64,6 +69,7 @@ public AbstractSchema() { @Override public Lookup
tables() { return tables; } + @Override public Lookup subSchemas() { return subSchemas; } @@ -95,12 +101,12 @@ protected Map getTableMap() { return ImmutableMap.of(); } - @Deprecated @Override public final Set getTableNames() { + @Override public final Set getTableNames() { //noinspection RedundantCast return (Set) getTableMap().keySet(); } - @Deprecated @Override public final @Nullable Table getTable(String name) { + @Override public final @Nullable Table getTable(String name) { return getTableMap().get(name); } @@ -166,12 +172,12 @@ protected Map getSubSchemaMap() { return ImmutableMap.of(); } - @Deprecated @Override public final Set getSubSchemaNames() { + @Override public final Set getSubSchemaNames() { //noinspection RedundantCast return (Set) getSubSchemaMap().keySet(); } - @Deprecated @Override public final @Nullable Schema getSubSchema(String name) { + @Override public final @Nullable Schema getSubSchema(String name) { return getSubSchemaMap().get(name); } diff --git a/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java b/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java index 5a04d05f41b..9b1bd5e4712 100644 --- a/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java +++ b/core/src/main/java/org/apache/calcite/schema/impl/DelegatingSchema.java @@ -19,12 +19,12 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.rel.type.RelProtoDataType; import org.apache.calcite.schema.Function; -import org.apache.calcite.schema.lookup.LikePattern; -import org.apache.calcite.schema.lookup.Lookup; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.SchemaVersion; import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.lookup.LikePattern; +import org.apache.calcite.schema.lookup.Lookup; import org.checkerframework.checker.nullness.qual.Nullable; @@ -67,11 +67,11 @@ public DelegatingSchema(Schema schema) { return schema.tables(); } - @Deprecated @Override public @Nullable Table getTable(String name) { + @Override public @Nullable Table getTable(String name) { return schema.tables().get(name); } - @Deprecated @Override public Set getTableNames() { + @Override public Set getTableNames() { return schema.tables().getNames(LikePattern.any()); } @@ -95,11 +95,11 @@ public DelegatingSchema(Schema schema) { return schema.subSchemas(); } - @Deprecated @Override public @Nullable Schema getSubSchema(String name) { + @Override public @Nullable Schema getSubSchema(String name) { return subSchemas().get(name); } - @Deprecated @Override public Set getSubSchemaNames() { + @Override public Set getSubSchemaNames() { return subSchemas().getNames(LikePattern.any()); } } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java new file mode 100644 index 00000000000..359ae2957b3 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/schema/lookup/CachedLookup.java @@ -0,0 +1,77 @@ +/* + * 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.apache.calcite.schema.lookup; + +import org.apache.calcite.util.NameMap; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Set; + +/** + * This class can be used to make a snapshot of a lookups. + * + * @param Element Type + */ +public class CachedLookup implements Lookup { + + private final Lookup delegate; + private Lookup cachedDelegate = null; + private boolean enabled = true; + + public CachedLookup(Lookup delegate) { + this.delegate = delegate; + } + + @Override public @Nullable T get(final String name) { + return delegate().get(name); + } + + @Override public @Nullable Named getIgnoreCase(final String name) { + return delegate().getIgnoreCase(name); + } + + @Override public Set getNames(final LikePattern pattern) { + return delegate().getNames(pattern); + } + + private Lookup delegate() { + if (!enabled) { + return delegate; + } + if (cachedDelegate == null) { + synchronized (this) { + if (cachedDelegate == null) { + NameMap map = new NameMap<>(); + for (String name : delegate.getNames(LikePattern.any())) { + map.put(name, delegate.get(name)); + } + cachedDelegate = new NameMapLookup<>(map); + } + } + } + return cachedDelegate; + } + + public void enable(boolean enabled) { + if (!enabled) { + cachedDelegate = null; + } + this.enabled = enabled; + } + +} diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/CompatibilityLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/CompatibilityLookup.java new file mode 100644 index 00000000000..d67f0ca5d34 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/schema/lookup/CompatibilityLookup.java @@ -0,0 +1,55 @@ +/* + * 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.apache.calcite.schema.lookup; + +import org.apache.calcite.linq4j.function.Predicate1; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * This class can be used to wrap existing schemas with a pair of {@code get...} + * and {@code get...Names} into a Lookup object. + * + * @param Element type + */ +public class CompatibilityLookup extends IgnoreCaseLookup { + + private final Function get; + private final Supplier> getNames; + + public CompatibilityLookup(Function get, Supplier> getNames) { + this.get = get; + this.getNames = getNames; + } + + @Nullable + @Override public T get(String name) { + return get.apply(name); + } + + @Override public @Nullable Set getNames(LikePattern pattern) { + final Predicate1 matcher = pattern.matcher(); + return getNames.get().stream() + .filter(name -> matcher.apply(name)) + .collect(Collectors.toSet()); + } +} diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/ConcatLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/ConcatLookup.java index 7db1e24a1ca..8bc456a5d26 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/ConcatLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/ConcatLookup.java @@ -14,42 +14,47 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; import org.checkerframework.checker.nullness.qual.Nullable; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +/** + * This class can be used to concat a list of lookups. + * + * @param Element type + */ class ConcatLookup implements Lookup { private final Lookup[] lookups; ConcatLookup(Lookup[] lookups) { this.lookups = lookups; } - @Override - public @Nullable T get(String name) { - for ( Lookup lookup : lookups) { + + @Override public @Nullable T get(String name) { + for (Lookup lookup : lookups) { T t = lookup.get(name); - if ( t != null) return t; + if (t != null) { + return t; + } } return null; } - @Override - public @Nullable Named getIgnoreCase(String name) { - for ( Lookup lookup : lookups) { + @Override public @Nullable Named getIgnoreCase(String name) { + for (Lookup lookup : lookups) { Named t = lookup.getIgnoreCase(name); - if ( t != null) return t; + if (t != null) { + return t; + } } return null; } - @Override - public @Nullable Set getNames(LikePattern pattern) { + @Override public @Nullable Set getNames(LikePattern pattern) { return Stream.of(lookups) .flatMap(lookup -> lookup.getNames(pattern).stream()) .collect(Collectors.toSet()); diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/EmptyLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/EmptyLookup.java index c9f113993fa..7488b24cab4 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/EmptyLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/EmptyLookup.java @@ -18,34 +18,28 @@ import com.google.common.collect.ImmutableSet; -import org.apache.calcite.schema.lookup.LikePattern; -import org.apache.calcite.schema.lookup.Lookup; -import org.apache.calcite.schema.lookup.Named; - import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Set; /** - * An abstract base class for lookups. implementing case insensitive lookup + * This class implements an empty Lookup. * + * @param Element type */ class EmptyLookup implements Lookup { - static Lookup INSTANCE = new EmptyLookup<>(); + static final Lookup INSTANCE = new EmptyLookup<>(); - @Override - public @Nullable T get(String name) { + @Override public @Nullable T get(String name) { return null; } - @Override - public @Nullable Named getIgnoreCase(String name) { + @Override public @Nullable Named getIgnoreCase(String name) { return null; } - @Override - public Set getNames(LikePattern pattern) { + @Override public Set getNames(LikePattern pattern) { return ImmutableSet.of(); } } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/EntryNotFoundException.java b/core/src/main/java/org/apache/calcite/schema/lookup/EntryNotFoundException.java index a3a1a7e527e..6ddfc93cb68 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/EntryNotFoundException.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/EntryNotFoundException.java @@ -14,11 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; /** - * Exceptions used in caches to signal none existing entries + * Exceptions used in caches to signal none existing entries. */ -class EntryNotFoundException extends RuntimeException{ +class EntryNotFoundException extends RuntimeException { } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java index 9f1de2f0085..fafb2bc8470 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/IgnoreCaseLookup.java @@ -16,9 +16,6 @@ */ package org.apache.calcite.schema.lookup; -import org.apache.calcite.schema.lookup.LikePattern; -import org.apache.calcite.schema.lookup.Lookup; -import org.apache.calcite.schema.lookup.Named; import org.apache.calcite.util.NameMap; import org.checkerframework.checker.nullness.qual.Nullable; @@ -29,6 +26,7 @@ /** * An abstract base class for lookups. implementing case insensitive lookup * + * @param Element type */ public abstract class IgnoreCaseLookup implements Lookup { @@ -43,7 +41,7 @@ public IgnoreCaseLookup() { * @param name Name * @return Entity, or null */ - @Nullable public abstract T get(String name) ; + @Nullable public abstract T get(String name); /** * Returns a named entity with a given name ignoring the case, or null if not found. @@ -51,24 +49,24 @@ public IgnoreCaseLookup() { * @param name Name * @return Entity, or null */ - @Override @Nullable public Named getIgnoreCase(String name){ + @Override @Nullable public Named getIgnoreCase(String name) { Map.Entry entry = getNameMap(false).range(name, false).firstEntry(); if (entry == null) { - entry = getNameMap(true).range(name, false).firstEntry(); - if ( entry == null) { - return null; - } + entry = getNameMap(true).range(name, false).firstEntry(); + if (entry == null) { + return null; + } } T result = get(entry.getValue()); - return result == null ? null : new Named<>(entry.getKey(),result); + return result == null ? null : new Named<>(entry.getKey(), result); } @Nullable public abstract Set getNames(LikePattern pattern); private NameMap getNameMap(boolean forceReload) { - if ( nameMap == null || forceReload) { + if (nameMap == null || forceReload) { synchronized (this) { - if ( nameMap == null || forceReload) { + if (nameMap == null || forceReload) { NameMap tmp = new NameMap<>(); for (String name : getNames(LikePattern.any())) { tmp.put(name, name); diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/LikePattern.java b/core/src/main/java/org/apache/calcite/schema/lookup/LikePattern.java index 23eac10ebfb..f00f40e3614 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/LikePattern.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/LikePattern.java @@ -14,13 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; import org.apache.calcite.linq4j.function.Predicate1; import java.util.regex.Pattern; +/** + * This class is used as parameter to Lookup.getNames + */ public class LikePattern { private static final String ANY = "%"; public final String pattern; diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/LoadingCacheLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/LoadingCacheLookup.java new file mode 100644 index 00000000000..dda5add7214 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/schema/lookup/LoadingCacheLookup.java @@ -0,0 +1,101 @@ +/* + * 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.apache.calcite.schema.lookup; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.UncheckedExecutionException; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * This class can be used to cache lookups. + * + * @param Element Type + */ +public class LoadingCacheLookup implements Lookup { + private final Lookup delegate; + + private final LoadingCache cache; + private final LoadingCache> cacheIgnoreCase; + + public LoadingCacheLookup(Lookup delegate) { + this.delegate = delegate; + this.cache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(new CacheLoader() { + @Override public T load(String name) throws Exception { + return Optional.ofNullable(delegate.get(name)) + .orElseThrow(() -> new EntryNotFoundException()); + } + }); + this.cacheIgnoreCase = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(new CacheLoader>() { + @Override public Named load(String name) throws Exception { + return Optional.ofNullable(delegate.getIgnoreCase(name)) + .orElseThrow(() -> new EntryNotFoundException()); + } + }); + } + + @Override public @Nullable T get(String name) { + try { + return cache.get(name); + } catch (UncheckedExecutionException e) { + if (e.getCause() instanceof EntryNotFoundException) { + return null; + } + throw e; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override public @Nullable Named getIgnoreCase(String name) { + try { + return cacheIgnoreCase.get(name); + } catch (UncheckedExecutionException e) { + if (e.getCause() instanceof EntryNotFoundException) { + return null; + } + throw e; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override public Set getNames(LikePattern pattern) { + return delegate.getNames(pattern); + } + + public void invalidate(String name) { + cache.invalidate(name); + cacheIgnoreCase.invalidate(name); + } + + public void invalidateAll() { + cache.invalidateAll(); + cacheIgnoreCase.invalidateAll(); + } +} diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/Lookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/Lookup.java index 001b513d91b..23a8f69a8df 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/Lookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/Lookup.java @@ -22,11 +22,11 @@ import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Function; /** - * A case sensitive/insensitive lookup for tables or functions + * A casesensitive/insensitive lookup for tables, schems, functions ... * + * @param Element type */ public interface Lookup { /** @@ -35,7 +35,7 @@ public interface Lookup { * @param name Name * @return Entity, or null */ - @Nullable T get(String name) ; + @Nullable T get(String name); /** * Returns a named entity with a given name ignoring the case, or null if not found. @@ -43,7 +43,7 @@ public interface Lookup { * @param name Name * @return Entity, or null */ - @Nullable Named getIgnoreCase(String name) ; + @Nullable Named getIgnoreCase(String name); /** * Returns the names of the entities in matching pattern. @@ -52,14 +52,20 @@ public interface Lookup { */ Set getNames(LikePattern pattern); - default Lookup map(BiFunction mapper) { - return new MappedLookup<>(this,mapper); + default Lookup map(BiFunction mapper) { + return new MappedLookup<>(this, mapper); } - static T get(Lookup lookup, String name, boolean caseSensitive) { - if ( caseSensitive) { + /** + * Helper method to call Lookup.get(String) or Lookup.getIgnoreCase(String) + * depending on the parameter caseSensitive. + * + * @return Entity, or null + */ + @Nullable static T get(Lookup lookup, String name, boolean caseSensitive) { + if (caseSensitive) { T entry = lookup.get(name); - if ( entry == null ) { + if (entry == null) { return null; } return entry; @@ -67,15 +73,24 @@ static T get(Lookup lookup, String name, boolean caseSensitive) { return Named.entity(lookup.getIgnoreCase(name)); } + /** + * Returns an empty lookup. + */ static Lookup empty() { return (Lookup) EmptyLookup.INSTANCE; } + /** + * Creates a new lookup object based on a NameMap. + */ static Lookup of(NameMap map) { return new NameMapLookup<>(map); } - static Lookup concat(Lookup ...lookups) { + /** + * Concat a list of lookups. + */ + static Lookup concat(Lookup... lookups) { return new ConcatLookup<>(lookups); } } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/MappedLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/MappedLookup.java index 65fb91a679b..c55d5b78c37 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/MappedLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/MappedLookup.java @@ -14,39 +14,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; -class MappedLookup implements Lookup { +/** + * A Lookup class which can be used to map different element types. + * + * @param Source element type + * @param Target element type + */ +class MappedLookup implements Lookup { private final Lookup lookup; - private final BiFunction mapper; + private final BiFunction mapper; - MappedLookup(Lookup lookup, BiFunction mapper) { + MappedLookup(Lookup lookup, BiFunction mapper) { this.lookup = lookup; this.mapper = mapper; } - @Override - public @Nullable T get(String name) { + + @Override public @Nullable T get(String name) { S entity = lookup.get(name); - return entity == null ? null : mapper.apply(entity,name); + return entity == null ? null : mapper.apply(entity, name); } - @Override - public @Nullable Named getIgnoreCase(String name) { + @Override public @Nullable Named getIgnoreCase(String name) { Named named = lookup.getIgnoreCase(name); - return named == null ? null : new Named<>(named.name(),mapper.apply(named.entity(),named.name())); + return named == null + ? null + : new Named<>(named.name(), mapper.apply(named.entity(), named.name())); } - @Override - public @Nullable Set getNames(LikePattern pattern) { + @Override public @Nullable Set getNames(LikePattern pattern) { return lookup.getNames(pattern); } } diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/NameMapLookup.java b/core/src/main/java/org/apache/calcite/schema/lookup/NameMapLookup.java index 0d6c8237e48..5079d34133c 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/NameMapLookup.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/NameMapLookup.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; import org.apache.calcite.linq4j.function.Predicate1; @@ -26,28 +25,35 @@ import java.util.Set; import java.util.stream.Collectors; +/** + * A Lookup class which is based on a NameMap. + * + * @param Element type + */ class NameMapLookup implements Lookup { private final NameMap map; NameMapLookup(NameMap map) { this.map = map; } - @Override - public @Nullable T get(String name) { - Map.Entry entry = map.range(name,true).firstEntry(); - if ( entry != null) return entry.getValue(); + + @Override public @Nullable T get(String name) { + Map.Entry entry = map.range(name, true).firstEntry(); + if (entry != null) { + return entry.getValue(); + } return null; } - @Override - public @Nullable Named getIgnoreCase(String name) { - Map.Entry entry = map.range(name,false).firstEntry(); - if ( entry != null) return new Named<>(entry.getKey(),entry.getValue()); + @Override public @Nullable Named getIgnoreCase(String name) { + Map.Entry entry = map.range(name, false).firstEntry(); + if (entry != null) { + return new Named<>(entry.getKey(), entry.getValue()); + } return null; } - @Override - public @Nullable Set getNames(LikePattern pattern) { + @Override public @Nullable Set getNames(LikePattern pattern) { final Predicate1 matcher = pattern.matcher(); return map.map().keySet().stream() .filter(name -> matcher.apply(name)) diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/Named.java b/core/src/main/java/org/apache/calcite/schema/lookup/Named.java index 836df72afc4..881ec0b65f1 100644 --- a/core/src/main/java/org/apache/calcite/schema/lookup/Named.java +++ b/core/src/main/java/org/apache/calcite/schema/lookup/Named.java @@ -14,11 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.calcite.schema.lookup; import java.security.InvalidParameterException; +/** + * This class is used to hold an object including its name. + * + * @param Element type + */ public class Named { private final String name; private final T entity; diff --git a/core/src/main/java/org/apache/calcite/schema/lookup/package-info.java b/core/src/main/java/org/apache/calcite/schema/lookup/package-info.java new file mode 100644 index 00000000000..edd5b38d382 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/schema/lookup/package-info.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/** + * Schema Lookup SPI. + * + *

The interfaces and classes in this package are used to lookup + * tables and subschemas within a schema. + */ +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.FIELD) +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.PARAMETER) +@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.RETURN) +package org.apache.calcite.schema.lookup; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.checkerframework.framework.qual.TypeUseLocation; diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java index e08aadf9535..f40edf36fd0 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -7575,7 +7575,7 @@ private void checkGetTimestamp(Connection con) throws SQLException { // create schema "/a/b1". Appears only when we disable caching. aSubSchemaMap.put("b1", new AbstractSchema()); - assertThat(aSchema.subSchemas().getNames(LikePattern.any()), hasSize(1)); + assertThat(aSchema.subSchemas().getNames(LikePattern.any()), hasSize(0)); assertThat(aSchema.subSchemas().get("b1"), nullValue()); aSchema.setCacheEnabled(false); assertThat(aSchema.subSchemas().getNames(LikePattern.any()), hasSize(1));