calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [1/5] calcite git commit: [CALCITE-1563] In case-insensitive connection, non-existent tables use alphabetically preceding table
Date Fri, 06 Jan 2017 20:52:45 GMT
Repository: calcite
Updated Branches:
  refs/heads/master 8dd935ee5 -> 2b9663752


[CALCITE-1563] In case-insensitive connection, non-existent tables use alphabetically preceding
table


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

Branch: refs/heads/master
Commit: 40e5b8847b6a2966b024191fb5f65db642d17519
Parents: 91102ba
Author: Julian Hyde <jhyde@apache.org>
Authored: Wed Jan 4 18:36:30 2017 -0800
Committer: Julian Hyde <jhyde@apache.org>
Committed: Fri Jan 6 10:45:52 2017 -0800

----------------------------------------------------------------------
 .../calcite/jdbc/CachingCalciteSchema.java      | 115 +++++-------
 .../org/apache/calcite/jdbc/CalciteSchema.java  | 184 ++++++-------------
 .../calcite/jdbc/SimpleCalciteSchema.java       |   5 +-
 .../java/org/apache/calcite/util/NameMap.java   |  81 ++++++++
 .../org/apache/calcite/util/NameMultimap.java   | 101 ++++++++++
 .../java/org/apache/calcite/util/NameSet.java   | 104 +++++++++++
 .../java/org/apache/calcite/test/JdbcTest.java  |  15 ++
 .../java/org/apache/calcite/util/UtilTest.java  | 123 +++++++++++++
 8 files changed, 528 insertions(+), 200 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/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
index 8defef4..7985bf2 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
@@ -20,7 +20,7 @@ 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 org.apache.calcite.util.NameSet;
 
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -30,7 +30,7 @@ import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
 
 import java.util.Collection;
-import java.util.NavigableSet;
+import java.util.Set;
 
 /**
  * Concrete implementation of {@link CalciteSchema} that caches tables,
@@ -38,8 +38,8 @@ import java.util.NavigableSet;
  */
 class CachingCalciteSchema extends CalciteSchema {
   private final Cached<SubSchemaCache> implicitSubSchemaCache;
-  private final Cached<NavigableSet<String>> implicitTableCache;
-  private final Cached<NavigableSet<String>> implicitFunctionCache;
+  private final Cached<NameSet> implicitTableCache;
+  private final Cached<NameSet> implicitFunctionCache;
 
   private boolean cache = true;
 
@@ -50,25 +50,21 @@ class CachingCalciteSchema extends CalciteSchema {
         new AbstractCached<SubSchemaCache>() {
           public SubSchemaCache build() {
             return new SubSchemaCache(CachingCalciteSchema.this,
-                Compatible.INSTANCE.navigableSet(
-                    ImmutableSortedSet.copyOf(COMPARATOR,
-                        CachingCalciteSchema.this.schema.getSubSchemaNames())));
+                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()));
+        new AbstractCached<NameSet>() {
+          public NameSet build() {
+            return NameSet.immutableCopyOf(
+                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()));
+        new AbstractCached<NameSet>() {
+          public NameSet build() {
+            return NameSet.immutableCopyOf(
+                CachingCalciteSchema.this.schema.getFunctionNames());
           }
         };
   }
@@ -90,22 +86,13 @@ class CachingCalciteSchema extends CalciteSchema {
 
   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);
-      }
+    final long now = System.currentTimeMillis();
+    final SubSchemaCache subSchemaCache =
+        implicitSubSchemaCache.get(now);
+    //noinspection LoopStatementThatDoesntLoop
+    for (String schemaName2
+        : subSchemaCache.names.range(schemaName, caseSensitive)) {
+      return subSchemaCache.cache.getUnchecked(schemaName2);
     }
     return null;
   }
@@ -120,26 +107,13 @@ class CachingCalciteSchema extends 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);
-        }
+    final long now = System.currentTimeMillis();
+    final NameSet implicitTableNames = implicitTableCache.get(now);
+    for (String tableName2
+        : implicitTableNames.range(tableName, caseSensitive)) {
+      final Table table = schema.getTable(tableName2);
+      if (table != null) {
+        return tableEntry(tableName2, table);
       }
     }
     return null;
@@ -150,7 +124,7 @@ class CachingCalciteSchema extends CalciteSchema {
     ImmutableSortedMap<String, CalciteSchema> explicitSubSchemas = builder.build();
     final long now = System.currentTimeMillis();
     final SubSchemaCache subSchemaCache = implicitSubSchemaCache.get(now);
-    for (String name : subSchemaCache.names) {
+    for (String name : subSchemaCache.names.iterable()) {
       if (explicitSubSchemas.containsKey(name)) {
         // explicit sub-schema wins.
         continue;
@@ -162,14 +136,17 @@ class CachingCalciteSchema extends CalciteSchema {
   protected void addImplicitTableToBuilder(
       ImmutableSortedSet.Builder<String> builder) {
     // Add implicit tables, case-sensitive.
-    builder.addAll(implicitTableCache.get(System.currentTimeMillis()));
+    final long now = System.currentTimeMillis();
+    final NameSet set = implicitTableCache.get(now);
+    builder.addAll(set.iterable());
   }
 
-  protected void addImplicitFunctionToBuilder(
-      ImmutableList.Builder<Function> builder) {
+  protected void addImplicitFunctionsToBuilder(
+      ImmutableList.Builder<Function> builder, boolean caseSensitive) {
     // Add implicit functions, case-insensitive.
-    for (String name2
-        : find(implicitFunctionCache.get(System.currentTimeMillis()), name)) {
+    final long now = System.currentTimeMillis();
+    final NameSet set = implicitFunctionCache.get(now);
+    for (String name2 : set.range(name, caseSensitive)) {
       final Collection<Function> functions = schema.getFunctions(name2);
       if (functions != null) {
         builder.addAll(functions);
@@ -180,14 +157,18 @@ class CachingCalciteSchema extends CalciteSchema {
   protected void addImplicitFuncNamesToBuilder(
       ImmutableSortedSet.Builder<String> builder) {
     // Add implicit functions, case-sensitive.
-    builder.addAll(implicitFunctionCache.get(System.currentTimeMillis()));
+    final long now = System.currentTimeMillis();
+    final NameSet set = implicitFunctionCache.get(now);
+    builder.addAll(set.iterable());
   }
 
   protected void addImplicitTablesBasedOnNullaryFunctionsToBuilder(
       ImmutableSortedMap.Builder<String, Table> builder) {
     ImmutableSortedMap<String, Table> explicitTables = builder.build();
 
-    for (String s : implicitFunctionCache.get(System.currentTimeMillis())) {
+    final long now = System.currentTimeMillis();
+    final NameSet set = implicitFunctionCache.get(now);
+    for (String s : set.iterable()) {
       // explicit table wins.
       if (explicitTables.containsKey(s)) {
         continue;
@@ -204,9 +185,9 @@ class CachingCalciteSchema extends CalciteSchema {
 
   protected TableEntry getImplicitTableBasedOnNullaryFunction(String tableName,
       boolean caseSensitive) {
-    final NavigableSet<String> set =
-        implicitFunctionCache.get(System.currentTimeMillis());
-    for (String s : find(set, tableName)) {
+    final long now = System.currentTimeMillis();
+    final NameSet set = implicitFunctionCache.get(now);
+    for (String s : set.range(tableName, caseSensitive)) {
       for (Function function : schema.getFunctions(s)) {
         if (function instanceof TableMacro
             && function.getParameters().isEmpty()) {
@@ -264,14 +245,14 @@ class CachingCalciteSchema extends CalciteSchema {
   /** 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;
+    final NameSet 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;
+        Set<String> names) {
+      this.names = NameSet.immutableCopyOf(names);
       this.cache = CacheBuilder.newBuilder().build(
           new CacheLoader<String, CalciteSchema>() {
             @SuppressWarnings("NullableProblems")

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/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 a1f24b0..f1614fd 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
@@ -26,25 +26,24 @@ import org.apache.calcite.schema.TableMacro;
 import org.apache.calcite.schema.impl.MaterializedViewTable;
 import org.apache.calcite.schema.impl.StarTable;
 import org.apache.calcite.util.Compatible;
+import org.apache.calcite.util.NameMap;
+import org.apache.calcite.util.NameMultimap;
+import org.apache.calcite.util.NameSet;
+import org.apache.calcite.util.Pair;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
 import java.util.NavigableSet;
 import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
 
 /**
  * Schema.
@@ -52,39 +51,19 @@ import java.util.TreeSet;
  * <p>Wrapper around user-defined schema used internally.</p>
  */
 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. */
-  protected static final Comparator<String> COMPARATOR =
-      new Comparator<String>() {
-        public int compare(String o1, String o2) {
-          int c = o1.compareToIgnoreCase(o2);
-          if (c == 0) {
-            c = o1.compareTo(o2);
-          }
-          return c;
-        }
-      };
 
   private final CalciteSchema parent;
   public final Schema schema;
   public final String name;
   /** Tables explicitly defined in this schema. Does not include tables in
    *  {@link #schema}. */
-  public final NavigableMap<String, TableEntry> tableMap =
-      new TreeMap<>(COMPARATOR);
-  protected final Multimap<String, FunctionEntry> functionMap =
-      LinkedListMultimap.create();
-  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);
+  protected final NameMap<TableEntry> tableMap = new NameMap<>();
+  protected final NameMultimap<FunctionEntry> functionMap =
+      new NameMultimap<>();
+  protected final NameMap<LatticeEntry> latticeMap = new NameMap<>();
+  protected final NameSet functionNames = new NameSet();
+  protected final NameMap<FunctionEntry> nullaryFunctionMap = new NameMap<>();
+  protected final NameMap<CalciteSchema> subSchemaMap = new NameMap<>();
   private ImmutableList<ImmutableList<String>> path;
 
   CalciteSchema(CalciteSchema parent, Schema schema, String name) {
@@ -120,7 +99,8 @@ public abstract class CalciteSchema {
       ImmutableSortedSet.Builder<String> builder);
 
   /** Adds implicit functions to a builder. */
-  protected abstract void addImplicitFunctionToBuilder(ImmutableList.Builder<Function>
builder);
+  protected abstract void addImplicitFunctionsToBuilder(
+      ImmutableList.Builder<Function> builder, boolean caseSensitive);
 
   /** Adds implicit function names to a builder. */
   protected abstract void addImplicitFuncNamesToBuilder(
@@ -165,7 +145,7 @@ public abstract class CalciteSchema {
   }
 
   private LatticeEntry add(String name, Lattice lattice) {
-    if (latticeMap.containsKey(name)) {
+    if (latticeMap.containsKey(name, false)) {
       throw new RuntimeException("Duplicate lattice '" + name + "'");
     }
     final LatticeEntryImpl entry = new LatticeEntryImpl(this, name, lattice);
@@ -205,19 +185,11 @@ public abstract class CalciteSchema {
 
   public final CalciteSchema getSubSchema(String schemaName,
       boolean caseSensitive) {
-    if (caseSensitive) {
-      // Check explicit schemas, case-sensitive.
-      final CalciteSchema entry = subSchemaMap.get(schemaName);
-      if (entry != null) {
-        return entry;
-      }
-    } else {
-      // Check explicit schemas, case-insensitive.
-      //noinspection LoopStatementThatDoesntLoop
-      for (Map.Entry<String, CalciteSchema> entry
-          : find(subSchemaMap, schemaName).entrySet()) {
-        return entry.getValue();
-      }
+    // Check explicit schemas.
+    //noinspection LoopStatementThatDoesntLoop
+    for (Map.Entry<String, CalciteSchema> entry
+        : subSchemaMap.range(schemaName, caseSensitive).entrySet()) {
+      return entry.getValue();
     }
     return getImplicitSubSchema(schemaName, caseSensitive);
   }
@@ -227,7 +199,7 @@ public abstract class CalciteSchema {
 
   /** Returns a table that materializes the given SQL statement. */
   public final TableEntry getTableBySql(String sql) {
-    for (TableEntry tableEntry : tableMap.values()) {
+    for (TableEntry tableEntry : tableMap.map().values()) {
       if (tableEntry.sqls.contains(sql)) {
         return tableEntry;
       }
@@ -237,21 +209,12 @@ public abstract class CalciteSchema {
 
   /** Returns a table with the given name. Does not look for views. */
   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;
-      }
-    } else {
-      // Check explicit tables, case-insensitive.
-      //noinspection LoopStatementThatDoesntLoop
-      for (Map.Entry<String, TableEntry> entry
-          : find(tableMap, tableName).entrySet()) {
-        return entry.getValue();
-      }
+    // Check explicit tables.
+    //noinspection LoopStatementThatDoesntLoop
+    for (Map.Entry<String, TableEntry> entry
+        : tableMap.range(tableName, caseSensitive).entrySet()) {
+      return entry.getValue();
     }
-
     return getImplicitTable(tableName, caseSensitive);
   }
 
@@ -292,8 +255,8 @@ public abstract class CalciteSchema {
     // 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<>(COMPARATOR);
-    builder.putAll(subSchemaMap);
+        new ImmutableSortedMap.Builder<>(NameSet.COMPARATOR);
+    builder.putAll(subSchemaMap.map());
     addImplicitSubSchemaToBuilder(builder);
     return Compatible.INSTANCE.navigableMap(builder.build());
   }
@@ -302,16 +265,16 @@ public abstract class CalciteSchema {
    *
    * <p>All are explicit (defined using {@link #add(String, Lattice)}). */
   public NavigableMap<String, LatticeEntry> getLatticeMap() {
-    return Compatible.INSTANCE.immutableNavigableMap(latticeMap);
+    return ImmutableSortedMap.copyOf(latticeMap.map());
   }
 
   /** Returns the set of all table names. Includes implicit and explicit tables
    * and functions with zero parameters. */
   public final NavigableSet<String> getTableNames() {
     final ImmutableSortedSet.Builder<String> builder =
-        new ImmutableSortedSet.Builder<>(COMPARATOR);
+        new ImmutableSortedSet.Builder<>(NameSet.COMPARATOR);
     // Add explicit tables, case-sensitive.
-    builder.addAll(tableMap.keySet());
+    builder.addAll(tableMap.map().keySet());
     // Add implicit tables, case-sensitive.
     addImplicitTableToBuilder(builder);
     return Compatible.INSTANCE.navigableSet(builder.build());
@@ -321,34 +284,13 @@ public abstract class CalciteSchema {
    * name. Never null. */
   public final Collection<Function> getFunctions(String name, boolean caseSensitive)
{
     final ImmutableList.Builder<Function> builder = ImmutableList.builder();
-
-    if (caseSensitive) {
-      // Add explicit functions, case-sensitive.
-      final Collection<FunctionEntry> functionEntries = functionMap.get(name);
-      if (functionEntries != null) {
-        for (FunctionEntry functionEntry : functionEntries) {
-          builder.add(functionEntry.getFunction());
-        }
-      }
-      // Add implicit functions, case-sensitive.
-      final Collection<Function> functions = schema.getFunctions(name);
-      if (functions != null) {
-        builder.addAll(functions);
-      }
-    } else {
-      // Add explicit functions, case-insensitive.
-      for (String name2 : find(functionNames, name)) {
-        final Collection<FunctionEntry> functionEntries =
-            functionMap.get(name2);
-        if (functionEntries != null) {
-          for (FunctionEntry functionEntry : functionEntries) {
-            builder.add(functionEntry.getFunction());
-          }
-        }
-      }
-      // Add implicit functions, case-insensitive.
-      addImplicitFunctionToBuilder(builder);
+    // Add explicit functions.
+    for (FunctionEntry functionEntry
+        : Pair.right(functionMap.range(name, caseSensitive))) {
+      builder.add(functionEntry.getFunction());
     }
+    // Add implicit functions.
+    addImplicitFunctionsToBuilder(builder, caseSensitive);
     return builder.build();
   }
 
@@ -356,9 +298,9 @@ public abstract class CalciteSchema {
    * explicit, never null. */
   public final NavigableSet<String> getFunctionNames() {
     final ImmutableSortedSet.Builder<String> builder =
-        new ImmutableSortedSet.Builder<>(COMPARATOR);
+        new ImmutableSortedSet.Builder<>(NameSet.COMPARATOR);
     // Add explicit functions, case-sensitive.
-    builder.addAll(functionMap.keySet());
+    builder.addAll(functionMap.map().keySet());
     // Add implicit functions, case-sensitive.
     addImplicitFuncNamesToBuilder(builder);
     return Compatible.INSTANCE.navigableSet(builder.build());
@@ -368,13 +310,14 @@ public abstract class CalciteSchema {
    * that take zero parameters. */
   public final NavigableMap<String, Table> getTablesBasedOnNullaryFunctions() {
     ImmutableSortedMap.Builder<String, Table> builder =
-        new ImmutableSortedMap.Builder<>(COMPARATOR);
-    for (Map.Entry<String, FunctionEntry> s : nullaryFunctionMap.entrySet()) {
-      final Function function = s.getValue().getFunction();
+        new ImmutableSortedMap.Builder<>(NameSet.COMPARATOR);
+    for (Map.Entry<String, FunctionEntry> entry
+        : nullaryFunctionMap.map().entrySet()) {
+      final Function function = entry.getValue().getFunction();
       if (function instanceof TableMacro) {
         assert function.getParameters().isEmpty();
         final Table table = ((TableMacro) function).apply(ImmutableList.of());
-        builder.put(s.getKey(), table);
+        builder.put(entry.getKey(), table);
       }
     }
     // add tables derived from implicit functions
@@ -386,51 +329,30 @@ public abstract class CalciteSchema {
    * that take zero parameters. */
   public final TableEntry getTableBasedOnNullaryFunction(String tableName,
       boolean caseSensitive) {
-    if (caseSensitive) {
-      final FunctionEntry functionEntry = nullaryFunctionMap.get(tableName);
-      if (functionEntry != null) {
-        final Function function = functionEntry.getFunction();
-        if (function instanceof TableMacro) {
-          assert function.getParameters().isEmpty();
-          final Table table = ((TableMacro) function).apply(ImmutableList.of());
-          return tableEntry(tableName, table);
-        }
-      }
-      for (Function function : schema.getFunctions(tableName)) {
-        if (function instanceof TableMacro
-            && function.getParameters().isEmpty()) {
-          final Table table = ((TableMacro) function).apply(ImmutableList.of());
-          return tableEntry(tableName, table);
-        }
-      }
-    } else {
-      for (Map.Entry<String, FunctionEntry> entry
-          : find(nullaryFunctionMap, tableName).entrySet()) {
-        final Function function = entry.getValue().getFunction();
-        if (function instanceof TableMacro) {
-          assert function.getParameters().isEmpty();
-          final Table table = ((TableMacro) function).apply(ImmutableList.of());
-          return tableEntry(tableName, table);
-        }
+    for (Map.Entry<String, FunctionEntry> entry
+        : nullaryFunctionMap.range(tableName, caseSensitive).entrySet()) {
+      final Function function = entry.getValue().getFunction();
+      if (function instanceof TableMacro) {
+        assert function.getParameters().isEmpty();
+        final Table table = ((TableMacro) function).apply(ImmutableList.of());
+        return tableEntry(tableName, table);
       }
-      TableEntry tableEntry =
-          getImplicitTableBasedOnNullaryFunction(tableName, false);
     }
-    return null;
+    return getImplicitTableBasedOnNullaryFunction(tableName, caseSensitive);
   }
 
   /** Returns a subset of a map whose keys match the given string
    * case-insensitively. */
   protected static <V> NavigableMap<String, V> find(NavigableMap<String, V>
map,
       String s) {
-    assert map.comparator() == COMPARATOR;
+    assert map.comparator() == NameSet.COMPARATOR;
     return map.subMap(s.toUpperCase(), true, s.toLowerCase(), true);
   }
 
   /** Returns a subset of a set whose values match the given string
    * case-insensitively. */
   protected static Iterable<String> find(NavigableSet<String> set, String name)
{
-    assert set.comparator() == COMPARATOR;
+    assert set.comparator() == NameSet.COMPARATOR;
     return set.subSet(name.toUpperCase(), true, name.toLowerCase(), true);
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/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
index 40a8870..db97b14 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/SimpleCalciteSchema.java
@@ -30,7 +30,7 @@ import com.google.common.collect.ImmutableSortedSet;
  * that maintains minimal state.
  */
 class SimpleCalciteSchema extends CalciteSchema {
-  /** Creates a CachingCalciteSchema.
+  /** Creates a SimpleCalciteSchema.
    *
    * <p>Use {@link CalciteSchema#createRootSchema(boolean)}
    * or {@link #add(String, Schema)}. */
@@ -89,7 +89,8 @@ class SimpleCalciteSchema extends CalciteSchema {
     builder.addAll(schema.getTableNames());
   }
 
-  protected void addImplicitFunctionToBuilder(ImmutableList.Builder<Function> builder)
{
+  protected void addImplicitFunctionsToBuilder(
+      ImmutableList.Builder<Function> builder, boolean caseSensitive) {
     for (String functionName : schema.getFunctionNames()) {
       builder.addAll(schema.getFunctions(functionName));
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/core/src/main/java/org/apache/calcite/util/NameMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMap.java b/core/src/main/java/org/apache/calcite/util/NameMap.java
new file mode 100644
index 0000000..a0c69bc
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/NameMap.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import com.google.common.collect.ImmutableSortedMap;
+
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import static org.apache.calcite.util.NameSet.COMPARATOR;
+
+/** Map whose keys are names and can be accessed with and without case
+ * sensitivity.
+ *
+ * @param <V> Value type */
+public class NameMap<V> {
+  private final NavigableMap<String, V> map;
+
+  /** Creates a NameSet based on an existing set. */
+  private NameMap(NavigableMap<String, V> map) {
+    this.map = map;
+    assert this.map.comparator() == COMPARATOR;
+  }
+
+  /** Creates a NameMap, initially empty. */
+  public NameMap() {
+    this(new TreeMap<String, V>(COMPARATOR));
+  }
+
+  /** Creates a NameMap that is an immutable copy of a given map. */
+  public static <V> NameMap immutableCopyOf(Map<String, V> names) {
+    return new NameMap<>(ImmutableSortedMap.copyOf(names, COMPARATOR));
+  }
+
+  public void put(String name, V v) {
+    map.put(name, v);
+  }
+
+  /** Returns a map containing all the entries in the map that match the given
+   * name. If case-sensitive, that map will have 0 or 1 elements; if
+   * case-insensitive, it may have 0 or more. */
+  public NavigableMap<String, V> range(String name, boolean caseSensitive) {
+    if (caseSensitive) {
+      if (map.containsKey(name)) {
+        return ImmutableSortedMap.of(name, map.get(name));
+      } else {
+        return ImmutableSortedMap.of();
+      }
+    } else {
+      return map.subMap(name.toUpperCase(), true, name.toLowerCase(), true);
+    }
+  }
+
+  /** Returns whether this map contains a given key, with a given
+   * case-sensitivity. */
+  public boolean containsKey(String name, boolean caseSensitive) {
+    return !range(name, caseSensitive).isEmpty();
+  }
+
+  /** Returns the underlying map. */
+  public NavigableMap<String, V> map() {
+    return map;
+  }
+}
+
+// End NameMap.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/core/src/main/java/org/apache/calcite/util/NameMultimap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMultimap.java b/core/src/main/java/org/apache/calcite/util/NameMultimap.java
new file mode 100644
index 0000000..1b3734a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/NameMultimap.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.util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import static org.apache.calcite.util.NameSet.COMPARATOR;
+
+/** Multimap whose keys are names and can be accessed with and without case
+ * sensitivity.
+ *
+ * @param <V> Value type */
+public class NameMultimap<V> {
+  private final NavigableMap<String, List<V>> map;
+
+  /** Creates a NameMultimap based on an existing map. */
+  private NameMultimap(NavigableMap<String, List<V>> map) {
+    this.map = map;
+    assert this.map.comparator() == COMPARATOR;
+  }
+
+  /** Creates a NameMultimap, initially empty. */
+  public NameMultimap() {
+    this(new TreeMap<String, List<V>>(COMPARATOR));
+  }
+
+  /** Adds an entry to this multimap. */
+  public void put(String name, V v) {
+    List<V> list = map.get(name);
+    if (list == null) {
+      list = new ArrayList<>();
+      map.put(name, list);
+    }
+    list.add(v);
+  }
+
+  /** Returns a map containing all the entries in this multimap that match the
+   * given name. */
+  public Collection<Map.Entry<String, V>> range(String name,
+      boolean caseSensitive) {
+    if (caseSensitive) {
+      final List<V> list = map.get(name);
+      if (list != null && !list.isEmpty()) {
+        final ImmutableList.Builder<Map.Entry<String, V>> builder =
+            ImmutableList.builder();
+        for (V v : list) {
+          builder.add(Pair.of(name, v));
+        }
+        return builder.build();
+      } else {
+        return ImmutableList.of();
+      }
+    } else {
+      final ImmutableList.Builder<Map.Entry<String, V>> builder =
+          ImmutableList.builder();
+      NavigableMap<String, List<V>> m =
+          map.subMap(name.toUpperCase(), true, name.toLowerCase(), true);
+      for (Map.Entry<String, List<V>> entry : m.entrySet()) {
+        for (V v : entry.getValue()) {
+          builder.add(Pair.of(entry.getKey(), v));
+        }
+      }
+      return builder.build();
+    }
+  }
+
+  /** Returns whether this map contains a given key, with a given
+   * case-sensitivity. */
+  public boolean containsKey(String name, boolean caseSensitive) {
+    return !range(name, caseSensitive).isEmpty();
+  }
+
+  /** Returns the underlying map.
+   * Its size is the number of keys, not the number of values. */
+  public NavigableMap<String, List<V>> map() {
+    return map;
+  }
+}
+
+// End NameMultimap.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/core/src/main/java/org/apache/calcite/util/NameSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameSet.java b/core/src/main/java/org/apache/calcite/util/NameSet.java
new file mode 100644
index 0000000..ccdd5d5
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/NameSet.java
@@ -0,0 +1,104 @@
+/*
+ * 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.util;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedSet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+/** Set of names that can be accessed with and without case sensitivity. */
+public class NameSet {
+  /** 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. */
+  public static final Comparator<String> COMPARATOR =
+      new Comparator<String>() {
+        public int compare(String o1, String o2) {
+          int c = o1.compareToIgnoreCase(o2);
+          if (c == 0) {
+            c = o1.compareTo(o2);
+          }
+          return c;
+        }
+      };
+
+  private final NavigableSet<String> names;
+
+  /** Creates a NameSet based on an existing set. */
+  private NameSet(NavigableSet<String> names) {
+    this.names = names;
+    assert names.comparator() == COMPARATOR;
+  }
+
+  /** Creates a NameSet, initially empty. */
+  public NameSet() {
+    this(new TreeSet<>(COMPARATOR));
+  }
+
+  /** Creates a NameSet that is an immutable copy of a given collection. */
+  public static NameSet immutableCopyOf(Set<String> names) {
+    return new NameSet(ImmutableSortedSet.copyOf(NameSet.COMPARATOR, names));
+  }
+
+  public void add(String name) {
+    names.add(name);
+  }
+
+  /** Returns an iterable over all the entries in the set that match the given
+   * name. If case-sensitive, that iterable will have 0 or 1 elements; if
+   * case-insensitive, it may have 0 or more. */
+  public Collection<String> range(String name, boolean caseSensitive) {
+    if (caseSensitive) {
+      if (names.contains(name)) {
+        return ImmutableList.of(name);
+      } else {
+        return ImmutableList.of();
+      }
+    } else {
+      return names.subSet(name.toUpperCase(), true, name.toLowerCase(), true);
+    }
+  }
+
+  /** Returns whether this set contains the given name, with a given
+   * case-sensitivity. */
+  public boolean contains(String name, boolean caseSensitive) {
+    if (names.contains(name)) {
+      return true;
+    }
+    if (!caseSensitive) {
+      final String s = names.ceiling(name.toLowerCase());
+      return s != null
+          && s.equalsIgnoreCase(name);
+    }
+    return false;
+  }
+
+  /** Returns the contents as an iterable. */
+  public Iterable<String> iterable() {
+    return Collections.unmodifiableSet(names);
+  }
+}
+
+// End NameSet.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/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 b8bbe12..a3659c0 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6113,6 +6113,21 @@ public class JdbcTest {
         .throws_("Table 'metaData.tAbles' not found");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1563">[CALCITE-1563]
+   * In case-insensitive connection, non-existent tables use alphabetically
+   * preceding table</a>. */
+  @Test public void testLexCaseInsensitiveFindsNonexistentTable() {
+    final CalciteAssert.AssertThat with =
+        CalciteAssert.that().with(Lex.MYSQL);
+    // With [CALCITE-1563], the following query succeeded; it queried
+    // metadata.tables.
+    with.query("select COUNT(*) as c from `metaData`.`zoo`")
+        .throws_("Table 'metaData.zoo' not found");
+    with.query("select COUNT(*) as c from `metaData`.`tAbLes`")
+        .returns("c=2\n");
+  }
+
   /** Tests case-insensitive resolution of sub-query columns.
    *
    * <p>Test case for

http://git-wip-us.apache.org/repos/asf/calcite/blob/40e5b884/core/src/test/java/org/apache/calcite/util/UtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java
index 96102be..d9c65d8 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -1818,6 +1818,129 @@ public class UtilTest {
       return litmus.succeed();
     }
   }
+
+  /** Unit test for {@link org.apache.calcite.util.NameSet}. */
+  @Test public void testNameSet() {
+    final NameSet names = new NameSet();
+    assertThat(names.contains("foo", true), is(false));
+    assertThat(names.contains("foo", false), is(false));
+    names.add("baz");
+    assertThat(names.contains("foo", true), is(false));
+    assertThat(names.contains("foo", false), is(false));
+    assertThat(names.contains("baz", true), is(true));
+    assertThat(names.contains("baz", false), is(true));
+    assertThat(names.contains("BAZ", true), is(false));
+    assertThat(names.contains("BAZ", false), is(true));
+    assertThat(names.contains("bAz", false), is(true));
+    assertThat(names.range("baz", true).size(), is(1));
+    assertThat(names.range("baz", false).size(), is(1));
+    assertThat(names.range("BAZ", true).size(), is(0));
+    assertThat(names.range("BaZ", true).size(), is(0));
+    assertThat(names.range("BaZ", false).size(), is(1));
+    assertThat(names.range("BAZ", false).size(), is(1));
+
+    assertThat(names.contains("bAzinga", false), is(false));
+    assertThat(names.range("bAzinga", true).size(), is(0));
+    assertThat(names.range("bAzinga", false).size(), is(0));
+
+    assertThat(names.contains("zoo", true), is(false));
+    assertThat(names.contains("zoo", false), is(false));
+    assertThat(names.range("zoo", true).size(), is(0));
+
+    assertThat(Iterables.size(names.iterable()), is(1));
+    names.add("Baz");
+    names.add("Abcde");
+    names.add("Zymurgy");
+    assertThat(Iterables.size(names.iterable()), is(4));
+    assertThat(names.range("baz", false).size(), is(2));
+    assertThat(names.range("baz", true).size(), is(1));
+    assertThat(names.range("BAZ", true).size(), is(0));
+    assertThat(names.range("Baz", true).size(), is(1));
+  }
+
+  /** Unit test for {@link org.apache.calcite.util.NameMap}. */
+  @Test public void testNameMap() {
+    final NameMap<Integer> map = new NameMap<>();
+    assertThat(map.containsKey("foo", true), is(false));
+    assertThat(map.containsKey("foo", false), is(false));
+    map.put("baz", 0);
+    assertThat(map.containsKey("foo", true), is(false));
+    assertThat(map.containsKey("foo", false), is(false));
+    assertThat(map.containsKey("baz", true), is(true));
+    assertThat(map.containsKey("baz", false), is(true));
+    assertThat(map.containsKey("BAZ", true), is(false));
+    assertThat(map.containsKey("BAZ", false), is(true));
+    assertThat(map.containsKey("bAz", false), is(true));
+    assertThat(map.range("baz", true).size(), is(1));
+    assertThat(map.range("baz", false).size(), is(1));
+    assertThat(map.range("BAZ", true).size(), is(0));
+    assertThat(map.range("BaZ", true).size(), is(0));
+    assertThat(map.range("BaZ", false).size(), is(1));
+    assertThat(map.range("BAZ", false).size(), is(1));
+
+    assertThat(map.containsKey("bAzinga", false), is(false));
+    assertThat(map.range("bAzinga", true).size(), is(0));
+    assertThat(map.range("bAzinga", false).size(), is(0));
+
+    assertThat(map.containsKey("zoo", true), is(false));
+    assertThat(map.containsKey("zoo", false), is(false));
+    assertThat(map.range("zoo", true).size(), is(0));
+
+    assertThat(map.map().size(), is(1));
+    map.put("Baz", 1);
+    map.put("Abcde", 2);
+    map.put("Zymurgy", 3);
+    assertThat(map.map().size(), is(4));
+    assertThat(map.map().entrySet().size(), is(4));
+    assertThat(map.map().keySet().size(), is(4));
+    assertThat(map.range("baz", false).size(), is(2));
+    assertThat(map.range("baz", true).size(), is(1));
+    assertThat(map.range("BAZ", true).size(), is(0));
+    assertThat(map.range("Baz", true).size(), is(1));
+  }
+
+  /** Unit test for {@link org.apache.calcite.util.NameMultimap}. */
+  @Test public void testNameMultimap() {
+    final NameMultimap<Integer> map = new NameMultimap<>();
+    assertThat(map.containsKey("foo", true), is(false));
+    assertThat(map.containsKey("foo", false), is(false));
+    map.put("baz", 0);
+    map.put("baz", 0);
+    map.put("BAz", 0);
+    assertThat(map.containsKey("foo", true), is(false));
+    assertThat(map.containsKey("foo", false), is(false));
+    assertThat(map.containsKey("baz", true), is(true));
+    assertThat(map.containsKey("baz", false), is(true));
+    assertThat(map.containsKey("BAZ", true), is(false));
+    assertThat(map.containsKey("BAZ", false), is(true));
+    assertThat(map.containsKey("bAz", false), is(true));
+    assertThat(map.range("baz", true).size(), is(2));
+    assertThat(map.range("baz", false).size(), is(3));
+    assertThat(map.range("BAZ", true).size(), is(0));
+    assertThat(map.range("BaZ", true).size(), is(0));
+    assertThat(map.range("BaZ", false).size(), is(3));
+    assertThat(map.range("BAZ", false).size(), is(3));
+
+    assertThat(map.containsKey("bAzinga", false), is(false));
+    assertThat(map.range("bAzinga", true).size(), is(0));
+    assertThat(map.range("bAzinga", false).size(), is(0));
+
+    assertThat(map.containsKey("zoo", true), is(false));
+    assertThat(map.containsKey("zoo", false), is(false));
+    assertThat(map.range("zoo", true).size(), is(0));
+
+    assertThat(map.map().size(), is(2));
+    map.put("Baz", 1);
+    map.put("Abcde", 2);
+    map.put("Zymurgy", 3);
+    assertThat(map.map().size(), is(5));
+    assertThat(map.map().entrySet().size(), is(5));
+    assertThat(map.map().keySet().size(), is(5));
+    assertThat(map.range("baz", false).size(), is(4));
+    assertThat(map.range("baz", true).size(), is(2));
+    assertThat(map.range("BAZ", true).size(), is(0));
+    assertThat(map.range("Baz", true).size(), is(1));
+  }
 }
 
 // End UtilTest.java


Mime
View raw message