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 2015/06/13 02:38:05 UTC

[1/7] incubator-calcite git commit: [CALCITE-740] Redundant WHERE clause causes wrong result in MongoDB adapter

Repository: incubator-calcite
Updated Branches:
  refs/heads/master dbdb091dc -> 468a161bb


[CALCITE-740] Redundant WHERE clause causes wrong result in MongoDB adapter


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

Branch: refs/heads/master
Commit: 6ece12a862d9fb03f2be77c0d60880a30fbd79b1
Parents: dbdb091
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jun 8 12:05:44 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Jun 8 12:07:48 2015 -0700

----------------------------------------------------------------------
 .../calcite/adapter/mongodb/MongoFilter.java    | 34 ++++++++++++++++--
 .../org/apache/calcite/test/MongoAdapterIT.java | 36 ++++++++++++++++++++
 2 files changed, 67 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6ece12a8/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
----------------------------------------------------------------------
diff --git a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
index 5f717d7..45b4baf 100644
--- a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
+++ b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoFilter.java
@@ -116,20 +116,48 @@ public class MongoFilter extends Filter implements MongoRel {
       Map<String, Object> map = builder.map();
       for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
         multimap.removeAll(entry.getKey());
-        map.put(entry.getKey(), literalToString(entry.getValue()));
+        map.put(entry.getKey(), literalValue(entry.getValue()));
       }
       for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry
           : multimap.asMap().entrySet()) {
         Map<String, Object> map2 = builder.map();
         for (Pair<String, RexLiteral> s : entry.getValue()) {
-          map2.put(s.left, literalToString(s.right));
+          addPredicate(map2, s.left, literalValue(s.right));
         }
         map.put(entry.getKey(), map2);
       }
       return map;
     }
 
-    private static Object literalToString(RexLiteral literal) {
+    private void addPredicate(Map<String, Object> map, String op, Object v) {
+      if (map.containsKey(op) && stronger(op, map.get(op), v)) {
+        return;
+      }
+      map.put(op, v);
+    }
+
+    /** Returns whether {@code v0} is a stronger value for operator {@code key}
+     * than {@code v1}.
+     *
+     * <p>For example, {@code stronger("$lt", 100, 200)} returns true, because
+     * "&lt; 100" is a more powerful condition than "&lt; 200".
+     */
+    private boolean stronger(String key, Object v0, Object v1) {
+      if (key.equals("$lt") || key.equals("$lte")) {
+        if (v0 instanceof Number && v1 instanceof Number) {
+          return ((Number) v0).doubleValue() < ((Number) v1).doubleValue();
+        }
+        if (v0 instanceof String && v1 instanceof String) {
+          return v0.toString().compareTo(v1.toString()) < 0;
+        }
+      }
+      if (key.equals("$gt") || key.equals("$gte")) {
+        return stronger("$lt", v1, v0);
+      }
+      return false;
+    }
+
+    private static Object literalValue(RexLiteral literal) {
       return literal.getValue2();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/6ece12a8/mongodb/src/test/java/org/apache/calcite/test/MongoAdapterIT.java
----------------------------------------------------------------------
diff --git a/mongodb/src/test/java/org/apache/calcite/test/MongoAdapterIT.java b/mongodb/src/test/java/org/apache/calcite/test/MongoAdapterIT.java
index f994142..2393660 100644
--- a/mongodb/src/test/java/org/apache/calcite/test/MongoAdapterIT.java
+++ b/mongodb/src/test/java/org/apache/calcite/test/MongoAdapterIT.java
@@ -716,6 +716,42 @@ public class MongoAdapterIT {
             + "STATE=WV; CITY=ATHENS\n");
   }
 
+  /** MongoDB's predicates are handed (they can only accept literals on the
+   * right-hand size) so it's worth testing that we handle them right both
+   * ways around.
+   *
+   * <p>Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-740">[CALCITE-740]
+   * Redundant WHERE clause causes wrong result in MongoDB adapter</a>. */
+  @Test public void testFilterPair() {
+    final int gt9k = 8125;
+    final int lt9k = 21227;
+    final int gt8k = 8707;
+    final int lt8k = 20645;
+    checkPredicate(gt9k, "where pop > 8000 and pop > 9000");
+    checkPredicate(gt9k, "where pop > 9000");
+    checkPredicate(lt9k, "where pop < 9000");
+    checkPredicate(gt8k, "where pop > 8000");
+    checkPredicate(lt8k, "where pop < 8000");
+    checkPredicate(gt9k, "where pop > 9000 and pop > 8000");
+    checkPredicate(gt8k, "where pop > 9000 or pop > 8000");
+    checkPredicate(gt8k, "where pop > 8000 or pop > 9000");
+    checkPredicate(lt8k, "where pop < 8000 and pop < 9000");
+  }
+
+  private void checkPredicate(int expected, String q) {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select count(*) as c from zips\n" + q)
+        .returns("C=" + expected + "\n");
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips\n" + q)
+        .returnsCount(expected);
+  }
+
   @Ignore
   @Test public void testFoodmartQueries() {
     final List<Pair<String, String>> queries = JdbcTest.getFoodmartQueries();


[7/7] incubator-calcite git commit: [CALCITE-757] Fix expansion of view of another view (Venki Korukanti)

Posted by jh...@apache.org.
[CALCITE-757] Fix expansion of view of another view (Venki Korukanti)

Pass ViewExpanderImpl to SqlToRelConverter so that a view of another view is expanded properly.

The issue was found in [DRILL-1145].

Close apache/incubator-calcite#94


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

Branch: refs/heads/master
Commit: 468a161bbd8fd4994085d96e2bc801209a17cf76
Parents: 2b21765
Author: vkorukanti <ve...@gmail.com>
Authored: Fri Jun 5 11:02:46 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jun 12 00:15:47 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/prepare/PlannerImpl.java   | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/468a161b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
index f39ab2c..1c67178 100644
--- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
@@ -218,11 +218,18 @@ public class PlannerImpl implements Planner {
       final RexBuilder rexBuilder = createRexBuilder();
       final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder);
       final SqlToRelConverter sqlToRelConverter =
-          new SqlToRelConverter(null, validator, catalogReader, cluster,
-              convertletTable);
+          new SqlToRelConverter(new ViewExpanderImpl(), validator,
+              catalogReader, cluster, convertletTable);
+
       sqlToRelConverter.setTrimUnusedFields(false);
+      sqlToRelConverter.enableTableAccessConversion(false);
+
+      RelNode rel =
+          sqlToRelConverter.convertQuery(validatedSqlNode, true, false);
+      rel = sqlToRelConverter.flattenTypes(rel, true);
+      rel = RelDecorrelator.decorrelateQuery(rel);
 
-      return sqlToRelConverter.convertQuery(validatedSqlNode, true, false);
+      return rel;
     }
   }
 


[2/7] incubator-calcite git commit: [CALCITE-429] Cardinality provider for use by lattice algorithm

Posted by jh...@apache.org.
[CALCITE-429] Cardinality provider for use by lattice algorithm


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

Branch: refs/heads/master
Commit: 4cc539fc28fac0c5897ada1cc5e57729807d260d
Parents: 6ece12a
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jun 8 14:07:06 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jun 11 16:12:26 2015 -0700

----------------------------------------------------------------------
 .../calcite/avatica/test/AvaticaSuite.java      |  1 +
 avatica/pom.xml                                 |  5 ++
 .../apache/calcite/avatica/AvaticaUtils.java    | 48 ++++++++++
 .../calcite/avatica/ConnectionConfigImpl.java   | 20 +----
 .../calcite/avatica/test/AvaticaUtilsTest.java  | 64 +++++++++++++
 .../CachingLatticeStatisticProvider.java        | 57 ++++++++++++
 .../DelegatingLatticeStatisticProvider.java     | 41 +++++++++
 .../org/apache/calcite/materialize/Lattice.java | 95 ++++++++------------
 .../materialize/LatticeStatisticProvider.java   | 27 ++++++
 .../apache/calcite/materialize/Lattices.java    | 40 +++++++++
 .../SqlLatticeStatisticProvider.java            | 48 ++++++++++
 .../org/apache/calcite/model/JsonLattice.java   | 13 +++
 .../org/apache/calcite/model/ModelHandler.java  |  3 +
 .../test/FoodMartLatticeStatisticProvider.java  | 92 +++++++++++++++++++
 .../org/apache/calcite/test/LatticeTest.java    | 28 ++++--
 site/_docs/model.md                             | 17 +++-
 16 files changed, 517 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java b/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
index 5afba20..4a0c26c 100644
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
+++ b/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
@@ -27,6 +27,7 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
+    AvaticaUtilsTest.class,
     ConnectStringParserTest.class,
     RemoteDriverTest.class
 })

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/avatica/pom.xml
----------------------------------------------------------------------
diff --git a/avatica/pom.xml b/avatica/pom.xml
index 3e241db..50b80c7 100644
--- a/avatica/pom.xml
+++ b/avatica/pom.xml
@@ -53,6 +53,11 @@ limitations under the License.
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-core</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
index 77fafc4..5ca5245 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica;
 
+import java.lang.reflect.Field;
 import java.util.AbstractList;
 import java.util.HashMap;
 import java.util.List;
@@ -117,6 +118,53 @@ public class AvaticaUtils {
     }
     return clazz;
   }
+
+  /** Creates an instance of a plugin class. First looks for a static
+   * member called INSTANCE, then calls a public default constructor.
+   *
+   * <p>If className contains a "#" instead looks for a static field.
+   *
+   * @param pluginClass Class (or interface) to instantiate
+   * @param className Name of implementing class
+   * @param <T> Class
+   * @return Plugin instance
+   */
+  public static <T> T instantiatePlugin(Class<T> pluginClass,
+      String className) {
+    try {
+      // Given a static field, say "com.example.MyClass#FOO_INSTANCE", return
+      // the value of that static field.
+      if (className.contains("#")) {
+        try {
+          int i = className.indexOf('#');
+          String left = className.substring(0, i);
+          String right = className.substring(i + 1);
+          //noinspection unchecked
+          final Class<T> clazz = (Class) Class.forName(left);
+          final Field field;
+          field = clazz.getField(right);
+          return pluginClass.cast(field.get(null));
+        } catch (NoSuchFieldException e) {
+          // ignore
+        }
+      }
+      //noinspection unchecked
+      final Class<T> clazz = (Class) Class.forName(className);
+      assert pluginClass.isAssignableFrom(clazz);
+      try {
+        // We assume that if there is an INSTANCE field it is static and
+        // has the right type.
+        final Field field = clazz.getField("INSTANCE");
+        return pluginClass.cast(field.get(null));
+      } catch (NoSuchFieldException e) {
+        // ignore
+      }
+      return clazz.newInstance();
+    } catch (Exception e) {
+      throw new RuntimeException("Property '" + className
+          + "' not valid for plugin type " + pluginClass.getName(), e);
+    }
+  }
 }
 
 // End AvaticaUtils.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/avatica/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
index 300063f..3200bee 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
@@ -18,7 +18,6 @@ package org.apache.calcite.avatica;
 
 import org.apache.calcite.avatica.remote.Service;
 
-import java.lang.reflect.Field;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Properties;
@@ -217,24 +216,7 @@ public class ConnectionConfigImpl implements ConnectionConfig {
           throw new RuntimeException("Required property '"
               + connectionProperty.camelName() + "' not specified");
         }
-        // First look for a C.INSTANCE field, then do new C().
-        try {
-          //noinspection unchecked
-          final Class<T> clazz = (Class) Class.forName(s);
-          assert pluginClass.isAssignableFrom(clazz);
-          try {
-            // We assume that if there is an INSTANCE field it is static and
-            // has the right type.
-            final Field field = clazz.getField("INSTANCE");
-            return pluginClass.cast(field.get(null));
-          } catch (NoSuchFieldException e) {
-            // ignore
-          }
-          return clazz.newInstance();
-        } catch (Exception e) {
-          throw new RuntimeException("Property '" + s
-              + "' not valid for plugin type " + pluginClass.getName(), e);
-        }
+        return AvaticaUtils.instantiatePlugin(pluginClass, s);
       }
     };
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
new file mode 100644
index 0000000..ca548a6
--- /dev/null
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.avatica.test;
+
+import org.apache.calcite.avatica.AvaticaUtils;
+
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit test for Avatica utilities.
+ */
+public class AvaticaUtilsTest {
+  @Test public void testInstantiatePlugin() {
+    final String s =
+        AvaticaUtils.instantiatePlugin(String.class, "java.lang.String");
+    assertThat(s, is(""));
+
+    // No default constructor or INSTANCE member
+    try {
+      final Integer i =
+          AvaticaUtils.instantiatePlugin(Integer.class, "java.lang.Integer");
+      fail("expected error, got " + i);
+    } catch (Throwable e) {
+      assertThat(e.getMessage(),
+          is("Property 'java.lang.Integer' not valid for plugin type java.lang.Integer"));
+    }
+
+    final BigInteger b =
+        AvaticaUtils.instantiatePlugin(BigInteger.class, "java.math.BigInteger#ONE");
+    assertThat(b, is(BigInteger.ONE));
+
+    try {
+      final BigInteger b2 =
+          AvaticaUtils.instantiatePlugin(BigInteger.class,
+              "java.math.BigInteger.ONE");
+      fail("expected error, got " + b2);
+    } catch (Throwable e) {
+      assertThat(e.getMessage(),
+          is("Property 'java.math.BigInteger.ONE' not valid for plugin type java.math.BigInteger"));
+    }
+  }
+}
+
+// End AvaticaUtilsTest.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java b/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java
new file mode 100644
index 0000000..bf70e5d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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.materialize;
+
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Implementation of {@link LatticeStatisticProvider} that gets statistics by
+ * executing "SELECT COUNT(DISTINCT ...) ..." SQL queries.
+ */
+class CachingLatticeStatisticProvider implements LatticeStatisticProvider {
+  private final LoadingCache<Pair<Lattice, Lattice.Column>, Integer> cache;
+
+  /** Creates a CachingStatisticProvider. */
+  public CachingLatticeStatisticProvider(
+      final LatticeStatisticProvider provider) {
+    cache = CacheBuilder.<Pair<Lattice, Lattice.Column>>newBuilder()
+        .build(
+            new CacheLoader<Pair<Lattice, Lattice.Column>, Integer>() {
+              public Integer load(Pair<Lattice, Lattice.Column> key)
+                  throws Exception {
+                return provider.cardinality(key.left, key.right);
+              }
+            });
+  }
+
+  public int cardinality(Lattice lattice, Lattice.Column column) {
+    try {
+      return cache.get(Pair.of(lattice, column));
+    } catch (ExecutionException e) {
+      throw Throwables.propagate(e);
+    }
+  }
+}
+
+// End CachingLatticeStatisticProvider.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/DelegatingLatticeStatisticProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/DelegatingLatticeStatisticProvider.java b/core/src/main/java/org/apache/calcite/materialize/DelegatingLatticeStatisticProvider.java
new file mode 100644
index 0000000..e1ec6c5
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/materialize/DelegatingLatticeStatisticProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.materialize;
+
+/**
+ * Implementation of {@link LatticeStatisticProvider} that delegates
+ * to an underlying provider.
+ */
+public class DelegatingLatticeStatisticProvider
+    implements LatticeStatisticProvider {
+  protected final LatticeStatisticProvider provider;
+
+  /** Creates a DelegatingLatticeStatisticProvider.
+   *
+   * @param provider Provider to which to delegate otherwise unhandled requests
+   */
+  protected DelegatingLatticeStatisticProvider(
+      LatticeStatisticProvider provider) {
+    this.provider = provider;
+  }
+
+  public int cardinality(Lattice lattice, Lattice.Column column) {
+    return provider.cardinality(lattice, column);
+  }
+}
+
+// End DelegatingLatticeStatisticProvider.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/Lattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattice.java b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
index 38b4eac..c2b07c3 100644
--- a/core/src/main/java/org/apache/calcite/materialize/Lattice.java
+++ b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
@@ -16,7 +16,9 @@
  */
 package org.apache.calcite.materialize;
 
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteRootSchema;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
@@ -55,7 +57,6 @@ import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Ordering;
@@ -85,6 +86,7 @@ public class Lattice {
         }
       };
 
+  public final CalciteRootSchema rootSchema;
   public final ImmutableList<Node> nodes;
   public final ImmutableList<Column> columns;
   public final boolean auto;
@@ -94,6 +96,7 @@ public class Lattice {
   public final ImmutableList<Measure> defaultMeasures;
   public final ImmutableList<Tile> tiles;
   public final ImmutableList<String> uniqueColumnNames;
+  public final LatticeStatisticProvider statisticProvider;
 
   private final Function<Integer, Column> toColumnFunction =
       new Function<Integer, Column>() {
@@ -109,15 +112,18 @@ public class Lattice {
         }
       };
 
-  private Lattice(ImmutableList<Node> nodes, boolean auto, boolean algorithm,
-      long algorithmMaxMillis,
-      Double rowCountEstimate, ImmutableList<Column> columns,
-      ImmutableList<Measure> defaultMeasures, ImmutableList<Tile> tiles) {
+  private Lattice(CalciteRootSchema rootSchema, ImmutableList<Node> nodes,
+      boolean auto, boolean algorithm, long algorithmMaxMillis,
+      LatticeStatisticProvider statisticProvider, Double rowCountEstimate,
+      ImmutableList<Column> columns, ImmutableList<Measure> defaultMeasures,
+      ImmutableList<Tile> tiles) {
+    this.rootSchema = rootSchema;
     this.nodes = Preconditions.checkNotNull(nodes);
     this.columns = Preconditions.checkNotNull(columns);
     this.auto = auto;
     this.algorithm = algorithm;
     this.algorithmMaxMillis = algorithmMaxMillis;
+    this.statisticProvider = Preconditions.checkNotNull(statisticProvider);
     this.defaultMeasures = Preconditions.checkNotNull(defaultMeasures);
     this.tiles = Preconditions.checkNotNull(tiles);
 
@@ -328,6 +334,14 @@ public class Lattice {
     return buf.toString();
   }
 
+  /** Returns a SQL query that counts the number of distinct values of the
+   * attributes given in {@code groupSet}. */
+  public String countSql(ImmutableBitSet groupSet) {
+    return "select count(*) as c from ("
+        + sql(groupSet, ImmutableList.<Measure>of())
+        + ")";
+  }
+
   private static void use(List<Node> usedNodes, Node node) {
     if (!usedNodes.contains(node)) {
       if (node.parent != null) {
@@ -376,7 +390,7 @@ public class Lattice {
     // distributed attribute with N1 * ... * Nm distinct values.
     BigInteger n = BigInteger.ONE;
     for (Column column : columns) {
-      final int cardinality = cardinality(column);
+      final int cardinality = statisticProvider.cardinality(this, column);
       if (cardinality > 1) {
         n = n.multiply(BigInteger.valueOf(cardinality));
       }
@@ -394,53 +408,6 @@ public class Lattice {
     return Math.min(v, f);
   }
 
-  public static final Map<String, Integer> CARDINALITY_MAP =
-      ImmutableMap.<String, Integer>builder()
-          .put("brand_name", 111)
-          .put("cases_per_pallet", 10)
-          .put("customer_id", 5581)
-          .put("day_of_month", 30)
-          .put("fiscal_period", 0)
-          .put("gross_weight", 376)
-          .put("low_fat", 2)
-          .put("month_of_year", 12)
-          .put("net_weight", 332)
-          .put("product_category", 45)
-          .put("product_class_id", 102)
-          .put("product_department", 22)
-          .put("product_family", 3)
-          .put("product_id", 1559)
-          .put("product_name", 1559)
-          .put("product_subcategory", 102)
-          .put("promotion_id", 149)
-          .put("quarter", 4)
-          .put("recyclable_package", 2)
-          .put("shelf_depth", 488)
-          .put("shelf_height", 524)
-          .put("shelf_width", 534)
-          .put("SKU", 1559)
-          .put("SRP", 315)
-          .put("store_cost", 10777)
-          .put("store_id", 13)
-          .put("store_sales", 1049)
-          .put("the_date", 323)
-          .put("the_day", 7)
-          .put("the_month", 12)
-          .put("the_year", 1)
-          .put("time_id", 323)
-          .put("units_per_case", 36)
-          .put("unit_sales", 6)
-          .put("week_of_year", 52)
-          .build();
-
-  private int cardinality(Column column) {
-    final Integer integer = CARDINALITY_MAP.get(column.alias);
-    if (integer != null && integer > 0) {
-      return integer;
-    }
-    return column.alias.length();
-  }
-
   /** Source relation of a lattice.
    *
    * <p>Relations form a tree; all relations except the root relation
@@ -601,12 +568,15 @@ public class Lattice {
         ImmutableList.builder();
     private final ImmutableList.Builder<Tile> tileListBuilder =
         ImmutableList.builder();
+    private final CalciteRootSchema rootSchema;
     private boolean algorithm = false;
     private long algorithmMaxMillis = -1;
     private boolean auto = true;
     private Double rowCountEstimate;
+    private String statisticProvider;
 
     public Builder(CalciteSchema schema, String sql) {
+      this.rootSchema = schema.root();
       CalcitePrepare.ConvertResult parsed =
           Schemas.convert(MaterializedViewTable.MATERIALIZATION_CONNECTION,
               schema, schema.path(null), sql);
@@ -710,11 +680,24 @@ public class Lattice {
       return this;
     }
 
+    /** Sets the "statisticProvider" attribute.
+     *
+     * <p>If not set, the lattice will use {@link Lattices#CACHED_SQL}. */
+    public Builder statisticProvider(String statisticProvider) {
+      this.statisticProvider = statisticProvider;
+      return this;
+    }
+
     /** Builds a lattice. */
     public Lattice build() {
-      return new Lattice(ImmutableList.copyOf(nodes), auto, algorithm,
-          algorithmMaxMillis, rowCountEstimate, columns,
-          defaultMeasureListBuilder.build(), tileListBuilder.build());
+      LatticeStatisticProvider statisticProvider =
+          this.statisticProvider != null
+              ? AvaticaUtils.instantiatePlugin(LatticeStatisticProvider.class,
+                  this.statisticProvider)
+              : Lattices.CACHED_SQL;
+      return new Lattice(rootSchema, ImmutableList.copyOf(nodes), auto,
+          algorithm, algorithmMaxMillis, statisticProvider, rowCountEstimate,
+          columns, defaultMeasureListBuilder.build(), tileListBuilder.build());
     }
 
     /** Resolves the arguments of a

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/LatticeStatisticProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/LatticeStatisticProvider.java b/core/src/main/java/org/apache/calcite/materialize/LatticeStatisticProvider.java
new file mode 100644
index 0000000..46668cc
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/materialize/LatticeStatisticProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.materialize;
+
+/**
+ * Estimates row counts for a lattice and its attributes.
+ */
+public interface LatticeStatisticProvider {
+  /** Returns an estimate of the number of distinct values in a column. */
+  int cardinality(Lattice lattice, Lattice.Column column);
+}
+
+// End LatticeStatisticProvider.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/Lattices.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattices.java b/core/src/main/java/org/apache/calcite/materialize/Lattices.java
new file mode 100644
index 0000000..cf70719
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/materialize/Lattices.java
@@ -0,0 +1,40 @@
+/*
+ * 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.materialize;
+
+/**
+ * Utilities for {@link Lattice}, {@link LatticeStatisticProvider}.
+ */
+public class Lattices {
+  private Lattices() {}
+
+  /** Statistics provider that uses SQL. */
+  public static final LatticeStatisticProvider SQL =
+      SqlLatticeStatisticProvider.INSTANCE;
+
+  /** Statistics provider that uses SQL then stores the results in a cache. */
+  public static final LatticeStatisticProvider CACHED_SQL =
+      cache(SqlLatticeStatisticProvider.INSTANCE);
+
+  /** Wraps a statistic provider in a cache. */
+  public static LatticeStatisticProvider cache(
+      LatticeStatisticProvider provider) {
+    return new CachingLatticeStatisticProvider(provider);
+  }
+}
+
+// End Lattices.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java b/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java
new file mode 100644
index 0000000..4a94847
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/materialize/SqlLatticeStatisticProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.materialize;
+
+import org.apache.calcite.schema.ScannableTable;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.util.ImmutableBitSet;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+/**
+ * Implementation of {@link LatticeStatisticProvider} that gets statistics by
+ * executing "SELECT COUNT(DISTINCT ...) ..." SQL queries.
+ */
+class SqlLatticeStatisticProvider implements LatticeStatisticProvider {
+  static final SqlLatticeStatisticProvider INSTANCE =
+      new SqlLatticeStatisticProvider();
+
+  /** Creates an SqlLatticeStatisticProvider. */
+  private SqlLatticeStatisticProvider() {}
+
+  @Override public int cardinality(Lattice lattice, Lattice.Column column) {
+    final String sql = lattice.countSql(ImmutableBitSet.of(column.ordinal));
+    final Table table =
+        new MaterializationService.DefaultTableFactory()
+            .createTable(lattice.rootSchema, sql, ImmutableList.<String>of());
+    final Object[] values =
+        Iterables.getOnlyElement(((ScannableTable) table).scan(null));
+    return ((Number) values[0]).intValue();
+  }
+}
+
+// End SqlLatticeStatisticProvider.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/model/JsonLattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/model/JsonLattice.java b/core/src/main/java/org/apache/calcite/model/JsonLattice.java
index 80974a3..d96eb06 100644
--- a/core/src/main/java/org/apache/calcite/model/JsonLattice.java
+++ b/core/src/main/java/org/apache/calcite/model/JsonLattice.java
@@ -55,6 +55,19 @@ public class JsonLattice {
    * <p>If null, Calcite will a query to find the real value. */
   public Double rowCountEstimate;
 
+  /** Name of a class that provides estimates of the number of distinct values
+   * in each column.
+   *
+   * <p>The class must implement the
+   * {@link org.apache.calcite.materialize.LatticeStatisticProvider} interface.
+   *
+   * <p>Or, you can use a class name plus a static field, for example
+   * "org.apache.calcite.materialize.Lattices#CACHING_SQL_STATISTIC_PROVIDER".
+   *
+   * <p>If not set, Calcite will generate and execute a SQL query to find the
+   * real value, and cache the results. */
+  public String statisticProvider;
+
   /** List of materialized aggregates to create up front. */
   public final List<JsonTile> tiles = Lists.newArrayList();
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/main/java/org/apache/calcite/model/ModelHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/model/ModelHandler.java b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
index dfdb901..0c0efa8 100644
--- a/core/src/main/java/org/apache/calcite/model/ModelHandler.java
+++ b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
@@ -291,6 +291,9 @@ public class ModelHandler {
       if (jsonLattice.rowCountEstimate != null) {
         latticeBuilder.rowCountEstimate(jsonLattice.rowCountEstimate);
       }
+      if (jsonLattice.statisticProvider != null) {
+        latticeBuilder.statisticProvider(jsonLattice.statisticProvider);
+      }
       populateLattice(jsonLattice, latticeBuilder);
       schema.add(jsonLattice.name, latticeBuilder.build());
     } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/test/java/org/apache/calcite/test/FoodMartLatticeStatisticProvider.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/FoodMartLatticeStatisticProvider.java b/core/src/test/java/org/apache/calcite/test/FoodMartLatticeStatisticProvider.java
new file mode 100644
index 0000000..3f36bf9
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/FoodMartLatticeStatisticProvider.java
@@ -0,0 +1,92 @@
+/*
+ * 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.test;
+
+import org.apache.calcite.materialize.DelegatingLatticeStatisticProvider;
+import org.apache.calcite.materialize.Lattice;
+import org.apache.calcite.materialize.LatticeStatisticProvider;
+import org.apache.calcite.materialize.Lattices;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+/**
+ * Implementation of {@link LatticeStatisticProvider}
+ * that has hard-coded values for various attributes in the FoodMart lattice.
+ *
+ * <p>This makes testing faster.
+ */
+public class FoodMartLatticeStatisticProvider
+    extends DelegatingLatticeStatisticProvider {
+  public static final FoodMartLatticeStatisticProvider INSTANCE =
+      new FoodMartLatticeStatisticProvider(Lattices.CACHED_SQL);
+
+  public static final Map<String, Integer> CARDINALITY_MAP =
+      ImmutableMap.<String, Integer>builder()
+          .put("brand_name", 111)
+          .put("cases_per_pallet", 10)
+          .put("customer_id", 5581)
+          .put("day_of_month", 30)
+          .put("fiscal_period", 0)
+          .put("gross_weight", 376)
+          .put("low_fat", 2)
+          .put("month_of_year", 12)
+          .put("net_weight", 332)
+          .put("product_category", 45)
+          .put("product_class_id", 102)
+          .put("product_department", 22)
+          .put("product_family", 3)
+          .put("product_id", 1559)
+          .put("product_name", 1559)
+          .put("product_subcategory", 102)
+          .put("promotion_id", 149)
+          .put("quarter", 4)
+          .put("recyclable_package", 2)
+          .put("shelf_depth", 488)
+          .put("shelf_height", 524)
+          .put("shelf_width", 534)
+          .put("SKU", 1559)
+          .put("SRP", 315)
+          .put("store_cost", 10777)
+          .put("store_id", 13)
+          .put("store_sales", 1049)
+          .put("the_date", 323)
+          .put("the_day", 7)
+          .put("the_month", 12)
+          .put("the_year", 1)
+          .put("time_id", 323)
+          .put("units_per_case", 36)
+          .put("unit_sales", 6)
+          .put("week_of_year", 52)
+          .build();
+
+  private FoodMartLatticeStatisticProvider(LatticeStatisticProvider provider) {
+    super(provider);
+  }
+
+  /** Returns an estimate of the number of distinct values in a column. */
+  public int cardinality(Lattice lattice, Lattice.Column column) {
+    final Integer integer = CARDINALITY_MAP.get(column.alias);
+    if (integer != null && integer > 0) {
+      return integer;
+    }
+    return column.alias.length();
+  }
+}
+
+// End FoodMartLatticeStatisticProvider.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/core/src/test/java/org/apache/calcite/test/LatticeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/LatticeTest.java b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
index 492b377..b4a79af 100644
--- a/core/src/test/java/org/apache/calcite/test/LatticeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.test;
 
+import org.apache.calcite.materialize.Lattices;
 import org.apache.calcite.materialize.MaterializationService;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
@@ -309,10 +310,25 @@ public class LatticeTest {
    * tiles.
    *
    * <p>Test case for
-   * <a href="https://issues.apache.org/jira/browse/CALCITE-428">CALCITE-428,
-   * "Use optimization algorithm to suggest which tiles of a lattice to
-   * materialize"</a>. */
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-428">[CALCITE-428]
+   * Use optimization algorithm to suggest which tiles of a lattice to
+   * materialize</a>. */
   @Test public void testTileAlgorithm() {
+    checkTileAlgorithm(FoodMartLatticeStatisticProvider.class.getCanonicalName(),
+        "EnumerableAggregate(group=[{2, 3}])\n"
+            + "  EnumerableTableScan(table=[[adhoc, m{16, 17, 27, 31}]])");
+  }
+
+  @Test public void testTileAlgorithm2() {
+    // Different explain than above, but note that it still selects columns
+    // (27, 31).
+    checkTileAlgorithm(Lattices.class.getCanonicalName() + "#CACHED_SQL",
+        "EnumerableAggregate(group=[{0, 1}])\n"
+            + "  EnumerableTableScan(table=[[adhoc, m{27, 31, 32, 36, 37}]");
+  }
+
+  private void checkTileAlgorithm(String statisticProvider,
+      String expectedExplain) {
     MaterializationService.setThreadLocal();
     MaterializationService.instance().clear();
     foodmartModel(
@@ -329,6 +345,9 @@ public class LatticeTest {
         + "    }, {\n"
         + "      agg: 'count'\n"
         + "  } ],\n"
+        + "  statisticProvider: '"
+        + statisticProvider
+        + "',\n"
         + "  tiles: [ {\n"
         + "    dimensions: [ 'the_year', ['t', 'quarter'] ],\n"
         + "    measures: [ ]\n"
@@ -337,8 +356,7 @@ public class LatticeTest {
             + "from \"foodmart\".\"sales_fact_1997\" as s\n"
             + "join \"foodmart\".\"time_by_day\" as t using (\"time_id\")\n")
         .enableMaterializations(true)
-        .explainContains("EnumerableAggregate(group=[{2, 3}])\n"
-            + "  EnumerableTableScan(table=[[adhoc, m{16, 17, 27, 31}]])")
+        .explainContains(expectedExplain)
         .returnsUnordered("the_year=1997; quarter=Q1",
             "the_year=1997; quarter=Q2",
             "the_year=1997; quarter=Q3",

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4cc539fc/site/_docs/model.md
----------------------------------------------------------------------
diff --git a/site/_docs/model.md b/site/_docs/model.md
index 78061b0..a6514a3 100644
--- a/site/_docs/model.md
+++ b/site/_docs/model.md
@@ -21,6 +21,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 {% endcomment %}
 -->
+{% assign apiRoot = "/apidocs" %}
 
 Calcite models can be represented as JSON files.
 This page describes the structure of those files.
@@ -148,7 +149,8 @@ Like base class <a href="#schema">Schema</a>, occurs within `root.schemas`.
 <a href="#schema">Schema</a>.
 
 `factory` (required string) is the name of the factory class for this
-schema. Must implement interface `org.apache.calcite.schema.SchemaFactory`
+schema. Must implement interface
+[org.apache.calcite.schema.SchemaFactory]({{ apiRoot }}/org/apache/calcite/schema/SchemaFactory.html)
 and have a public default constructor.
 
 `operand` (optional map) contains attributes to be passed to the
@@ -293,7 +295,8 @@ Like base class <a href="#table">Table</a>, occurs within `root.schemas.tables`.
 `name`, `type`, `columns` inherited from <a href="#table">Table</a>.
 
 `factory` (required string) is the name of the factory class for this
-table. Must implement interface `org.apache.calcite.schema.TableFactory`
+table. Must implement interface
+[org.apache.calcite.schema.TableFactory]({{ apiRoot }}/org/apache/calcite/schema/TableFactory.html)
 and have a public default constructor.
 
 `operand` (optional map) contains attributes to be passed to the
@@ -401,6 +404,16 @@ just 'count(*)':
 [ { name: 'count' } ]
 {% endhighlight %}
 
+`statisticProvider` (optional name of a class that implements 
+[org.apache.calcite.materialize.LatticeStatisticProvider]({{ apiRoot }}/org/apache/calcite/materialize/LatticeStatisticProvider.html))
+is provides estimates of the number of distinct values * in each column.
+
+You can use a class name, or a class plus a static field, for example
+`org.apache.calcite.materialize.Lattices#CACHING_SQL_STATISTIC_PROVIDER`.
+
+If not set, Calcite will generate and execute a SQL query to find the real
+value, and cache the results.
+
 See also: <a href="lattice.md">Lattices</a>.
 
 ### Tile



[3/7] incubator-calcite git commit: Test case for [CALCITE-754] Validator error when resolving OVER clause of JOIN query (Hsuan-Yi Chu)

Posted by jh...@apache.org.
Test case for [CALCITE-754] Validator error when resolving OVER clause of JOIN query (Hsuan-Yi Chu)

Close apache/incubator-calcite#93


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

Branch: refs/heads/master
Commit: ebce955d700602720882aa01b6ddead9ab10bde0
Parents: 4cc539f
Author: Hsuan-Yi Chu <hs...@usc.edu>
Authored: Mon Jun 8 15:04:54 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jun 11 23:10:30 2015 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/test/SqlValidatorTest.java   | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/ebce955d/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 2bf64c7..4b7c1f8 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4259,6 +4259,22 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         "Column 'HIREDATE' not found in any table");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-754">[CALCITE-754]
+   * Validator error when resolving OVER clause of JOIN query</a>. */
+  @Ignore
+  @Test public void testPartitionByColumnInJoinAlias() {
+    sql("select sum(1) over(partition by t1.ename) \n"
+            + "from emp t1, emp t2")
+        .ok();
+    sql("select sum(1) over(partition by emp.ename) \n"
+            + "from emp, dept")
+        .ok();
+    sql("select sum(1) over(partition by ^deptno^) \n"
+            + "from emp, dept")
+        .fails("Column 'DEPTNO' is ambiguous");
+  }
+
   @Test public void testWindowNegative() {
     // Do not fail when window has negative size. Allow
     final String negSize = null;


[4/7] incubator-calcite git commit: Remove deprecated SqlTypeName methods

Posted by jh...@apache.org.
Remove deprecated SqlTypeName methods


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

Branch: refs/heads/master
Commit: 6718963132e8984877c379bc5fc95534b31022e4
Parents: 87acda3
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Jun 11 22:55:39 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jun 11 23:46:07 2015 -0700

----------------------------------------------------------------------
 .../apache/calcite/sql/type/SqlTypeName.java    | 42 --------------------
 .../apache/calcite/test/SqlValidatorTest.java   |  9 +++--
 2 files changed, 5 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/67189631/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
index a7f6880..5f5b90c 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
@@ -17,7 +17,6 @@
 package org.apache.calcite.sql.type;
 
 import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.util.Util;
@@ -299,18 +298,6 @@ public enum SqlTypeName {
   }
 
   /**
-   * Returns the default precision for this type if supported, otherwise -1 if
-   * precision is either unsupported or must be specified explicitly.
-   *
-   * @deprecated Use
-   * {@link org.apache.calcite.rel.type.RelDataTypeSystem#getDefaultPrecision(SqlTypeName)};
-   * will be removed after calcite-0.9.1.
-   */
-  public int getDefaultPrecision() {
-    return RelDataTypeSystem.DEFAULT.getDefaultPrecision(this);
-  }
-
-  /**
    * @return default scale for this type if supported, otherwise -1 if scale
    * is either unsupported or must be specified explicitly
    */
@@ -676,35 +663,6 @@ public enum SqlTypeName {
   }
 
   /**
-   * Returns the maximum precision (or length) allowed for this type, or -1 if
-   * precision/length are not applicable for this type.
-   *
-   * @return Maximum allowed precision
-   *
-   * @deprecated Use
-   * {@link org.apache.calcite.rel.type.RelDataTypeSystem#getMaxScale(SqlTypeName)};
-   * will be removed after calcite-0.9.1.
-   */
-  public int getMaxPrecision() {
-    return RelDataTypeSystem.DEFAULT.getMaxPrecision(this);
-  }
-
-  /**
-   * Returns the maximum scale (or fractional second precision in the case of
-   * intervals) allowed for this type, or -1 if precision/length are not
-   * applicable for this type.
-   *
-   * @return Maximum allowed scale
-   *
-   * @deprecated Use
-   * {@link org.apache.calcite.rel.type.RelDataTypeSystem#getMaxScale(SqlTypeName)};
-   * will be removed after calcite-0.9.1.
-   */
-  public int getMaxScale() {
-    return RelDataTypeSystem.DEFAULT.getMaxScale(this);
-  }
-
-  /**
    * Returns the minimum precision (or length) allowed for this type, or -1 if
    * precision/length are not applicable for this type.
    *

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/67189631/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index af82649..811343b 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -3430,10 +3430,11 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         SqlTypeName.INTERVAL_YEAR_MONTH.getMinPrecision() == 1);
     assertTrue(
         SqlTypeName.INTERVAL_DAY_TIME.getMinPrecision() == 1);
-    assertTrue(
-        SqlTypeName.INTERVAL_YEAR_MONTH.getMaxPrecision() == 10);
-    assertTrue(
-        SqlTypeName.INTERVAL_DAY_TIME.getMaxPrecision() == 10);
+    final RelDataTypeSystem defTypeSystem = RelDataTypeSystem.DEFAULT;
+    assertEquals(10,
+        defTypeSystem.getMaxPrecision(SqlTypeName.INTERVAL_YEAR_MONTH));
+    assertEquals(10,
+        defTypeSystem.getMaxPrecision(SqlTypeName.INTERVAL_DAY_TIME));
     assertEquals(2,
         typeSystem.getDefaultPrecision(SqlTypeName.INTERVAL_YEAR_MONTH));
     assertEquals(2,


[6/7] incubator-calcite git commit: Fix coverity warnings

Posted by jh...@apache.org.
Fix coverity warnings


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

Branch: refs/heads/master
Commit: 2b2176577c9c36466772eee8636dca159bcc5d9a
Parents: 6718963
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Jun 11 23:12:26 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jun 11 23:46:07 2015 -0700

----------------------------------------------------------------------
 .../src/main/java/org/apache/calcite/schema/Schemas.java | 11 ++++++++++-
 .../main/java/org/apache/calcite/tools/RelBuilder.java   |  5 +++--
 2 files changed, 13 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2b217657/core/src/main/java/org/apache/calcite/schema/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index 7c09ffb..aa53419 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -473,9 +473,18 @@ public final class Schemas {
     }
   }
 
+  /** Returns a sub-schema of a given schema obtained by following a sequence
+   * of names.
+   *
+   * <p>The result is null if the initial schema is null or any sub-schema does
+   * not exist.
+   */
   public static CalciteSchema subSchema(CalciteSchema schema,
-        List<String> names) {
+      Iterable<String> names) {
     for (String string : names) {
+      if (schema == null) {
+        return null;
+      }
       schema = schema.getSubSchema(string, false);
     }
     return schema;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2b217657/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index ce185d8..f664baf 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -864,14 +864,15 @@ public class RelBuilder {
     }
     final RexNode offsetNode = offset <= 0 ? null : literal(offset);
     final RexNode fetchNode = fetch < 0 ? null : literal(fetch);
-    if (extraNodes.size() > inputRowType.getFieldCount()) {
+    final boolean addedFields = extraNodes.size() > originalExtraNodes.size();
+    if (addedFields) {
       project(extraNodes);
     }
     final RelNode sort =
         sortFactory.createSort(build(), RelCollations.of(fieldCollations),
             offsetNode, fetchNode);
     push(sort);
-    if (extraNodes.size() > inputRowType.getFieldCount()) {
+    if (addedFields) {
       project(originalExtraNodes);
     }
     return this;


[5/7] incubator-calcite git commit: [CALCITE-754] Validator error when resolving OVER clause of JOIN query

Posted by jh...@apache.org.
[CALCITE-754] Validator error when resolving OVER clause of JOIN query


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

Branch: refs/heads/master
Commit: 87acda3179d6a5536f8af1506d5048ef06aeb591
Parents: ebce955
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Jun 11 22:52:47 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jun 11 23:46:07 2015 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/sql/SqlNode.java    |  5 +-
 .../java/org/apache/calcite/sql/SqlWindow.java  | 32 ++-------
 .../apache/calcite/sql/fun/SqlCastFunction.java | 72 ++++++--------------
 .../calcite/sql/validate/SelectNamespace.java   |  2 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  | 18 +----
 .../calcite/sql/validate/SqlValidatorUtil.java  | 39 +++++++++++
 .../calcite/sql2rel/SqlToRelConverter.java      |  1 +
 .../apache/calcite/test/SqlValidatorTest.java   |  1 -
 8 files changed, 72 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/SqlNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlNode.java b/core/src/main/java/org/apache/calcite/sql/SqlNode.java
index 6708ba5..6531114 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlNode.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlNode.java
@@ -105,6 +105,7 @@ public abstract class SqlNode implements Cloneable {
     return getKind().belongsTo(category);
   }
 
+  @Deprecated // to be removed before 2.0
   public static SqlNode[] cloneArray(SqlNode[] nodes) {
     SqlNode[] clones = nodes.clone();
     for (int i = 0; i < clones.length; i++) {
@@ -279,11 +280,13 @@ public abstract class SqlNode implements Cloneable {
 
   /**
    * Returns whether expression is always ascending, descending or constant.
-   * This property is useful because it allows to safely aggregte infinite
+   * This property is useful because it allows to safely aggregate infinite
    * streams of values.
    *
    * <p>The default implementation returns
    * {@link SqlMonotonicity#NOT_MONOTONIC}.
+   *
+   * @param scope Scope
    */
   public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) {
     return SqlMonotonicity.NOT_MONOTONIC;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/SqlWindow.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWindow.java b/core/src/main/java/org/apache/calcite/sql/SqlWindow.java
index afc6b45..a8ae456 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlWindow.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlWindow.java
@@ -24,16 +24,14 @@ import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.util.SqlBasicVisitor;
 import org.apache.calcite.sql.util.SqlVisitor;
-import org.apache.calcite.sql.validate.SqlMoniker;
-import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.util.ImmutableNullableList;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import static org.apache.calcite.util.Static.RESOURCE;
@@ -329,28 +327,6 @@ public class SqlWindow extends SqlCall {
     }
   }
 
-  /**
-   * This method retrieves the list of columns for the current table then
-   * walks through the list looking for a column that is monotonic (sorted)
-   */
-  static boolean isTableSorted(SqlValidatorScope scope) {
-    List<SqlMoniker> columnNames = new ArrayList<SqlMoniker>();
-
-    // REVIEW: jhyde, 2007/11/7: This is the only use of
-    // findAllColumnNames. Find a better way to detect monotonicity, then
-    // remove that method.
-    scope.findAllColumnNames(columnNames);
-    for (SqlMoniker columnName : columnNames) {
-      SqlIdentifier columnId = columnName.toIdentifier();
-      final SqlMonotonicity monotonicity =
-          scope.getMonotonicity(columnId);
-      if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   public static SqlNode createCurrentRow(SqlParserPos pos) {
     return Bound.CURRENT_ROW.symbol(pos);
   }
@@ -564,7 +540,7 @@ public class SqlWindow extends SqlCall {
 
     // 6.10 rule 6a Function RANK & DENSE_RANK require ORDER BY clause
     if (orderList.size() == 0
-        && !SqlWindow.isTableSorted(scope)
+        && !SqlValidatorUtil.containsMonotonic(scope)
         && windowCall != null
         && windowCall.getOperator().requiresOrder()) {
       throw validator.newValidationError(this, RESOURCE.funcNeedsOrderBy());
@@ -596,7 +572,7 @@ public class SqlWindow extends SqlCall {
         // requires an ORDER BY clause if frame is logical(RANGE)
         // We relax this requirement if the table appears to be
         // sorted already
-        if (!isRows() && !SqlWindow.isTableSorted(scope)) {
+        if (!isRows() && !SqlValidatorUtil.containsMonotonic(scope)) {
           throw validator.newValidationError(this,
               RESOURCE.overMissingOrderBy());
         }
@@ -619,7 +595,7 @@ public class SqlWindow extends SqlCall {
       // Validate across boundaries. 7.10 Rule 8 a-d
       checkSpecialLiterals(this, validator);
     } else if (orderList.size() == 0
-        && !SqlWindow.isTableSorted(scope)
+        && !SqlValidatorUtil.containsMonotonic(scope)
         && windowCall != null
         && windowCall.getOperator().requiresOrder()) {
       throw validator.newValidationError(this, RESOURCE.overMissingOrderBy());

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java
index 7c7c7c4..08bd3b3 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCastFunction.java
@@ -39,7 +39,8 @@ import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 
-import java.util.HashSet;
+import com.google.common.collect.ImmutableSet;
+
 import java.util.Set;
 
 import static org.apache.calcite.util.Static.RESOURCE;
@@ -74,56 +75,25 @@ public class SqlCastFunction extends SqlFunction {
    * List all casts that do not preserve monotonicity.
    */
   private Set<TypeFamilyCast> createNonMonotonicPreservingCasts() {
-    Set<TypeFamilyCast> result = new HashSet<TypeFamilyCast>();
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.EXACT_NUMERIC,
-            SqlTypeFamily.CHARACTER));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.NUMERIC,
-            SqlTypeFamily.CHARACTER));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.APPROXIMATE_NUMERIC,
-            SqlTypeFamily.CHARACTER));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.DATETIME_INTERVAL,
-            SqlTypeFamily.CHARACTER));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.CHARACTER,
-            SqlTypeFamily.EXACT_NUMERIC));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.CHARACTER,
-            SqlTypeFamily.NUMERIC));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.CHARACTER,
-            SqlTypeFamily.APPROXIMATE_NUMERIC));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.CHARACTER,
-            SqlTypeFamily.DATETIME_INTERVAL));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.DATETIME,
-            SqlTypeFamily.TIME));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.TIMESTAMP,
-            SqlTypeFamily.TIME));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.TIME,
-            SqlTypeFamily.DATETIME));
-    result.add(
-        new TypeFamilyCast(
-            SqlTypeFamily.TIME,
-            SqlTypeFamily.TIMESTAMP));
-    return result;
+    ImmutableSet.Builder<TypeFamilyCast> builder = ImmutableSet.builder();
+    add(builder, SqlTypeFamily.EXACT_NUMERIC, SqlTypeFamily.CHARACTER);
+    add(builder, SqlTypeFamily.NUMERIC, SqlTypeFamily.CHARACTER);
+    add(builder, SqlTypeFamily.APPROXIMATE_NUMERIC, SqlTypeFamily.CHARACTER);
+    add(builder, SqlTypeFamily.DATETIME_INTERVAL, SqlTypeFamily.CHARACTER);
+    add(builder, SqlTypeFamily.CHARACTER, SqlTypeFamily.EXACT_NUMERIC);
+    add(builder, SqlTypeFamily.CHARACTER, SqlTypeFamily.NUMERIC);
+    add(builder, SqlTypeFamily.CHARACTER, SqlTypeFamily.APPROXIMATE_NUMERIC);
+    add(builder, SqlTypeFamily.CHARACTER, SqlTypeFamily.DATETIME_INTERVAL);
+    add(builder, SqlTypeFamily.DATETIME, SqlTypeFamily.TIME);
+    add(builder, SqlTypeFamily.TIMESTAMP, SqlTypeFamily.TIME);
+    add(builder, SqlTypeFamily.TIME, SqlTypeFamily.DATETIME);
+    add(builder, SqlTypeFamily.TIME, SqlTypeFamily.TIMESTAMP);
+    return builder.build();
+  }
+
+  private void add(ImmutableSet.Builder<TypeFamilyCast> result,
+      SqlTypeFamily from, SqlTypeFamily to) {
+    result.add(new TypeFamilyCast(from, to));
   }
 
   private boolean isMonotonicPreservingCast(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
index a0e0ad2..48a45c1 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
@@ -52,7 +52,7 @@ public class SelectNamespace extends AbstractNamespace {
   //~ Methods ----------------------------------------------------------------
 
   // implement SqlValidatorNamespace, overriding return type
-  public SqlSelect getNode() {
+  @Override public SqlSelect getNode() {
     return select;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 4a47c78..f210749 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -3033,7 +3033,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       switch (modality) {
       case STREAM:
         SqlNodeList groupList = select.getGroup();
-        if (groupList == null || !containsMonotonic(scope, groupList)) {
+        if (groupList == null
+            || !SqlValidatorUtil.containsMonotonic(scope, groupList)) {
           if (fail) {
             throw newValidationError(aggregateNode,
                 Static.RESOURCE.streamMustGroupByMonotonic());
@@ -3087,21 +3088,6 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     }
   }
 
-  private static boolean containsMonotonic(SelectScope scope,
-      SqlNodeList nodes) {
-    for (SqlNode node : nodes) {
-      final SqlMonotonicity monotonicity = scope.getMonotonicity(node);
-      switch (monotonicity) {
-      case CONSTANT:
-      case NOT_MONOTONIC:
-        break;
-      default:
-        return true;
-      }
-    }
-    return false;
-  }
-
   protected void validateWindowClause(SqlSelect select) {
     final SqlNodeList windowList = select.getWindowList();
     if ((windowList == null) || (windowList.size() == 0)) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index 768852e..3ddeed4 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -571,6 +571,45 @@ public class SqlValidatorUtil {
     return ImmutableList.copyOf(flattenedBitSets);
   }
 
+  /**
+   * Returns whether there are any input columns that are sorted.
+   *
+   * <p>If so, it can be the default ORDER BY clause for a WINDOW specification.
+   * (This is an extension to the SQL standard for streaming.)
+   */
+  public static boolean containsMonotonic(SqlValidatorScope scope) {
+    for (SqlValidatorNamespace ns : children(scope)) {
+      ns = ns.resolve();
+      for (String field : ns.getRowType().getFieldNames()) {
+        if (!ns.getMonotonicity(field).mayRepeat()) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private static List<SqlValidatorNamespace> children(SqlValidatorScope scope) {
+    return scope instanceof ListScope
+        ? ((ListScope) scope).getChildren()
+        : ImmutableList.<SqlValidatorNamespace>of();
+  }
+
+  /**
+   * Returns whether any of the given expressions are sorted.
+   *
+   * <p>If so, it can be the default ORDER BY clause for a WINDOW specification.
+   * (This is an extension to the SQL standard for streaming.)
+   */
+  static boolean containsMonotonic(SelectScope scope, SqlNodeList nodes) {
+    for (SqlNode node : nodes) {
+      if (!scope.getMonotonicity(node).mayRepeat()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   //~ Inner Classes ----------------------------------------------------------
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index a190159..ec36ad8 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -3483,6 +3483,7 @@ public class SqlToRelConverter {
     for (int i = 0; i < joinList.size(); i++) {
       Object o = joinList.get(i);
       if (o instanceof List) {
+        @SuppressWarnings("unchecked")
         List<SqlNode> projectList = (List<SqlNode>) o;
         final List<RexNode> selectList = new ArrayList<>();
         final List<String> fieldNameList = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/87acda31/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 4b7c1f8..af82649 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4262,7 +4262,6 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-754">[CALCITE-754]
    * Validator error when resolving OVER clause of JOIN query</a>. */
-  @Ignore
   @Test public void testPartitionByColumnInJoinAlias() {
     sql("select sum(1) over(partition by t1.ename) \n"
             + "from emp t1, emp t2")