Repository: calcite
Updated Branches:
  refs/heads/master c2059f152 -> ac8d04ed9


[CALCITE-911] Add a variant of CalciteSchema that does not cache sub-objects

Close apache/calcite#142


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/ac8d04ed
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/ac8d04ed
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/ac8d04ed

Branch: refs/heads/master
Commit: ac8d04ed95589f29571b7de3c220876c3ebc3a00
Parents: c2059f1
Author: Jinfeng Ni <[email protected]>
Authored: Tue Mar 3 14:51:09 2015 -0800
Committer: Julian Hyde <[email protected]>
Committed: Thu Nov 5 23:18:24 2015 -0800

----------------------------------------------------------------------
 .../calcite/jdbc/CachingCalciteSchema.java      | 292 +++++++++++++++
 .../calcite/jdbc/CalciteConnectionImpl.java     |  12 +-
 .../org/apache/calcite/jdbc/CalciteFactory.java |   2 +-
 .../calcite/jdbc/CalciteJdbc41Factory.java      |   4 +-
 .../apache/calcite/jdbc/CalciteMetaImpl.java    |   2 +-
 .../org/apache/calcite/jdbc/CalcitePrepare.java |   2 +-
 .../apache/calcite/jdbc/CalciteRootSchema.java  |   3 +-
 .../org/apache/calcite/jdbc/CalciteSchema.java  | 352 +++++++------------
 .../java/org/apache/calcite/jdbc/Driver.java    |   2 +-
 .../calcite/jdbc/SimpleCalciteSchema.java       | 141 ++++++++
 .../org/apache/calcite/materialize/Lattice.java |  11 +-
 .../materialize/MaterializationActor.java       |   9 +-
 .../java/org/apache/calcite/schema/Schemas.java |   3 +-
 .../org/apache/calcite/tools/Frameworks.java    |   1 -
 .../java/org/apache/calcite/test/JdbcTest.java  |  51 +++
 site/_docs/howto.md                             |   4 +-
 16 files changed, 633 insertions(+), 258 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java 
b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
new file mode 100644
index 0000000..8defef4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
@@ -0,0 +1,292 @@
+/*
+ * 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.jdbc;
+
+import org.apache.calcite.schema.Function;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.TableMacro;
+import org.apache.calcite.util.Compatible;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.ImmutableSortedSet;
+
+import java.util.Collection;
+import java.util.NavigableSet;
+
+/**
+ * Concrete implementation of {@link CalciteSchema} that caches tables,
+ * functions and sub-schemas.
+ */
+class CachingCalciteSchema extends CalciteSchema {
+  private final Cached<SubSchemaCache> implicitSubSchemaCache;
+  private final Cached<NavigableSet<String>> implicitTableCache;
+  private final Cached<NavigableSet<String>> implicitFunctionCache;
+
+  private boolean cache = true;
+
+  /** Creates a CachingCalciteSchema. */
+  CachingCalciteSchema(CalciteSchema parent, Schema schema, String name) {
+    super(parent, schema, name);
+    this.implicitSubSchemaCache =
+        new AbstractCached<SubSchemaCache>() {
+          public SubSchemaCache build() {
+            return new SubSchemaCache(CachingCalciteSchema.this,
+                Compatible.INSTANCE.navigableSet(
+                    ImmutableSortedSet.copyOf(COMPARATOR,
+                        
CachingCalciteSchema.this.schema.getSubSchemaNames())));
+          }
+        };
+    this.implicitTableCache =
+        new AbstractCached<NavigableSet<String>>() {
+          public NavigableSet<String> build() {
+            return Compatible.INSTANCE.navigableSet(
+                ImmutableSortedSet.copyOf(COMPARATOR,
+                    CachingCalciteSchema.this.schema.getTableNames()));
+          }
+        };
+    this.implicitFunctionCache =
+        new AbstractCached<NavigableSet<String>>() {
+          public NavigableSet<String> build() {
+            return Compatible.INSTANCE.navigableSet(
+                ImmutableSortedSet.copyOf(COMPARATOR,
+                    CachingCalciteSchema.this.schema.getFunctionNames()));
+          }
+        };
+  }
+
+  public void setCache(boolean cache) {
+    if (cache == this.cache) {
+      return;
+    }
+    final long now = System.currentTimeMillis();
+    implicitSubSchemaCache.enable(now, cache);
+    implicitTableCache.enable(now, cache);
+    implicitFunctionCache.enable(now, cache);
+    this.cache = cache;
+  }
+
+  protected boolean isCacheEnabled() {
+    return this.cache;
+  }
+
+  protected CalciteSchema getImplicitSubSchema(String schemaName,
+      boolean caseSensitive) {
+    if (caseSensitive) {
+      // Check implicit schemas, case-sensitive.
+      final long now = System.currentTimeMillis();
+      final SubSchemaCache subSchemaCache = implicitSubSchemaCache.get(now);
+      if (subSchemaCache.names.contains(schemaName)) {
+        return subSchemaCache.cache.getUnchecked(schemaName);
+      }
+    } else {
+      // Check implicit schemas, case-insensitive.
+      final long now = System.currentTimeMillis();
+      final SubSchemaCache subSchemaCache =
+          implicitSubSchemaCache.get(now);
+      final String schemaName2 = subSchemaCache.names.floor(schemaName);
+      if (schemaName2 != null) {
+        return subSchemaCache.cache.getUnchecked(schemaName2);
+      }
+    }
+    return null;
+  }
+
+  /** Adds a child schema of this schema. */
+  public CalciteSchema add(String name, Schema schema) {
+    final CalciteSchema calciteSchema =
+        new CachingCalciteSchema(this, schema, name);
+    subSchemaMap.put(name, calciteSchema);
+    return calciteSchema;
+  }
+
+  protected TableEntry getImplicitTable(String tableName,
+      boolean caseSensitive) {
+    if (caseSensitive) {
+      // Check implicit tables, case-sensitive.
+      final long now = System.currentTimeMillis();
+      if (implicitTableCache.get(now).contains(tableName)) {
+        final Table table = schema.getTable(tableName);
+        if (table != null) {
+          return tableEntry(tableName, table);
+        }
+      }
+    } else {
+      // Check implicit tables, case-insensitive.
+      final long now = System.currentTimeMillis();
+      final NavigableSet<String> implicitTableNames =
+          implicitTableCache.get(now);
+      final String tableName2 = implicitTableNames.floor(tableName);
+      if (tableName2 != null) {
+        final Table table = schema.getTable(tableName2);
+        if (table != null) {
+          return tableEntry(tableName2, table);
+        }
+      }
+    }
+    return null;
+  }
+
+  protected void addImplicitSubSchemaToBuilder(
+      ImmutableSortedMap.Builder<String, CalciteSchema> builder) {
+    ImmutableSortedMap<String, CalciteSchema> explicitSubSchemas = 
builder.build();
+    final long now = System.currentTimeMillis();
+    final SubSchemaCache subSchemaCache = implicitSubSchemaCache.get(now);
+    for (String name : subSchemaCache.names) {
+      if (explicitSubSchemas.containsKey(name)) {
+        // explicit sub-schema wins.
+        continue;
+      }
+      builder.put(name, subSchemaCache.cache.getUnchecked(name));
+    }
+  }
+
+  protected void addImplicitTableToBuilder(
+      ImmutableSortedSet.Builder<String> builder) {
+    // Add implicit tables, case-sensitive.
+    builder.addAll(implicitTableCache.get(System.currentTimeMillis()));
+  }
+
+  protected void addImplicitFunctionToBuilder(
+      ImmutableList.Builder<Function> builder) {
+    // Add implicit functions, case-insensitive.
+    for (String name2
+        : find(implicitFunctionCache.get(System.currentTimeMillis()), name)) {
+      final Collection<Function> functions = schema.getFunctions(name2);
+      if (functions != null) {
+        builder.addAll(functions);
+      }
+    }
+  }
+
+  protected void addImplicitFuncNamesToBuilder(
+      ImmutableSortedSet.Builder<String> builder) {
+    // Add implicit functions, case-sensitive.
+    builder.addAll(implicitFunctionCache.get(System.currentTimeMillis()));
+  }
+
+  protected void addImplicitTablesBasedOnNullaryFunctionsToBuilder(
+      ImmutableSortedMap.Builder<String, Table> builder) {
+    ImmutableSortedMap<String, Table> explicitTables = builder.build();
+
+    for (String s : implicitFunctionCache.get(System.currentTimeMillis())) {
+      // explicit table wins.
+      if (explicitTables.containsKey(s)) {
+        continue;
+      }
+      for (Function function : schema.getFunctions(s)) {
+        if (function instanceof TableMacro
+            && function.getParameters().isEmpty()) {
+          final Table table = ((TableMacro) 
function).apply(ImmutableList.of());
+          builder.put(s, table);
+        }
+      }
+    }
+  }
+
+  protected TableEntry getImplicitTableBasedOnNullaryFunction(String tableName,
+      boolean caseSensitive) {
+    final NavigableSet<String> set =
+        implicitFunctionCache.get(System.currentTimeMillis());
+    for (String s : find(set, tableName)) {
+      for (Function function : schema.getFunctions(s)) {
+        if (function instanceof TableMacro
+            && function.getParameters().isEmpty()) {
+          final Table table =
+              ((TableMacro) function).apply(ImmutableList.of());
+          return tableEntry(tableName, table);
+        }
+      }
+    }
+    return null;
+  }
+
+  /** Strategy for caching the value of an object and re-creating it if its
+   * value is out of date as of a given timestamp.
+   *
+   * @param <T> Type of cached object
+   */
+  private interface Cached<T> {
+    /** Returns the value; uses cached value if valid. */
+    T get(long now);
+
+    /** Creates a new value. */
+    T build();
+
+    /** Called when CalciteSchema caching is enabled or disabled. */
+    void enable(long now, boolean enabled);
+  }
+
+  /** Implementation of {@link CachingCalciteSchema.Cached}
+   * that drives from {@link CachingCalciteSchema#cache}. */
+  private abstract class AbstractCached<T> implements Cached<T> {
+    T t;
+    long checked = Long.MIN_VALUE;
+
+    public T get(long now) {
+      if (!CachingCalciteSchema.this.cache) {
+        return build();
+      }
+      if (checked == Long.MIN_VALUE
+          || schema.contentsHaveChangedSince(checked, now)) {
+        t = build();
+      }
+      checked = now;
+      return t;
+    }
+
+    public void enable(long now, boolean enabled) {
+      if (!enabled) {
+        t = null;
+      }
+      checked = Long.MIN_VALUE;
+    }
+  }
+
+  /** Information about the implicit sub-schemas of an {@link CalciteSchema}. 
*/
+  private static class SubSchemaCache {
+    /** The names of sub-schemas returned from the {@link Schema} SPI. */
+    final NavigableSet<String> names;
+    /** Cached {@link CalciteSchema} wrappers. It is
+     * worth caching them because they contain maps of their own sub-objects. 
*/
+    final LoadingCache<String, CalciteSchema> cache;
+
+    private SubSchemaCache(final CalciteSchema calciteSchema,
+        NavigableSet<String> names) {
+      this.names = names;
+      this.cache = CacheBuilder.newBuilder().build(
+          new CacheLoader<String, CalciteSchema>() {
+            @SuppressWarnings("NullableProblems")
+            @Override public CalciteSchema load(String schemaName) {
+              final Schema subSchema =
+                  calciteSchema.schema.getSubSchema(schemaName);
+              if (subSchema == null) {
+                throw new RuntimeException("sub-schema " + schemaName
+                    + " not found");
+              }
+              return new CachingCalciteSchema(calciteSchema, subSchema, 
schemaName);
+            }
+          });
+    }
+  }
+}
+
+// End CachingCalciteSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
index 2b723d9..4319fe0 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
@@ -87,7 +87,7 @@ abstract class CalciteConnectionImpl
     implements CalciteConnection, QueryProvider {
   public final JavaTypeFactory typeFactory;
 
-  final CalciteRootSchema rootSchema;
+  final CalciteSchema rootSchema;
   final Function0<CalcitePrepare> prepareFactory;
   final CalciteServer server = new CalciteServerImpl();
 
@@ -107,7 +107,7 @@ abstract class CalciteConnectionImpl
    * @param typeFactory Type factory, or null
    */
   protected CalciteConnectionImpl(Driver driver, AvaticaFactory factory,
-      String url, Properties info, CalciteRootSchema rootSchema,
+      String url, Properties info, CalciteSchema rootSchema,
       JavaTypeFactory typeFactory) {
     super(driver, factory, url, info);
     CalciteConnectionConfig cfg = new CalciteConnectionConfigImpl(info);
@@ -120,8 +120,10 @@ abstract class CalciteConnectionImpl
       this.typeFactory = new JavaTypeFactoryImpl(typeSystem);
     }
     this.rootSchema =
-        rootSchema != null ? rootSchema : CalciteSchema.createRootSchema(true);
-
+        Preconditions.checkNotNull(rootSchema != null
+            ? rootSchema
+            : CalciteSchema.createRootSchema(true));
+    Preconditions.checkArgument(this.rootSchema.isRoot(), "must be root 
schema");
     this.properties.put(InternalProperty.CASE_SENSITIVE, cfg.caseSensitive());
     this.properties.put(InternalProperty.UNQUOTED_CASING, 
cfg.unquotedCasing());
     this.properties.put(InternalProperty.QUOTED_CASING, cfg.quotedCasing());
@@ -432,7 +434,7 @@ abstract class CalciteConnectionImpl
       return connection.typeFactory;
     }
 
-    public CalciteRootSchema getRootSchema() {
+    public CalciteSchema getRootSchema() {
       return connection.rootSchema;
     }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java
index 03f5496..68c5507 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteFactory.java
@@ -56,7 +56,7 @@ public abstract class CalciteFactory implements 
AvaticaFactory {
   /** Creates a connection with a root schema. */
   public abstract AvaticaConnection newConnection(UnregisteredDriver driver,
       AvaticaFactory factory, String url, Properties info,
-      CalciteRootSchema rootSchema, JavaTypeFactory typeFactory);
+      CalciteSchema rootSchema, JavaTypeFactory typeFactory);
 }
 
 // End CalciteFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
index 5f677c1..04258b7 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
@@ -55,7 +55,7 @@ public class CalciteJdbc41Factory extends CalciteFactory {
 
   public CalciteJdbc41Connection newConnection(UnregisteredDriver driver,
       AvaticaFactory factory, String url, Properties info,
-      CalciteRootSchema rootSchema, JavaTypeFactory typeFactory) {
+      CalciteSchema rootSchema, JavaTypeFactory typeFactory) {
     return new CalciteJdbc41Connection(
         (Driver) driver, factory, url, info, rootSchema, typeFactory);
   }
@@ -109,7 +109,7 @@ public class CalciteJdbc41Factory extends CalciteFactory {
   /** Implementation of connection for JDBC 4.1. */
   private static class CalciteJdbc41Connection extends CalciteConnectionImpl {
     CalciteJdbc41Connection(Driver driver, AvaticaFactory factory, String url,
-        Properties info, CalciteRootSchema rootSchema,
+        Properties info, CalciteSchema rootSchema,
         JavaTypeFactory typeFactory) {
       super(driver, factory, url, info, rootSchema, typeFactory);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
index e4f0ae5..96b0f54 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
@@ -647,7 +647,7 @@ public class CalciteMetaImpl extends MetaImpl {
 
   /** A trojan-horse method, subject to change without notice. */
   @VisibleForTesting
-  public static CalciteConnection connect(CalciteRootSchema schema,
+  public static CalciteConnection connect(CalciteSchema schema,
       JavaTypeFactory typeFactory) {
     return DRIVER.connect(schema, typeFactory);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
index 7ed43f4..0aa47bc 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
@@ -107,7 +107,7 @@ public interface CalcitePrepare {
   interface Context {
     JavaTypeFactory getTypeFactory();
 
-    CalciteRootSchema getRootSchema();
+    CalciteSchema getRootSchema();
 
     List<String> getDefaultSchemaPath();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteRootSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteRootSchema.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteRootSchema.java
index 6a22618..66e7526 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteRootSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteRootSchema.java
@@ -21,7 +21,8 @@ import org.apache.calcite.schema.Schema;
 /**
  * Root schema.
  */
-public class CalciteRootSchema extends CalciteSchema {
+@Deprecated // to be removed before 2.0
+public class CalciteRootSchema extends CachingCalciteSchema {
   /** Creates a root schema. */
   CalciteRootSchema(Schema schema) {
     super(null, schema, "");

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
----------------------------------------------------------------------
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 3ce35b0..a1f24b0 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
@@ -28,9 +28,6 @@ import org.apache.calcite.schema.impl.StarTable;
 import org.apache.calcite.util.Compatible;
 
 import com.google.common.base.Preconditions;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
@@ -54,13 +51,13 @@ import java.util.TreeSet;
  *
  * <p>Wrapper around user-defined schema used internally.</p>
  */
-public class CalciteSchema {
+public abstract class CalciteSchema {
   /** Comparator that compares all strings differently, but if two strings are
    * equal in case-insensitive match they are right next to each other. In a
    * collection sorted on this comparator, we can find case-insensitive matches
    * for a given string using a range scan between the upper-case string and
    * the lower-case string. */
-  private static final Comparator<String> COMPARATOR =
+  protected static final Comparator<String> COMPARATOR =
       new Comparator<String>() {
         public int compare(String o1, String o2) {
           int c = o1.compareToIgnoreCase(o2);
@@ -77,69 +74,68 @@ public class CalciteSchema {
   /** Tables explicitly defined in this schema. Does not include tables in
    *  {@link #schema}. */
   public final NavigableMap<String, TableEntry> tableMap =
-      new TreeMap<String, TableEntry>(COMPARATOR);
-  private final Multimap<String, FunctionEntry> functionMap =
+      new TreeMap<>(COMPARATOR);
+  protected final Multimap<String, FunctionEntry> functionMap =
       LinkedListMultimap.create();
-  private final NavigableMap<String, LatticeEntry> latticeMap =
-      new TreeMap<String, LatticeEntry>(COMPARATOR);
-  private final NavigableSet<String> functionNames =
-      new TreeSet<String>(COMPARATOR);
-  private final NavigableMap<String, FunctionEntry> nullaryFunctionMap =
-      new TreeMap<String, FunctionEntry>(COMPARATOR);
-  private final NavigableMap<String, CalciteSchema> subSchemaMap =
-      new TreeMap<String, CalciteSchema>(COMPARATOR);
+  protected final NavigableMap<String, LatticeEntry> latticeMap =
+      new TreeMap<>(COMPARATOR);
+  protected final NavigableSet<String> functionNames =
+      new TreeSet<>(COMPARATOR);
+  protected final NavigableMap<String, FunctionEntry> nullaryFunctionMap =
+      new TreeMap<>(COMPARATOR);
+  protected final NavigableMap<String, CalciteSchema> subSchemaMap =
+      new TreeMap<>(COMPARATOR);
   private ImmutableList<ImmutableList<String>> path;
-  private boolean cache = true;
-  private final Cached<SubSchemaCache> implicitSubSchemaCache;
-  private final Cached<NavigableSet<String>> implicitTableCache;
-  private final Cached<NavigableSet<String>> implicitFunctionCache;
 
-  public CalciteSchema(CalciteSchema parent, final Schema schema, String name) 
{
+  CalciteSchema(CalciteSchema parent, Schema schema, String name) {
     this.parent = parent;
     this.schema = schema;
     this.name = name;
-    assert (parent == null) == (this instanceof CalciteRootSchema);
-    this.implicitSubSchemaCache =
-        new AbstractCached<SubSchemaCache>() {
-          public SubSchemaCache build() {
-            return new SubSchemaCache(CalciteSchema.this,
-                Compatible.INSTANCE.navigableSet(
-                    ImmutableSortedSet.copyOf(COMPARATOR,
-                        schema.getSubSchemaNames())));
-          }
-        };
-    this.implicitTableCache =
-        new AbstractCached<NavigableSet<String>>() {
-          public NavigableSet<String> build() {
-            return Compatible.INSTANCE.navigableSet(
-                ImmutableSortedSet.copyOf(COMPARATOR,
-                    schema.getTableNames()));
-          }
-        };
-    this.implicitFunctionCache =
-        new AbstractCached<NavigableSet<String>>() {
-          public NavigableSet<String> build() {
-            return Compatible.INSTANCE.navigableSet(
-                ImmutableSortedSet.copyOf(COMPARATOR,
-                    schema.getFunctionNames()));
-          }
-        };
   }
 
-  /** Creates a root schema. When <code>addMetadataSchema</code> argument is
-   * true a "metadata" schema containing definitions of tables, columns etc. is
-   * added to root schema. */
-  public static CalciteRootSchema createRootSchema(boolean addMetadataSchema) {
-    CalciteRootSchema rootSchema =
-        new CalciteRootSchema(new CalciteConnectionImpl.RootSchema());
-    if (addMetadataSchema) {
-      rootSchema.add("metadata", MetadataSchema.INSTANCE);
-    }
-    return rootSchema;
-  }
+  /** Returns a sub-schema with a given name that is defined implicitly
+   * (that is, by the underlying {@link Schema} object, not explicitly
+   * by a call to {@link #add(String, Schema)}), or null. */
+  protected abstract CalciteSchema getImplicitSubSchema(String schemaName,
+      boolean caseSensitive);
+
+  /** Returns a table with a given name that is defined implicitly
+   * (that is, by the underlying {@link Schema} object, not explicitly
+   * by a call to {@link #add(String, Table)}), or null. */
+  protected abstract TableEntry getImplicitTable(String tableName,
+      boolean caseSensitive);
+
+  /** Returns table function with a given name and zero arguments that is
+   * defined implicitly (that is, by the underlying {@link Schema} object,
+   * not explicitly by a call to {@link #add(String, Function)}), or null. */
+  protected abstract TableEntry getImplicitTableBasedOnNullaryFunction(String 
tableName,
+      boolean caseSensitive);
+
+  /** Adds implicit sub-schemas to a builder. */
+  protected abstract void addImplicitSubSchemaToBuilder(
+      ImmutableSortedMap.Builder<String, CalciteSchema> builder);
+
+  /** Adds implicit tables to a builder. */
+  protected abstract void addImplicitTableToBuilder(
+      ImmutableSortedSet.Builder<String> builder);
+
+  /** Adds implicit functions to a builder. */
+  protected abstract void 
addImplicitFunctionToBuilder(ImmutableList.Builder<Function> builder);
+
+  /** Adds implicit function names to a builder. */
+  protected abstract void addImplicitFuncNamesToBuilder(
+      ImmutableSortedSet.Builder<String> builder);
+
+  /** Adds implicit table functions to a builder. */
+  protected abstract void addImplicitTablesBasedOnNullaryFunctionsToBuilder(
+      ImmutableSortedMap.Builder<String, Table> builder);
+
+  protected abstract boolean isCacheEnabled();
+
+  public abstract void setCache(boolean cache);
 
   /** Creates a TableEntryImpl with no SQLs. */
-  private TableEntryImpl tableEntry(String name, Table table) {
+  protected TableEntryImpl tableEntry(String name, Table table) {
     return new TableEntryImpl(this, name, table, ImmutableList.<String>of());
   }
 
@@ -177,18 +173,23 @@ public class CalciteSchema {
     return entry;
   }
 
-  public CalciteRootSchema root() {
+  public CalciteSchema root() {
     for (CalciteSchema schema = this;;) {
       if (schema.parent == null) {
-        return (CalciteRootSchema) schema;
+        return schema;
       }
       schema = schema.parent;
     }
   }
 
+  /** Returns whether this is a root schema. */
+  public boolean isRoot() {
+    return parent == null;
+  }
+
   /** Returns the path of an object in this schema. */
   public List<String> path(String name) {
-    final List<String> list = new ArrayList<String>();
+    final List<String> list = new ArrayList<>();
     if (name != null) {
       list.add(name);
     }
@@ -202,17 +203,6 @@ public class CalciteSchema {
     return ImmutableList.copyOf(Lists.reverse(list));
   }
 
-  private void setCache(boolean cache) {
-    if (cache == this.cache) {
-      return;
-    }
-    final long now = System.currentTimeMillis();
-    implicitSubSchemaCache.enable(now, cache);
-    implicitTableCache.enable(now, cache);
-    implicitFunctionCache.enable(now, cache);
-    this.cache = cache;
-  }
-
   public final CalciteSchema getSubSchema(String schemaName,
       boolean caseSensitive) {
     if (caseSensitive) {
@@ -221,13 +211,6 @@ public class CalciteSchema {
       if (entry != null) {
         return entry;
       }
-      // Check implicit schemas, case-sensitive.
-      final long now = System.currentTimeMillis();
-      final SubSchemaCache subSchemaCache = implicitSubSchemaCache.get(now);
-      if (subSchemaCache.names.contains(schemaName)) {
-        return subSchemaCache.cache.getUnchecked(schemaName);
-      }
-      return null;
     } else {
       // Check explicit schemas, case-insensitive.
       //noinspection LoopStatementThatDoesntLoop
@@ -235,24 +218,12 @@ public class CalciteSchema {
           : find(subSchemaMap, schemaName).entrySet()) {
         return entry.getValue();
       }
-      // Check implicit schemas, case-insensitive.
-      final long now = System.currentTimeMillis();
-      final SubSchemaCache subSchemaCache =
-          implicitSubSchemaCache.get(now);
-      final String schemaName2 = subSchemaCache.names.floor(schemaName);
-      if (schemaName2 != null) {
-        return subSchemaCache.cache.getUnchecked(schemaName2);
-      }
-      return null;
     }
+    return getImplicitSubSchema(schemaName, caseSensitive);
   }
 
   /** Adds a child schema of this schema. */
-  public CalciteSchema add(String name, Schema schema) {
-    final CalciteSchema calciteSchema = new CalciteSchema(this, schema, name);
-    subSchemaMap.put(name, calciteSchema);
-    return calciteSchema;
-  }
+  public abstract CalciteSchema add(String name, Schema schema);
 
   /** Returns a table that materializes the given SQL statement. */
   public final TableEntry getTableBySql(String sql) {
@@ -265,23 +236,13 @@ public class CalciteSchema {
   }
 
   /** Returns a table with the given name. Does not look for views. */
-  public final TableEntry getTable(String tableName,
-      boolean caseSensitive) {
+  public final TableEntry getTable(String tableName, boolean caseSensitive) {
     if (caseSensitive) {
       // Check explicit tables, case-sensitive.
       final TableEntry entry = tableMap.get(tableName);
       if (entry != null) {
         return entry;
       }
-      // Check implicit tables, case-sensitive.
-      final long now = System.currentTimeMillis();
-      if (implicitTableCache.get(now).contains(tableName)) {
-        final Table table = schema.getTable(tableName);
-        if (table != null) {
-          return tableEntry(tableName, table);
-        }
-      }
-      return null;
     } else {
       // Check explicit tables, case-insensitive.
       //noinspection LoopStatementThatDoesntLoop
@@ -289,19 +250,9 @@ public class CalciteSchema {
           : find(tableMap, tableName).entrySet()) {
         return entry.getValue();
       }
-      // Check implicit tables, case-insensitive.
-      final long now = System.currentTimeMillis();
-      final NavigableSet<String> implicitTableNames =
-          implicitTableCache.get(now);
-      final String tableName2 = implicitTableNames.floor(tableName);
-      if (tableName2 != null) {
-        final Table table = schema.getTable(tableName2);
-        if (table != null) {
-          return tableEntry(tableName2, table);
-        }
-      }
-      return null;
     }
+
+    return getImplicitTable(tableName, caseSensitive);
   }
 
   public String getName() {
@@ -337,17 +288,13 @@ public class CalciteSchema {
    * {@link #add(String, org.apache.calcite.schema.Schema)}) and implicit
    * (defined using {@link 
org.apache.calcite.schema.Schema#getSubSchemaNames()}
    * and {@link Schema#getSubSchema(String)}). */
-  public NavigableMap<String, CalciteSchema> getSubSchemaMap() {
+  public final NavigableMap<String, CalciteSchema> getSubSchemaMap() {
     // Build a map of implicit sub-schemas first, then explicit sub-schemas.
     // If there are implicit and explicit with the same name, explicit wins.
     final ImmutableSortedMap.Builder<String, CalciteSchema> builder =
-        new ImmutableSortedMap.Builder<String, CalciteSchema>(COMPARATOR);
-    final long now = System.currentTimeMillis();
-    final SubSchemaCache subSchemaCache = implicitSubSchemaCache.get(now);
-    for (String name : subSchemaCache.names) {
-      builder.put(name, subSchemaCache.cache.getUnchecked(name));
-    }
+        new ImmutableSortedMap.Builder<>(COMPARATOR);
     builder.putAll(subSchemaMap);
+    addImplicitSubSchemaToBuilder(builder);
     return Compatible.INSTANCE.navigableMap(builder.build());
   }
 
@@ -360,19 +307,19 @@ public class CalciteSchema {
 
   /** Returns the set of all table names. Includes implicit and explicit tables
    * and functions with zero parameters. */
-  public NavigableSet<String> getTableNames() {
+  public final NavigableSet<String> getTableNames() {
     final ImmutableSortedSet.Builder<String> builder =
-        new ImmutableSortedSet.Builder<String>(COMPARATOR);
+        new ImmutableSortedSet.Builder<>(COMPARATOR);
     // Add explicit tables, case-sensitive.
     builder.addAll(tableMap.keySet());
     // Add implicit tables, case-sensitive.
-    builder.addAll(implicitTableCache.get(System.currentTimeMillis()));
+    addImplicitTableToBuilder(builder);
     return Compatible.INSTANCE.navigableSet(builder.build());
   }
 
   /** Returns a collection of all functions, explicit and implicit, with a 
given
    * name. Never null. */
-  public Collection<Function> getFunctions(String name, boolean caseSensitive) 
{
+  public final Collection<Function> getFunctions(String name, boolean 
caseSensitive) {
     final ImmutableList.Builder<Function> builder = ImmutableList.builder();
 
     if (caseSensitive) {
@@ -400,34 +347,28 @@ public class CalciteSchema {
         }
       }
       // Add implicit functions, case-insensitive.
-      for (String name2
-          : find(implicitFunctionCache.get(System.currentTimeMillis()), name)) 
{
-        final Collection<Function> functions = schema.getFunctions(name2);
-        if (functions != null) {
-          builder.addAll(functions);
-        }
-      }
+      addImplicitFunctionToBuilder(builder);
     }
     return builder.build();
   }
 
   /** Returns the list of function names in this schema, both implicit and
    * explicit, never null. */
-  public NavigableSet<String> getFunctionNames() {
+  public final NavigableSet<String> getFunctionNames() {
     final ImmutableSortedSet.Builder<String> builder =
-        new ImmutableSortedSet.Builder<String>(COMPARATOR);
+        new ImmutableSortedSet.Builder<>(COMPARATOR);
     // Add explicit functions, case-sensitive.
     builder.addAll(functionMap.keySet());
     // Add implicit functions, case-sensitive.
-    builder.addAll(implicitFunctionCache.get(System.currentTimeMillis()));
+    addImplicitFuncNamesToBuilder(builder);
     return Compatible.INSTANCE.navigableSet(builder.build());
   }
 
   /** Returns tables derived from explicit and implicit functions
    * that take zero parameters. */
-  public NavigableMap<String, Table> getTablesBasedOnNullaryFunctions() {
+  public final NavigableMap<String, Table> getTablesBasedOnNullaryFunctions() {
     ImmutableSortedMap.Builder<String, Table> builder =
-        new ImmutableSortedMap.Builder<String, Table>(COMPARATOR);
+        new ImmutableSortedMap.Builder<>(COMPARATOR);
     for (Map.Entry<String, FunctionEntry> s : nullaryFunctionMap.entrySet()) {
       final Function function = s.getValue().getFunction();
       if (function instanceof TableMacro) {
@@ -436,21 +377,14 @@ public class CalciteSchema {
         builder.put(s.getKey(), table);
       }
     }
-    for (String s : implicitFunctionCache.get(System.currentTimeMillis())) {
-      for (Function function : schema.getFunctions(s)) {
-        if (function instanceof TableMacro
-            && function.getParameters().isEmpty()) {
-          final Table table = ((TableMacro) 
function).apply(ImmutableList.of());
-          builder.put(s, table);
-        }
-      }
-    }
+    // add tables derived from implicit functions
+    addImplicitTablesBasedOnNullaryFunctionsToBuilder(builder);
     return Compatible.INSTANCE.navigableMap(builder.build());
   }
 
   /** Returns a tables derived from explicit and implicit functions
    * that take zero parameters. */
-  public TableEntry getTableBasedOnNullaryFunction(String tableName,
+  public final TableEntry getTableBasedOnNullaryFunction(String tableName,
       boolean caseSensitive) {
     if (caseSensitive) {
       final FunctionEntry functionEntry = nullaryFunctionMap.get(tableName);
@@ -479,25 +413,15 @@ public class CalciteSchema {
           return tableEntry(tableName, table);
         }
       }
-      final NavigableSet<String> set =
-          implicitFunctionCache.get(System.currentTimeMillis());
-      for (String s : find(set, tableName)) {
-        for (Function function : schema.getFunctions(s)) {
-          if (function instanceof TableMacro
-              && function.getParameters().isEmpty()) {
-            final Table table =
-                ((TableMacro) function).apply(ImmutableList.of());
-            return tableEntry(tableName, table);
-          }
-        }
-      }
+      TableEntry tableEntry =
+          getImplicitTableBasedOnNullaryFunction(tableName, false);
     }
     return null;
   }
 
   /** Returns a subset of a map whose keys match the given string
    * case-insensitively. */
-  private static <V> NavigableMap<String, V> find(NavigableMap<String, V> map,
+  protected static <V> NavigableMap<String, V> find(NavigableMap<String, V> 
map,
       String s) {
     assert map.comparator() == COMPARATOR;
     return map.subMap(s.toUpperCase(), true, s.toLowerCase(), true);
@@ -505,11 +429,43 @@ public class CalciteSchema {
 
   /** Returns a subset of a set whose values match the given string
    * case-insensitively. */
-  private static Iterable<String> find(NavigableSet<String> set, String name) {
+  protected static Iterable<String> find(NavigableSet<String> set, String 
name) {
     assert set.comparator() == COMPARATOR;
     return set.subSet(name.toUpperCase(), true, name.toLowerCase(), true);
   }
 
+  /** Creates a root schema.
+   *
+   * <p>When <code>addMetadataSchema</code> argument is true adds a "metadata"
+   * schema containing definitions of tables, columns etc. to root schema.
+   * By default, creates a {@link CachingCalciteSchema}.
+   */
+  public static CalciteSchema createRootSchema(boolean addMetadataSchema) {
+    return createRootSchema(addMetadataSchema, true);
+  }
+
+  /** Creates a root schema.
+   *
+   * @param addMetadataSchema Whether to add a "metadata" schema containing
+   *              definitions of tables, columns etc.
+   * @param cache If true create {@link CachingCalciteSchema};
+   *                if false create {@link SimpleCalciteSchema}
+   */
+  public static CalciteSchema createRootSchema(boolean addMetadataSchema,
+      boolean cache) {
+    CalciteSchema rootSchema;
+    final Schema schema = new CalciteConnectionImpl.RootSchema();
+    if (cache) {
+      rootSchema = new CachingCalciteSchema(null, schema, "");
+    } else {
+      rootSchema = new SimpleCalciteSchema(null, schema, "");
+    }
+    if (addMetadataSchema) {
+      rootSchema.add("metadata", MetadataSchema.INSTANCE);
+    }
+    return rootSchema;
+  }
+
   /**
    * Entry in a schema, such as a table or sub-schema.
    *
@@ -596,7 +552,7 @@ public class CalciteSchema {
     }
 
     public boolean isCacheEnabled() {
-      return CalciteSchema.this.cache;
+      return CalciteSchema.this.isCacheEnabled();
     }
 
     public boolean contentsHaveChangedSince(long lastCheck, long now) {
@@ -741,74 +697,6 @@ public class CalciteSchema {
     }
   }
 
-  /** Strategy for caching the value of an object and re-creating it if its
-   * value is out of date as of a given timestamp.
-   *
-   * @param <T> Type of cached object
-   */
-  private interface Cached<T> {
-    /** Returns the value; uses cached value if valid. */
-    T get(long now);
-
-    /** Creates a new value. */
-    T build();
-
-    /** Called when CalciteSchema caching is enabled or disabled. */
-    void enable(long now, boolean enabled);
-  }
-
-  /** Implementation of {@link CalciteSchema.Cached}
-   * that drives from {@link CalciteSchema#cache}. */
-  private abstract class AbstractCached<T> implements Cached<T> {
-    T t;
-    long checked = Long.MIN_VALUE;
-
-    public T get(long now) {
-      if (!CalciteSchema.this.cache) {
-        return build();
-      }
-      if (checked == Long.MIN_VALUE
-          || schema.contentsHaveChangedSince(checked, now)) {
-        t = build();
-      }
-      checked = now;
-      return t;
-    }
-
-    public void enable(long now, boolean enabled) {
-      if (!enabled) {
-        t = null;
-      }
-      checked = Long.MIN_VALUE;
-    }
-  }
-
-  /** Information about the implicit sub-schemas of an {@link CalciteSchema}. 
*/
-  private static class SubSchemaCache {
-    /** The names of sub-schemas returned from the {@link Schema} SPI. */
-    final NavigableSet<String> names;
-    /** Cached {@link CalciteSchema} wrappers. It is
-     * worth caching them because they contain maps of their own sub-objects. 
*/
-    final LoadingCache<String, CalciteSchema> cache;
-
-    private SubSchemaCache(final CalciteSchema calciteSchema,
-        NavigableSet<String> names) {
-      this.names = names;
-      this.cache = CacheBuilder.newBuilder().build(
-          new CacheLoader<String, CalciteSchema>() {
-            @SuppressWarnings("NullableProblems")
-            @Override public CalciteSchema load(String schemaName) {
-              final Schema subSchema =
-                  calciteSchema.schema.getSubSchema(schemaName);
-              if (subSchema == null) {
-                throw new RuntimeException("sub-schema " + schemaName
-                    + " not found");
-              }
-              return new CalciteSchema(calciteSchema, subSchema, schemaName);
-            }
-          });
-    }
-  }
 }
 
 // End CalciteSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/Driver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/Driver.java 
b/core/src/main/java/org/apache/calcite/jdbc/Driver.java
index 3a29c92..2014e9f 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/Driver.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/Driver.java
@@ -116,7 +116,7 @@ public class Driver extends UnregisteredDriver {
   }
 
   /** Creates an internal connection. */
-  CalciteConnection connect(CalciteRootSchema rootSchema,
+  CalciteConnection connect(CalciteSchema rootSchema,
       JavaTypeFactory typeFactory) {
     return (CalciteConnection) ((CalciteFactory) factory)
         .newConnection(this, factory, CONNECT_STRING_PREFIX, new Properties(),

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java 
b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
new file mode 100644
index 0000000..40a8870
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
@@ -0,0 +1,141 @@
+/*
+ * 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.jdbc;
+
+import org.apache.calcite.schema.Function;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.TableMacro;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.ImmutableSortedSet;
+
+/**
+ * A concrete implementation of {@link org.apache.calcite.jdbc.CalciteSchema}
+ * that maintains minimal state.
+ */
+class SimpleCalciteSchema extends CalciteSchema {
+  /** Creates a CachingCalciteSchema.
+   *
+   * <p>Use {@link CalciteSchema#createRootSchema(boolean)}
+   * or {@link #add(String, Schema)}. */
+  SimpleCalciteSchema(CalciteSchema parent, Schema schema, String name) {
+    super(parent, schema, name);
+  }
+
+  public void setCache(boolean cache) {
+    throw new UnsupportedOperationException();
+  }
+
+  public CalciteSchema add(String name, Schema schema) {
+    final CalciteSchema calciteSchema =
+        new SimpleCalciteSchema(this, schema, name);
+    subSchemaMap.put(name, calciteSchema);
+    return calciteSchema;
+  }
+
+  protected CalciteSchema getImplicitSubSchema(String schemaName,
+      boolean caseSensitive) {
+    // Check implicit schemas.
+    Schema s = schema.getSubSchema(schemaName);
+    if (s != null) {
+      return new SimpleCalciteSchema(this, s, schemaName);
+    }
+    return null;
+  }
+
+  protected TableEntry getImplicitTable(String tableName,
+      boolean caseSensitive) {
+    // Check implicit tables.
+    Table table = schema.getTable(tableName);
+    if (table != null) {
+      return tableEntry(tableName, table);
+    }
+    return null;
+  }
+
+  protected void addImplicitSubSchemaToBuilder(
+      ImmutableSortedMap.Builder<String, CalciteSchema> builder) {
+    ImmutableSortedMap<String, CalciteSchema> explicitSubSchemas = 
builder.build();
+    for (String schemaName : schema.getSubSchemaNames()) {
+      if (explicitSubSchemas.containsKey(schemaName)) {
+        // explicit subschema wins.
+        continue;
+      }
+      Schema s = schema.getSubSchema(schemaName);
+      if (s != null) {
+        CalciteSchema calciteSchema = new SimpleCalciteSchema(this, s, 
schemaName);
+        builder.put(schemaName, calciteSchema);
+      }
+    }
+  }
+
+  protected void addImplicitTableToBuilder(ImmutableSortedSet.Builder<String> 
builder) {
+    builder.addAll(schema.getTableNames());
+  }
+
+  protected void addImplicitFunctionToBuilder(ImmutableList.Builder<Function> 
builder) {
+    for (String functionName : schema.getFunctionNames()) {
+      builder.addAll(schema.getFunctions(functionName));
+    }
+  }
+
+  protected void 
addImplicitFuncNamesToBuilder(ImmutableSortedSet.Builder<String> builder) {
+    builder.addAll(schema.getFunctionNames());
+  }
+
+  protected void addImplicitTablesBasedOnNullaryFunctionsToBuilder(
+      ImmutableSortedMap.Builder<String, Table> builder) {
+    ImmutableSortedMap<String, Table> explicitTables = builder.build();
+
+    for (String s : schema.getFunctionNames()) {
+      // explicit table wins.
+      if (explicitTables.containsKey(s)) {
+        continue;
+      }
+      for (Function function : schema.getFunctions(s)) {
+        if (function instanceof TableMacro
+            && function.getParameters().isEmpty()) {
+          final Table table = ((TableMacro) 
function).apply(ImmutableList.of());
+          builder.put(s, table);
+        }
+      }
+    }
+  }
+
+  protected TableEntry getImplicitTableBasedOnNullaryFunction(String tableName,
+      boolean caseSensitive) {
+    for (String s : schema.getFunctionNames()) {
+      for (Function function : schema.getFunctions(s)) {
+        if (function instanceof TableMacro
+            && function.getParameters().isEmpty()) {
+          final Table table = ((TableMacro) 
function).apply(ImmutableList.of());
+          return tableEntry(tableName, table);
+        }
+      }
+    }
+    return null;
+  }
+
+  protected boolean isCacheEnabled() {
+    return false;
+  }
+
+}
+
+// End SimpleCalciteSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/materialize/Lattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattice.java 
b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
index ee7fef6..cc11625 100644
--- a/core/src/main/java/org/apache/calcite/materialize/Lattice.java
+++ b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
@@ -18,7 +18,6 @@ package org.apache.calcite.materialize;
 
 import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.jdbc.CalcitePrepare;
-import org.apache.calcite.jdbc.CalciteRootSchema;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
@@ -86,7 +85,7 @@ public class Lattice {
         }
       };
 
-  public final CalciteRootSchema rootSchema;
+  public final CalciteSchema rootSchema;
   public final ImmutableList<Node> nodes;
   public final ImmutableList<Column> columns;
   public final boolean auto;
@@ -112,7 +111,7 @@ public class Lattice {
         }
       };
 
-  private Lattice(CalciteRootSchema rootSchema, ImmutableList<Node> nodes,
+  private Lattice(CalciteSchema rootSchema, ImmutableList<Node> nodes,
       boolean auto, boolean algorithm, long algorithmMaxMillis,
       LatticeStatisticProvider statisticProvider, Double rowCountEstimate,
       ImmutableList<Column> columns, ImmutableList<Measure> defaultMeasures,
@@ -568,7 +567,7 @@ public class Lattice {
         ImmutableList.builder();
     private final ImmutableList.Builder<Tile> tileListBuilder =
         ImmutableList.builder();
-    private final CalciteRootSchema rootSchema;
+    private final CalciteSchema rootSchema;
     private boolean algorithm = false;
     private long algorithmMaxMillis = -1;
     private boolean auto = true;
@@ -576,7 +575,8 @@ public class Lattice {
     private String statisticProvider;
 
     public Builder(CalciteSchema schema, String sql) {
-      this.rootSchema = schema.root();
+      this.rootSchema = Preconditions.checkNotNull(schema.root());
+      Preconditions.checkArgument(rootSchema.isRoot(), "must be root schema");
       CalcitePrepare.ConvertResult parsed =
           Schemas.convert(MaterializedViewTable.MATERIALIZATION_CONNECTION,
               schema, schema.path(null), sql);
@@ -695,6 +695,7 @@ public class Lattice {
               ? AvaticaUtils.instantiatePlugin(LatticeStatisticProvider.class,
                   this.statisticProvider)
               : Lattices.CACHED_SQL;
+      Preconditions.checkArgument(rootSchema.isRoot(), "must be root schema");
       return new Lattice(rootSchema, ImmutableList.copyOf(nodes), auto,
           algorithm, algorithmMaxMillis, statisticProvider, rowCountEstimate,
           columns, defaultMeasureListBuilder.build(), tileListBuilder.build());

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java 
b/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java
index ede840d..f70a08b 100644
--- 
a/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java
+++ 
b/core/src/main/java/org/apache/calcite/materialize/MaterializationActor.java
@@ -16,10 +16,10 @@
  */
 package org.apache.calcite.materialize;
 
-import org.apache.calcite.jdbc.CalciteRootSchema;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.rel.type.RelDataType;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
@@ -50,7 +50,7 @@ class MaterializationActor {
    * same results as executing the query. */
   static class Materialization {
     final MaterializationKey key;
-    final CalciteRootSchema rootSchema;
+    final CalciteSchema rootSchema;
     CalciteSchema.TableEntry materializedTable;
     final String sql;
     final RelDataType rowType;
@@ -67,12 +67,13 @@ class MaterializationActor {
      * @param rowType Row type
      */
     Materialization(MaterializationKey key,
-        CalciteRootSchema rootSchema,
+        CalciteSchema rootSchema,
         CalciteSchema.TableEntry materializedTable,
         String sql,
         RelDataType rowType) {
       this.key = key;
-      this.rootSchema = rootSchema;
+      this.rootSchema = Preconditions.checkNotNull(rootSchema);
+      Preconditions.checkArgument(rootSchema.isRoot(), "must be root schema");
       this.materializedTable = materializedTable; // may be null
       this.sql = sql;
       this.rowType = rowType;

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/schema/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java 
b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index 639e852..6c8ab1d 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -23,7 +23,6 @@ import org.apache.calcite.config.CalciteConnectionConfigImpl;
 import org.apache.calcite.config.CalciteConnectionProperty;
 import org.apache.calcite.jdbc.CalciteConnection;
 import org.apache.calcite.jdbc.CalcitePrepare;
-import org.apache.calcite.jdbc.CalciteRootSchema;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.QueryProvider;
@@ -389,7 +388,7 @@ public final class Schemas {
         return typeFactory;
       }
 
-      public CalciteRootSchema getRootSchema() {
+      public CalciteSchema getRootSchema() {
         return schema.root();
       }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/main/java/org/apache/calcite/tools/Frameworks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Frameworks.java 
b/core/src/main/java/org/apache/calcite/tools/Frameworks.java
index f3ebe12..dffac61 100644
--- a/core/src/main/java/org/apache/calcite/tools/Frameworks.java
+++ b/core/src/main/java/org/apache/calcite/tools/Frameworks.java
@@ -76,7 +76,6 @@ public class Frameworks {
    * statement. */
   public abstract static class PrepareAction<R> {
     private final FrameworkConfig config;
-
     public PrepareAction() {
       this.config = newConfigBuilder() //
           .defaultSchema(Frameworks.createRootSchema(true)).build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
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 3c0c77c..65d024c 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6436,6 +6436,57 @@ public class JdbcTest {
     }
   }
 
+  @Test public void testExplicitImplicitSchemaSameName() throws Exception {
+    final SchemaPlus rootSchema = CalciteSchema.createRootSchema(false).plus();
+
+    // create schema "/a"
+    final Map<String, Schema> aSubSchemaMap = new HashMap<>();
+    final SchemaPlus aSchema = rootSchema.add("a",
+        new AbstractSchema() {
+          @Override protected Map<String, Schema> getSubSchemaMap() {
+            return aSubSchemaMap;
+          }
+        });
+
+    // add explicit schema "/a/b".
+    aSchema.add("b", new AbstractSchema());
+
+    // add implicit schema "/a/b"
+    aSubSchemaMap.put("b", new AbstractSchema());
+
+    aSchema.setCacheEnabled(true);
+
+    // explicit should win implicit.
+    assertThat(aSchema.getSubSchemaNames().size(), is(1));
+  }
+
+  @Test public void testSimpleCalciteSchema() throws Exception {
+    final SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, 
false).plus();
+
+    // create schema "/a"
+    final Map<String, Schema> aSubSchemaMap = new HashMap<>();
+    final SchemaPlus aSchema = rootSchema.add("a",
+        new AbstractSchema() {
+          @Override protected Map<String, Schema> getSubSchemaMap() {
+            return aSubSchemaMap;
+          }
+        });
+
+    // add explicit schema "/a/b".
+    aSchema.add("b", new AbstractSchema());
+
+    // add implicit schema "/a/c"
+    aSubSchemaMap.put("c", new AbstractSchema());
+
+    assertThat(aSchema.getSubSchema("c"), notNullValue());
+    assertThat(aSchema.getSubSchema("b"), notNullValue());
+
+    // add implicit schema "/a/b"
+    aSubSchemaMap.put("b", new AbstractSchema());
+    // explicit should win implicit.
+    assertThat(aSchema.getSubSchemaNames().size(), is(2));
+  }
+
   @Test public void testSchemaCaching() throws Exception {
     final Connection connection =
         CalciteAssert.that(CalciteAssert.Config.JDBC_FOODMART).connect();

http://git-wip-us.apache.org/repos/asf/calcite/blob/ac8d04ed/site/_docs/howto.md
----------------------------------------------------------------------
diff --git a/site/_docs/howto.md b/site/_docs/howto.md
index af33cdd..aff04b2 100644
--- a/site/_docs/howto.md
+++ b/site/_docs/howto.md
@@ -318,7 +318,7 @@ New adapters can be created by implementing 
`CalcitePrepare.Context`:
 {% highlight java %}
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.jdbc.CalcitePrepare;
-import org.apache.calcite.jdbc.CalciteRootSchema;
+import org.apache.calcite.jdbc.CalciteSchema;
 
 public class AdapterContext implements CalcitePrepare.Context {
   @Override
@@ -328,7 +328,7 @@ public class AdapterContext implements 
CalcitePrepare.Context {
   }
 
   @Override
-  public CalciteRootSchema getRootSchema() {
+  public CalciteSchema getRootSchema() {
     // adapter implementation
     return rootSchema;
   }

Reply via email to