You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/01/06 20:52:45 UTC

[1/5] calcite git commit: [CALCITE-1563] In case-insensitive connection, non-existent tables use alphabetically preceding table

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 <jh...@apache.org>
Authored: Wed Jan 4 18:36:30 2017 -0800
Committer: Julian Hyde <jh...@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


[2/5] calcite git commit: [CALCITE-1544] AggregateJoinTransposeRule fails to preserve row type (Kurt Young)

Posted by jh...@apache.org.
[CALCITE-1544] AggregateJoinTransposeRule fails to preserve row type (Kurt Young)

The "allColumnsInAggregate" variable is inferred by expanding the
aggregate keys with equative join keys. It's not appropriate to let
this flag to determine whether we should convert the query since the
row type will easily be unmatched.

Close apache/calcite#344


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

Branch: refs/heads/master
Commit: 91102baf9ad9d45764ec190fbbeb00182be9aabe
Parents: 408285f
Author: Kurt Young <yk...@gmail.com>
Authored: Wed Jan 4 15:27:40 2017 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 6 10:45:52 2017 -0800

----------------------------------------------------------------------
 .../rel/rules/AggregateJoinTransposeRule.java   | 57 ++++++++++----------
 .../apache/calcite/test/RelOptRulesTest.java    | 29 ++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 48 +++++++++++++++++
 3 files changed, 106 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/91102baf/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
index fa76306..3aba5bf 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateJoinTransposeRule.java
@@ -29,6 +29,7 @@ import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.logical.LogicalJoin;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexInputRef;
@@ -306,39 +307,39 @@ public class AggregateJoinTransposeRule extends RelOptRule {
               leftSubTotal == null ? -1 : leftSubTotal,
               rightSubTotal == null ? -1 : rightSubTotal + newLeftWidth));
     }
-  b:
-    if (allColumnsInAggregate && newAggCalls.isEmpty()) {
-      // no need to aggregate
-    } else {
-      relBuilder.project(projects);
-      if (allColumnsInAggregate) {
-        // let's see if we can convert
-        List<RexNode> projects2 = new ArrayList<>();
-        for (int key : Mappings.apply(mapping, aggregate.getGroupSet())) {
-          projects2.add(relBuilder.field(key));
-        }
-        for (AggregateCall newAggCall : newAggCalls) {
-          final SqlSplittableAggFunction splitter =
-              newAggCall.getAggregation()
-                  .unwrap(SqlSplittableAggFunction.class);
-          if (splitter != null) {
-            projects2.add(
-                splitter.singleton(rexBuilder, relBuilder.peek().getRowType(),
-                    newAggCall));
-          }
-        }
-        if (projects2.size()
-            == aggregate.getGroupSet().cardinality() + newAggCalls.size()) {
-          // We successfully converted agg calls into projects.
-          relBuilder.project(projects2);
-          break b;
+
+    relBuilder.project(projects);
+
+    boolean aggConvertedToProjects = false;
+    if (allColumnsInAggregate) {
+      // let's see if we can convert aggregate into projects
+      List<RexNode> projects2 = new ArrayList<>();
+      for (int key : Mappings.apply(mapping, aggregate.getGroupSet())) {
+        projects2.add(relBuilder.field(key));
+      }
+      for (AggregateCall newAggCall : newAggCalls) {
+        final SqlSplittableAggFunction splitter =
+            newAggCall.getAggregation().unwrap(SqlSplittableAggFunction.class);
+        if (splitter != null) {
+          final RelDataType rowType = relBuilder.peek().getRowType();
+          projects2.add(splitter.singleton(rexBuilder, rowType, newAggCall));
         }
       }
+      if (projects2.size()
+          == aggregate.getGroupSet().cardinality() + newAggCalls.size()) {
+        // We successfully converted agg calls into projects.
+        relBuilder.project(projects2);
+        aggConvertedToProjects = true;
+      }
+    }
+
+    if (!aggConvertedToProjects) {
       relBuilder.aggregate(
           relBuilder.groupKey(Mappings.apply(mapping, aggregate.getGroupSet()),
               aggregate.indicator, Mappings.apply2(mapping, aggregate.getGroupSets())),
           newAggCalls);
     }
+
     call.transformTo(relBuilder.build());
   }
 
@@ -348,8 +349,8 @@ public class AggregateJoinTransposeRule extends RelOptRule {
   private static ImmutableBitSet keyColumns(ImmutableBitSet aggregateColumns,
       ImmutableList<RexNode> predicates) {
     SortedMap<Integer, BitSet> equivalence = new TreeMap<>();
-    for (RexNode pred : predicates) {
-      populateEquivalences(equivalence, pred);
+    for (RexNode predicate : predicates) {
+      populateEquivalences(equivalence, predicate);
     }
     ImmutableBitSet keyColumns = aggregateColumns;
     for (Integer aggregateColumn : aggregateColumns) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/91102baf/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index b349a7e..1e36724 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -2462,6 +2462,35 @@ public class RelOptRulesTest extends RelOptTestBase {
     checkPlanning(tester, preProgram, new HepPlanner(program), sql, true);
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1544">[CALCITE-1544]
+   * AggregateJoinTransposeRule fails to preserve row type</a>. */
+  @Test public void testPushAggregateThroughJoin4() throws Exception {
+    final HepProgram preProgram = new HepProgramBuilder()
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    final HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(AggregateJoinTransposeRule.EXTENDED)
+        .build();
+    final String sql = "select e.deptno\n"
+        + "from sales.emp as e join sales.dept as d on e.deptno = d.deptno\n"
+        + "group by e.deptno";
+    sql(sql).withPre(preProgram).with(program).check();
+  }
+
+  @Test public void testPushAggregateThroughJoin5() throws Exception {
+    final HepProgram preProgram = new HepProgramBuilder()
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    final HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(AggregateJoinTransposeRule.EXTENDED)
+        .build();
+    final String sql = "select e.deptno, d.deptno\n"
+        + "from sales.emp as e join sales.dept as d on e.deptno = d.deptno\n"
+        + "group by e.deptno, d.deptno";
+    sql(sql).withPre(preProgram).with(program).check();
+  }
+
   /** SUM is the easiest aggregate function to split. */
   @Test public void testPushAggregateSumThroughJoin() throws Exception {
     final HepProgram preProgram = new HepProgramBuilder()

http://git-wip-us.apache.org/repos/asf/calcite/blob/91102baf/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index fd5fe5f..1c8cb99 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -4837,6 +4837,54 @@ LogicalAggregate(group=[{0, 9}])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testPushAggregateThroughJoin4">
+        <Resource name="sql">
+            <![CDATA[select e.deptno
+from sales.emp as e join sales.dept as d on e.deptno = d.deptno
+group by e.deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{7}])
+  LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0])
+  LogicalJoin(condition=[=($0, $1)], joinType=[inner])
+    LogicalAggregate(group=[{7}])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testPushAggregateThroughJoin5">
+        <Resource name="sql">
+            <![CDATA[select e.deptno, d.deptno
+from sales.emp as e join sales.dept as d on e.deptno = d.deptno
+group by e.deptno, d.deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{7, 9}])
+  LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], DEPTNO0=[$1])
+  LogicalJoin(condition=[=($0, $1)], joinType=[inner])
+    LogicalAggregate(group=[{7}])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testPushAggregateSumThroughJoin">
         <Resource name="sql">
             <![CDATA[select e.job,sum(sal)


[5/5] calcite git commit: [CALCITE-1558] AggregateExpandDistinctAggregatesRule gets field mapping wrong if groupKey is used in aggregate function (Zhenghua Gao)

Posted by jh...@apache.org.
[CALCITE-1558] AggregateExpandDistinctAggregatesRule gets field mapping wrong if groupKey is used in aggregate function (Zhenghua Gao)

Add Util.intersects and ImmutableBitSet.asSet. (Julian Hyde)

Close apache/calcite#347


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

Branch: refs/heads/master
Commit: 2b9663752a0bfffce4641450b01365cc4f2124e1
Parents: 34df5a5
Author: Zhenghua Gao <do...@gmail.com>
Authored: Thu Jan 5 14:42:48 2017 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 6 10:52:10 2017 -0800

----------------------------------------------------------------------
 .../AggregateExpandDistinctAggregatesRule.java  | 33 ++++++++++----
 .../apache/calcite/util/ImmutableBitSet.java    | 22 ++++++++++
 .../main/java/org/apache/calcite/util/Util.java | 10 +++++
 .../apache/calcite/test/RelOptRulesTest.java    | 46 +++++++++++++++-----
 .../calcite/util/ImmutableBitSetTest.java       | 32 +++++++++++++-
 .../java/org/apache/calcite/util/UtilTest.java  | 16 +++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 45 +++++++++++++++++++
 7 files changed, 183 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index de12e42..553285e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -266,6 +266,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
         relBuilder.peek().getRowType().getFieldList();
     final boolean hasGroupBy = aggregate.getGroupSet().size() > 0;
 
+    final Set<Integer> groupSet = aggregate.getGroupSet().asSet();
+
     // Add the distinct aggregate column(s) to the group-by columns,
     // if not already a part of the group-by
     newGroupSet.addAll(aggregate.getGroupSet().asList());
@@ -290,26 +292,31 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     for (final AggregateCall aggCall : aggCalls) {
       if (!aggCall.isDistinct()) {
         for (int arg : aggCall.getArgList()) {
-          sourceOf.put(arg, projects.size());
+          if (!groupSet.contains(arg)) {
+            sourceOf.put(arg, projects.size());
+          }
         }
       }
     }
     int fakeArg0 = 0;
     for (final AggregateCall aggCall : aggCalls) {
       // We will deal with non-distinct aggregates below
-      if (!aggCall.isDistinct()) {
-        if (aggCall.getArgList().size() == 0) {
-          while (sourceOf.get(fakeArg0) != null) {
-            ++fakeArg0;
-          }
-          fakeArgs.add(fakeArg0);
+      if (!aggCall.isDistinct()
+          && (aggCall.getArgList().isEmpty()
+              || Util.intersects(groupSet, aggCall.getArgList()))) {
+        while (sourceOf.get(fakeArg0) != null) {
+          ++fakeArg0;
         }
+        fakeArgs.add(fakeArg0);
+        ++fakeArg0;
       }
     }
     for (final AggregateCall aggCall : aggCalls) {
       if (!aggCall.isDistinct()) {
         for (int arg : aggCall.getArgList()) {
-          sourceOf.remove(arg);
+          if (!groupSet.contains(arg)) {
+            sourceOf.remove(arg);
+          }
         }
       }
     }
@@ -336,6 +343,10 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
           ++fakeArgIdx;
         } else {
           for (int arg : newCall.getArgList()) {
+            if (groupSet.contains(arg)) {
+              arg = fakeArgs.get(fakeArgIdx++);
+              callArgMap.put(newCall, arg);
+            }
             sourceOf.put(arg, projects.size());
             projects.add(
                 Pair.of((RexNode) new RexInputRef(arg, newCall.getType()),
@@ -361,7 +372,11 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
       final AggregateCall newCall;
       for (int j = 0; j < argCount; j++) {
         final Integer arg = aggCall.getArgList().get(j);
-        newArgs.add(sourceOf.get(arg));
+        if (callArgMap.containsKey(aggCall)) {
+          newArgs.add(sourceOf.get(callArgMap.get(aggCall)));
+        } else {
+          newArgs.add(sourceOf.get(arg));
+        }
       }
       if (aggCall.isDistinct()) {
         newCall =

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
index ff1774c..61b3321 100644
--- a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
+++ b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
@@ -30,6 +30,7 @@ import com.google.common.collect.Ordering;
 import java.io.Serializable;
 import java.nio.LongBuffer;
 import java.util.AbstractList;
+import java.util.AbstractSet;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -37,6 +38,7 @@ import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import javax.annotation.Nonnull;
@@ -625,6 +627,26 @@ public class ImmutableBitSet
     };
   }
 
+  /** Creates a view onto this bit set as a set of integers.
+   *
+   * <p>The {@code size} and {@code contains} methods are both O(n), but the
+   * iterator is efficient. */
+  public Set<Integer> asSet() {
+    return new AbstractSet<Integer>() {
+      @Nonnull public Iterator<Integer> iterator() {
+        return ImmutableBitSet.this.iterator();
+      }
+
+      public int size() {
+        return cardinality();
+      }
+
+      @Override public boolean contains(Object o) {
+        return ImmutableBitSet.this.get((Integer) o);
+      }
+    };
+  }
+
   /**
    * Converts this bit set to an array.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/main/java/org/apache/calcite/util/Util.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java
index 3ab5d4b..4117a8d 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -2121,6 +2121,16 @@ public class Util {
     return -1;
   }
 
+  /** Returns whether two collections have any elements in common. */
+  public static <E> boolean intersects(Collection<E> c0, Collection<E> c1) {
+    for (E e : c1) {
+      if (c0.contains(e)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /** Looks for a string within a list of strings, using a given
    * case-sensitivity policy, and returns the position at which the first match
    * is found, or -1 if there are no matches. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 1e36724..ac55f8a 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -826,6 +826,42 @@ public class RelOptRulesTest extends RelOptTestBase {
             + " from sales.emp group by rollup(deptno,job)");
   }
 
+  @Test public void testDistinctNonDistinctAggregates() {
+    final String sql = "select emp.empno, count(*), avg(distinct dept.deptno)\n"
+        + "from sales.emp emp inner join sales.dept dept\n"
+        + "on emp.deptno = dept.deptno\n"
+        + "group by emp.empno";
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
+        .build();
+    sql(sql).with(program).check();
+  }
+
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1558">[CALCITE-1558]
+   * AggregateExpandDistinctAggregatesRule gets field mapping wrong if groupKey
+   * is used in aggregate function</a>. */
+  @Test public void testDistinctNonDistinctAggregatesWithGrouping1() {
+    final String sql = "SELECT deptno,\n"
+        + "  SUM(deptno), SUM(DISTINCT sal), MAX(deptno), MAX(comm)\n"
+        + "FROM emp\n"
+        + "GROUP BY deptno";
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
+        .build();
+    sql(sql).with(program).check();
+  }
+
+  @Test public void testDistinctNonDistinctAggregatesWithGrouping2() {
+    final String sql = "SELECT deptno, COUNT(deptno), SUM(DISTINCT sal)\n"
+        + "FROM emp\n"
+        + "GROUP BY deptno";
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
+        .build();
+    sql(sql).with(program).check();
+  }
+
   @Test public void testPushProjectPastFilter() {
     checkPlanning(ProjectFilterTransposeRule.INSTANCE,
         "select empno + deptno from emp where sal = 10 * comm "
@@ -3066,16 +3102,6 @@ public class RelOptRulesTest extends RelOptTestBase {
     sql(sql).with(program).check();
   }
 
-  @Test public void testDistinctNonDistinctAggregates() {
-    final String sql = "select emp.empno, count(*), avg(distinct dept.deptno)\n"
-        + "from sales.emp emp inner join sales.dept dept\n"
-        + "on emp.deptno = dept.deptno\n"
-        + "group by emp.empno";
-    final HepProgram program = HepProgram.builder()
-        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
-        .build();
-    sql(sql).with(program).check();
-  }
 }
 
 // End RelOptRulesTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java b/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
index 110f8b0..8894012 100644
--- a/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
+++ b/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
@@ -26,7 +26,9 @@ import org.junit.Test;
 import java.nio.LongBuffer;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.SortedMap;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -179,18 +181,44 @@ public class ImmutableBitSetTest {
 
   /**
    * Tests the methods
-   * {@link org.apache.calcite.util.ImmutableBitSet#toList} and
-   * {@link org.apache.calcite.util.ImmutableBitSet#asList}.
+   * {@link org.apache.calcite.util.ImmutableBitSet#toList}, and
+   * {@link org.apache.calcite.util.ImmutableBitSet#asList} and
+   * {@link org.apache.calcite.util.ImmutableBitSet#asSet}.
    */
   @Test public void testAsList() {
     final List<ImmutableBitSet> list = getSortedList();
+
+    // create a set of integers in and not in the lists
+    final Set<Integer> integers = new HashSet<>();
+    for (ImmutableBitSet set : list) {
+      for (Integer integer : set) {
+        integers.add(integer);
+        integers.add(integer + 1);
+        integers.add(integer + 10);
+      }
+    }
+
     for (ImmutableBitSet bitSet : list) {
       final List<Integer> list1 = bitSet.toList();
       final List<Integer> listView = bitSet.asList();
+      final Set<Integer> setView = bitSet.asSet();
       assertThat(list1.size(), equalTo(bitSet.cardinality()));
+      assertThat(listView.size(), equalTo(bitSet.cardinality()));
+      assertThat(setView.size(), equalTo(bitSet.cardinality()));
       assertThat(list1.toString(), equalTo(listView.toString()));
+      assertThat(list1.toString(), equalTo(setView.toString()));
       assertTrue(list1.equals(listView));
       assertThat(list1.hashCode(), equalTo(listView.hashCode()));
+
+      final Set<Integer> set = new HashSet<>(list1);
+      assertThat(setView.hashCode(), is(set.hashCode()));
+      assertThat(setView, equalTo(set));
+
+      for (Integer integer : integers) {
+        final boolean b = list1.contains(integer);
+        assertThat(listView.contains(integer), is(b));
+        assertThat(setView.contains(integer), is(b));
+      }
     }
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/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 d9c65d8..4b970ce 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -1285,6 +1285,22 @@ public class UtilTest {
     assertFalse(Util.isDistinct(Arrays.asList("a", null, "b", null)));
   }
 
+  /** Unit test for {@link Util#intersects(Collection, Collection)}. */
+  @Test public void testIntersects() {
+    final List<String> empty = Collections.emptyList();
+    final List<String> listA = Collections.singletonList("a");
+    final List<String> listC = Collections.singletonList("c");
+    final List<String> listD = Collections.singletonList("d");
+    final List<String> listAbc = Arrays.asList("a", "b", "c");
+    assertThat(Util.intersects(empty, listA), is(false));
+    assertThat(Util.intersects(empty, empty), is(false));
+    assertThat(Util.intersects(listA, listAbc), is(true));
+    assertThat(Util.intersects(listAbc, listAbc), is(true));
+    assertThat(Util.intersects(listAbc, listC), is(true));
+    assertThat(Util.intersects(listAbc, listD), is(false));
+    assertThat(Util.intersects(listC, listD), is(false));
+  }
+
   /**
    * Unit test for {@link org.apache.calcite.util.JsonBuilder}.
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/2b966375/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 1c8cb99..f76ef23 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -420,6 +420,51 @@ LogicalProject(DEPTNO=[$0], I0=[$2], I1=[$3])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testDistinctNonDistinctAggregatesWithGrouping1">
+        <Resource name="sql">
+            <![CDATA[SELECT deptno,
+  SUM(deptno), SUM(DISTINCT sal), MAX(deptno), MAX(comm)
+FROM emp
+GROUP BY deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[SUM($0)], EXPR$2=[SUM(DISTINCT $1)], EXPR$3=[MAX($0)], EXPR$4=[MAX($2)])
+  LogicalProject(DEPTNO=[$7], SAL=[$5], COMM=[$6])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[SUM($2)], EXPR$2=[SUM($1)], EXPR$3=[MAX($3)], EXPR$4=[MAX($4)])
+  LogicalAggregate(group=[{0, 1}], EXPR$1=[SUM($0)], EXPR$3=[MAX($0)], EXPR$4=[MAX($2)])
+    LogicalProject(DEPTNO=[$7], SAL=[$5], COMM=[$6])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctNonDistinctAggregatesWithGrouping2">
+        <Resource name="sql">
+            <![CDATA[SELECT deptno, COUNT(deptno), SUM(DISTINCT sal)
+FROM emp
+GROUP BY deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT()], EXPR$2=[SUM(DISTINCT $1)])
+  LogicalProject(DEPTNO=[$7], SAL=[$5])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[SUM($2)], EXPR$2=[SUM($1)])
+  LogicalAggregate(group=[{0, 1}], EXPR$1=[COUNT()])
+    LogicalProject(DEPTNO=[$7], SAL=[$5])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testEmptyAggregate">
         <Resource name="sql">
             <![CDATA[select sum(empno) from emp where false group by deptno]]>


[3/5] calcite git commit: [CALCITE-1543] Correlated scalar sub-query with multiple aggregates gives AssertionError (Kurt Young)

Posted by jh...@apache.org.
[CALCITE-1543] Correlated scalar sub-query with multiple aggregates gives AssertionError (Kurt Young)

Add TPC-H test case (Julian Hyde)

Close apache/calcite#343


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

Branch: refs/heads/master
Commit: 408285f08f0b8e0e019dc0c6fe02d8e05425f4a3
Parents: 8dd935e
Author: Kurt Young <yk...@gmail.com>
Authored: Wed Jan 4 11:57:50 2017 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 6 10:45:52 2017 -0800

----------------------------------------------------------------------
 .../apache/calcite/sql2rel/RelDecorrelator.java |  1 -
 .../calcite/test/SqlToRelConverterTest.java     | 13 ++++++++
 .../calcite/test/SqlToRelConverterTest.xml      | 32 ++++++++++++++++++++
 .../apache/calcite/adapter/tpch/TpchTest.java   | 18 +++++++++++
 4 files changed, 63 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/408285f0/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index 41e262b..a526d2c 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -451,7 +451,6 @@ public class RelDecorrelator implements ReflectiveVisitor {
       // If input has not been rewritten, do not rewrite this rel.
       return null;
     }
-    assert !frame.corVarOutputPos.isEmpty();
     final RelNode newInput = frame.r;
 
     // map from newInput

http://git-wip-us.apache.org/repos/asf/calcite/blob/408285f0/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 238113a..ef3840d 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1742,6 +1742,19 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
     sql(sql).decorrelate(true).expand(true).ok();
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1543">[CALCITE-1543]
+   * Correlated scalar sub-query with multiple aggregates gives
+   * AssertionError</a>. */
+  @Test public void testCorrelationMultiScalarAggregate() {
+    final String sql = "select sum(e1.empno)\n"
+        + "from emp e1, dept d1\n"
+        + "where e1.deptno = d1.deptno\n"
+        + "and e1.sal > (select avg(e2.sal) from emp e2\n"
+        + "  where e2.deptno = d1.deptno)";
+    sql(sql).decorrelate(true).expand(true).ok();
+  }
+
   @Test public void testCorrelationScalarAggAndFilterRex() {
     final String sql = "SELECT e1.empno\n"
         + "FROM emp e1, dept d1 where e1.deptno = d1.deptno\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/408285f0/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 0b31805..cfbfa8f 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -2878,6 +2878,38 @@ LogicalProject(EMPNO=[$0])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testCorrelationMultiScalarAggregate">
+        <Resource name="sql">
+            <![CDATA[select sum(e1.empno)
+from emp e1, dept d1
+where e1.deptno = d1.deptno
+and e1.sal > (select avg(e2.sal) from emp e2
+  where e2.deptno = d1.deptno)]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[SUM($0)])
+  LogicalProject(EMPNO=[$0])
+    LogicalProject(EMPNO=[$0])
+      LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10], EXPR$0=[$12])
+        LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10], DEPTNO00=[CAST($11):INTEGER], EXPR$0=[CAST($12):INTEGER])
+          LogicalJoin(condition=[AND(=($9, $11), >($5, $12))], joinType=[inner])
+            LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+              LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+              LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+            LogicalAggregate(group=[{0}], EXPR$0=[AVG($1)])
+              LogicalProject(DEPTNO0=[$1], SAL=[$0])
+                LogicalProject(SAL=[$5], DEPTNO0=[$9])
+                  LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+                    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+                    LogicalAggregate(group=[{0}])
+                      LogicalProject(DEPTNO0=[$9])
+                        LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+                          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+                          LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testCorrelationExistsAndFilter">
         <Resource name="sql">
             <![CDATA[SELECT e1.empno

http://git-wip-us.apache.org/repos/asf/calcite/blob/408285f0/plus/src/test/java/org/apache/calcite/adapter/tpch/TpchTest.java
----------------------------------------------------------------------
diff --git a/plus/src/test/java/org/apache/calcite/adapter/tpch/TpchTest.java b/plus/src/test/java/org/apache/calcite/adapter/tpch/TpchTest.java
index 0170520..7dee05d 100644
--- a/plus/src/test/java/org/apache/calcite/adapter/tpch/TpchTest.java
+++ b/plus/src/test/java/org/apache/calcite/adapter/tpch/TpchTest.java
@@ -779,6 +779,24 @@ public class TpchTest {
         .returnsCount(1500000);
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1543">[CALCITE-1543]
+   * Correlated scalar sub-query with multiple aggregates gives
+   * AssertionError</a>. */
+  @Ignore("planning succeeds, but gives OutOfMemoryError during execution")
+  @Test public void testDecorrelateScalarAggregate() {
+    final String sql = "select sum(l_extendedprice)\n"
+        + "from lineitem, part\n"
+        + "where\n"
+        + "     p_partkey = l_partkey\n"
+        + "     and l_quantity > (\n"
+        + "       select avg(l_quantity)\n"
+        + "       from lineitem\n"
+        + "       where l_partkey = p_partkey\n"
+        + "    )\n";
+    with().query(sql).runs();
+  }
+
   @Test public void testCustomer() {
     with()
         .query("select * from tpch.customer")


[4/5] calcite git commit: [CALCITE-1562] Update jsr305 from 1.3.9 to 3.0.1

Posted by jh...@apache.org.
[CALCITE-1562] Update jsr305 from 1.3.9 to 3.0.1

Close apache/calcite#346


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

Branch: refs/heads/master
Commit: 34df5a5a6a91f212861296f74af6da7d81184ca9
Parents: 40e5b88
Author: Josh Elser <el...@apache.org>
Authored: Wed Jan 4 23:54:57 2017 -0500
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jan 6 10:45:53 2017 -0800

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/34df5a5a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 108e179..f4973fb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,7 +61,7 @@ limitations under the License.
     <commons-lang3.version>3.2</commons-lang3.version>
     <commons-logging.version>1.1.3</commons-logging.version>
     <elasticsearch-java-driver.version>2.3.2</elasticsearch-java-driver.version>
-    <findbugs.version>1.3.9</findbugs.version>
+    <findbugs.version>3.0.1</findbugs.version>
     <fmpp-maven-plugin.version>1.0</fmpp-maven-plugin.version>
     <foodmart-data-hsqldb.version>0.3</foodmart-data-hsqldb.version>
     <foodmart-queries.version>0.4.1</foodmart-queries.version>