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 2016/03/17 21:10:08 UTC

[1/8] calcite git commit: [CALCITE-1077] Switch Calcite to the released Avatica 1.7.1

Repository: calcite
Updated Branches:
  refs/heads/master 1567ca7ab -> 82470e32d


[CALCITE-1077] Switch Calcite to the released Avatica 1.7.1


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

Branch: refs/heads/master
Commit: c2d84abd3877a51bae0d0d5a68fe55f744f2cbcf
Parents: 1567ca7
Author: Josh Elser <el...@apache.org>
Authored: Mon Mar 14 10:56:55 2016 -0400
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 11:47:17 2016 -0700

----------------------------------------------------------------------
 pom.xml | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/c2d84abd/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0bf4d3a..bc108d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,7 +53,7 @@ limitations under the License.
 
     <!-- This list is in alphabetical order. -->
     <airlift-tpch.version>0.1</airlift-tpch.version>
-    <avatica.version>1.7.0-SNAPSHOT</avatica.version>
+    <avatica.version>1.7.1</avatica.version>
     <build-helper-maven-plugin.version>1.9</build-helper-maven-plugin.version>
     <cassandra-driver-core.version>3.0.0</cassandra-driver-core.version>
     <checksum-maven-plugin.version>1.2</checksum-maven-plugin.version>
@@ -153,12 +153,6 @@ limitations under the License.
         <artifactId>avatica</artifactId>
         <version>${avatica.version}</version>
       </dependency>
-      <!--dependency>
-        <groupId>org.apache.calcite.avatica</groupId>
-        <artifactId>avatica</artifactId>
-        <version>${avatica.version}</version>
-        <type>test-jar</type>
-      </dependency-->
       <dependency>
         <groupId>org.apache.calcite.avatica</groupId>
         <artifactId>avatica-server</artifactId>


[6/8] calcite git commit: [CALCITE-1147] Allow multiple providers for the same kind of metadata

Posted by jh...@apache.org.
[CALCITE-1147] Allow multiple providers for the same kind of metadata

Make it possible to extend RelMetadataQuery for a user-defined metadata
type, and create a test case illustrating.

Give an explicit error when metadata provider does not have a RelNode
handler (that is, a safety net if there are no handlers for more specific
RelNode sub-classes).

Improve how we handle ExecutionException.


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

Branch: refs/heads/master
Commit: 94f8837c4974edc33e58b16b9ee2f061125b9b60
Parents: 22aa8a8
Author: Julian Hyde <jh...@apache.org>
Authored: Fri Mar 11 15:35:59 2016 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:01 2016 -0700

----------------------------------------------------------------------
 .../CachingLatticeStatisticProvider.java        |   5 +-
 .../plan/hep/HepRelMetadataProvider.java        |   8 +-
 .../volcano/VolcanoRelMetadataProvider.java     |   8 +-
 .../metadata/CachingRelMetadataProvider.java    |   3 +-
 .../metadata/ChainedRelMetadataProvider.java    |  10 +-
 .../rel/metadata/JaninoRelMetadataProvider.java |  11 +-
 .../rel/metadata/MetadataFactoryImpl.java       |  10 +-
 .../metadata/ReflectiveRelMetadataProvider.java |  52 +++---
 .../rel/metadata/RelMetadataProvider.java       |   5 +-
 .../calcite/rel/metadata/RelMetadataQuery.java  |  93 +++++-----
 .../org/apache/calcite/test/CalciteAssert.java  |   5 +-
 .../apache/calcite/test/RelMetadataTest.java    | 170 ++++++++++++++-----
 .../apache/calcite/test/RelOptRulesTest.java    |   2 +-
 .../apache/calcite/test/SqlToRelTestBase.java   |  29 +++-
 14 files changed, 256 insertions(+), 155 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/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
index bf70e5d..bf2702c 100644
--- a/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java
+++ b/core/src/main/java/org/apache/calcite/materialize/CachingLatticeStatisticProvider.java
@@ -22,6 +22,7 @@ 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 com.google.common.util.concurrent.UncheckedExecutionException;
 
 import java.util.concurrent.ExecutionException;
 
@@ -48,8 +49,8 @@ class CachingLatticeStatisticProvider implements LatticeStatisticProvider {
   public int cardinality(Lattice lattice, Lattice.Column column) {
     try {
       return cache.get(Pair.of(lattice, column));
-    } catch (ExecutionException e) {
-      throw Throwables.propagate(e);
+    } catch (UncheckedExecutionException | ExecutionException e) {
+      throw Throwables.propagate(e.getCause());
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java
index 317fe94..3ed2b96 100644
--- a/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/plan/hep/HepRelMetadataProvider.java
@@ -24,10 +24,10 @@ import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.metadata.UnboundMetadata;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 import java.lang.reflect.Method;
-import java.util.Map;
 
 /**
  * HepRelMetadataProvider implements the {@link RelMetadataProvider} interface
@@ -62,9 +62,9 @@ class HepRelMetadataProvider implements RelMetadataProvider {
     };
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
-    return ImmutableMap.of();
+    return ImmutableMultimap.of();
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java
index c32d7d3..f996707 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRelMetadataProvider.java
@@ -24,10 +24,10 @@ import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.metadata.UnboundMetadata;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 import java.lang.reflect.Method;
-import java.util.Map;
 
 /**
  * VolcanoRelMetadataProvider implements the {@link RelMetadataProvider}
@@ -115,9 +115,9 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider {
     };
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
-    return ImmutableMap.of();
+    return ImmutableMultimap.of();
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
index 202bf4c..eb63604 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/CachingRelMetadataProvider.java
@@ -22,6 +22,7 @@ import org.apache.calcite.rel.RelNode;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
@@ -77,7 +78,7 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
     };
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
     return underlyingProvider.handlers(def);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
index 698bc9c..1ecec0f 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/ChainedRelMetadataProvider.java
@@ -20,8 +20,9 @@ import org.apache.calcite.rel.RelNode;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
@@ -29,7 +30,6 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Implementation of the {@link RelMetadataProvider}
@@ -104,10 +104,10 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
     }
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
-    final ImmutableMap.Builder<Method, MetadataHandler<M>> builder =
-        ImmutableMap.builder();
+    final ImmutableMultimap.Builder<Method, MetadataHandler<M>> builder =
+        ImmutableMultimap.builder();
     for (RelMetadataProvider provider : providers.reverse()) {
       builder.putAll(provider.handlers(def));
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
index ac7f4d1..ac395e4 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
@@ -63,6 +63,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.UncheckedExecutionException;
 
 import org.codehaus.commons.compiler.CompileException;
 import org.codehaus.commons.compiler.CompilerFactoryFactory;
@@ -182,14 +183,14 @@ public class JaninoRelMetadataProvider implements RelMetadataProvider {
     throw new UnsupportedOperationException();
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
     return provider.handlers(def);
   }
 
   private static <M extends Metadata>
   MetadataHandler<M> load3(MetadataDef<M> def,
-      Map<Method, MetadataHandler<M>> map,
+      Multimap<Method, MetadataHandler<M>> map,
       ImmutableList<Class<? extends RelNode>> relClasses) {
     final StringBuilder buff = new StringBuilder();
     final String name =
@@ -198,7 +199,7 @@ public class JaninoRelMetadataProvider implements RelMetadataProvider {
     final List<Pair<String, MetadataHandler>> providerList = new ArrayList<>();
     //noinspection unchecked
     final ReflectiveRelMetadataProvider.Space space =
-        new ReflectiveRelMetadataProvider.Space((Map) map);
+        new ReflectiveRelMetadataProvider.Space((Multimap) map);
     for (MetadataHandler provider : space.providerMap.values()) {
       if (providerSet.add(provider)) {
         providerList.add(Pair.of("provider" + (providerSet.size() - 1),
@@ -445,8 +446,8 @@ public class JaninoRelMetadataProvider implements RelMetadataProvider {
           ImmutableList.copyOf(ALL_RELS));
       //noinspection unchecked
       return (H) HANDLERS.get(key);
-    } catch (ExecutionException e) {
-      throw Throwables.propagate(e);
+    } catch (UncheckedExecutionException | ExecutionException e) {
+      throw Throwables.propagate(e.getCause());
     }
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
index f5b5717..09446be 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataFactoryImpl.java
@@ -19,9 +19,11 @@ package org.apache.calcite.rel.metadata;
 import org.apache.calcite.rel.RelNode;
 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 com.google.common.util.concurrent.UncheckedExecutionException;
 
 import java.util.concurrent.ExecutionException;
 
@@ -70,12 +72,8 @@ public class MetadataFactoryImpl implements MetadataFactory {
           (Pair) Pair.of(rel.getClass(), metadataClazz);
       final Metadata apply = cache.get(key).bind(rel, mq);
       return metadataClazz.cast(apply);
-    } catch (ExecutionException e) {
-      if (e.getCause() instanceof RuntimeException) {
-        throw (RuntimeException) e.getCause();
-      } else {
-        throw (Error) e.getCause();
-      }
+    } catch (UncheckedExecutionException | ExecutionException e) {
+      throw Throwables.propagate(e.getCause());
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
index 5f6d680..baceb35 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/ReflectiveRelMetadataProvider.java
@@ -25,9 +25,11 @@ import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.ReflectiveVisitor;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
@@ -63,7 +65,7 @@ public class ReflectiveRelMetadataProvider
   //~ Instance fields --------------------------------------------------------
   private final ConcurrentMap<Class<RelNode>, UnboundMetadata> map;
   private final Class<? extends Metadata> metadataClass0;
-  private final ImmutableMap<Method, MetadataHandler> handlerMap;
+  private final ImmutableMultimap<Method, MetadataHandler> handlerMap;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -77,11 +79,11 @@ public class ReflectiveRelMetadataProvider
   protected ReflectiveRelMetadataProvider(
       ConcurrentMap<Class<RelNode>, UnboundMetadata> map,
       Class<? extends Metadata> metadataClass0,
-      Map<Method, MetadataHandler> handlerMap) {
+      Multimap<Method, MetadataHandler> handlerMap) {
     assert !map.isEmpty() : "are your methods named wrong?";
     this.map = map;
     this.metadataClass0 = metadataClass0;
-    this.handlerMap = ImmutableMap.copyOf(handlerMap);
+    this.handlerMap = ImmutableMultimap.copyOf(handlerMap);
   }
 
   /** Returns an implementation of {@link RelMetadataProvider} that scans for
@@ -210,11 +212,11 @@ public class ReflectiveRelMetadataProvider
         space.providerMap);
   }
 
-  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  public <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def) {
-    final ImmutableMap.Builder<Method, MetadataHandler<M>> builder =
-        ImmutableMap.builder();
-    for (Map.Entry<Method, MetadataHandler> entry : handlerMap.entrySet()) {
+    final ImmutableMultimap.Builder<Method, MetadataHandler<M>> builder =
+        ImmutableMultimap.builder();
+    for (Map.Entry<Method, MetadataHandler> entry : handlerMap.entries()) {
       if (def.methods.contains(entry.getKey())) {
         //noinspection unchecked
         builder.put(entry.getKey(), entry.getValue());
@@ -288,14 +290,14 @@ public class ReflectiveRelMetadataProvider
   static class Space {
     final Set<Class<RelNode>> classes = new HashSet<>();
     final Map<Pair<Class<RelNode>, Method>, Method> handlerMap = new HashMap<>();
-    final ImmutableMap<Method, MetadataHandler> providerMap;
+    final ImmutableMultimap<Method, MetadataHandler> providerMap;
 
-    Space(Map<Method, MetadataHandler> providerMap) {
-      this.providerMap = ImmutableMap.copyOf(providerMap);
+    Space(Multimap<Method, MetadataHandler> providerMap) {
+      this.providerMap = ImmutableMultimap.copyOf(providerMap);
 
       // Find the distinct set of RelNode classes handled by this provider,
       // ordered base-class first.
-      for (Map.Entry<Method, MetadataHandler> entry : providerMap.entrySet()) {
+      for (Map.Entry<Method, MetadataHandler> entry : providerMap.entries()) {
         final Method method = entry.getKey();
         final MetadataHandler provider = entry.getValue();
         for (final Method handlerMethod : provider.getClass().getMethods()) {
@@ -313,14 +315,14 @@ public class ReflectiveRelMetadataProvider
      * nearest base class. Assumes that base classes have already been added to
      * {@code map}. */
     @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" })
-    Method find(Class<? extends RelNode> relNodeClass, Method method) {
-      Method implementingMethod;
-      while (relNodeClass != null) {
-        implementingMethod = handlerMap.get(Pair.of(relNodeClass, method));
+    Method find(final Class<? extends RelNode> relNodeClass, Method method) {
+      Preconditions.checkNotNull(relNodeClass);
+      for (Class r = relNodeClass;;) {
+        Method implementingMethod = handlerMap.get(Pair.of(r, method));
         if (implementingMethod != null) {
           return implementingMethod;
         }
-        for (Class<?> clazz : relNodeClass.getInterfaces()) {
+        for (Class<?> clazz : r.getInterfaces()) {
           if (RelNode.class.isAssignableFrom(clazz)) {
             implementingMethod = handlerMap.get(Pair.of(clazz, method));
             if (implementingMethod != null) {
@@ -328,13 +330,13 @@ public class ReflectiveRelMetadataProvider
             }
           }
         }
-        if (RelNode.class.isAssignableFrom(relNodeClass.getSuperclass())) {
-          relNodeClass = (Class<RelNode>) relNodeClass.getSuperclass();
-        } else {
-          relNodeClass = null;
+        r = r.getSuperclass();
+        if (r == null || !RelNode.class.isAssignableFrom(r)) {
+          throw new IllegalArgumentException("No handler for method [" + method
+              + "] applied to argument of type [" + relNodeClass
+              + "]; we recommend you create a catch-all (RelNode) handler");
         }
       }
-      return null;
     }
   }
 
@@ -343,7 +345,7 @@ public class ReflectiveRelMetadataProvider
     private Class<Metadata> metadataClass0;
 
     public Space2(Class<Metadata> metadataClass0,
-        ImmutableMap<Method, MetadataHandler> providerMap) {
+        ImmutableMultimap<Method, MetadataHandler> providerMap) {
       super(providerMap);
       this.metadataClass0 = metadataClass0;
     }
@@ -359,8 +361,8 @@ public class ReflectiveRelMetadataProvider
         assert method.getDeclaringClass() == metadataClass0;
       }
 
-      final ImmutableMap.Builder<Method, MetadataHandler> providerBuilder =
-          ImmutableMap.builder();
+      final ImmutableMultimap.Builder<Method, MetadataHandler> providerBuilder =
+          ImmutableMultimap.builder();
       for (final Method method : methods) {
         providerBuilder.put(method, target);
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
index c1d5f39..b4ddbe2 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataProvider.java
@@ -18,8 +18,9 @@ package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
 
+import com.google.common.collect.Multimap;
+
 import java.lang.reflect.Method;
-import java.util.Map;
 
 /**
  * RelMetadataProvider defines an interface for obtaining metadata about
@@ -65,7 +66,7 @@ public interface RelMetadataProvider {
   apply(Class<? extends RelNode> relClass,
       Class<? extends M> metadataClass);
 
-  <M extends Metadata> Map<Method, MetadataHandler<M>>
+  <M extends Metadata> Multimap<Method, MetadataHandler<M>>
   handlers(MetadataDef<M> def);
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
index abca58e..4e07612 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
@@ -80,7 +80,7 @@ public class RelMetadataQuery {
 
   public final JaninoRelMetadataProvider metadataProvider;
 
-  private static final RelMetadataQuery EMPTY = new RelMetadataQuery(false);
+  protected static final RelMetadataQuery EMPTY = new RelMetadataQuery(false);
 
   private BuiltInMetadata.Collation.Handler collationHandler;
   private BuiltInMetadata.ColumnOrigin.Handler columnOriginHandler;
@@ -108,7 +108,7 @@ public class RelMetadataQuery {
         }
       };
 
-  private RelMetadataQuery(JaninoRelMetadataProvider metadataProvider,
+  protected RelMetadataQuery(JaninoRelMetadataProvider metadataProvider,
       RelMetadataQuery prototype) {
     this.metadataProvider = Preconditions.checkNotNull(metadataProvider);
     this.collationHandler = prototype.collationHandler;
@@ -131,7 +131,7 @@ public class RelMetadataQuery {
     this.uniqueKeysHandler = prototype.uniqueKeysHandler;
   }
 
-  private static <H> H initialHandler(Class<H> handlerClass) {
+  protected static <H> H initialHandler(Class<H> handlerClass) {
     return handlerClass.cast(
         Proxy.newProxyInstance(RelMetadataQuery.class.getClassLoader(),
             new Class[] {handlerClass},
@@ -179,6 +179,13 @@ public class RelMetadataQuery {
     this.uniqueKeysHandler = initialHandler(BuiltInMetadata.UniqueKeys.Handler.class);
   }
 
+  /** Re-generates the handler for a given kind of metadata, adding support for
+   * {@code class_} if it is not already present. */
+  protected <M extends Metadata, H extends MetadataHandler<M>> H
+  revise(Class<? extends RelNode> class_, MetadataDef<M> def) {
+    return metadataProvider.revise(class_, def);
+  }
+
   /**
    * Returns the
    * {@link BuiltInMetadata.RowCount#getRowCount()}
@@ -194,8 +201,7 @@ public class RelMetadataQuery {
         Double result = rowCountHandler.getRowCount(rel, this);
         return validateResult(result);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        rowCountHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.RowCount.DEF);
+        rowCountHandler = revise(e.relClass, BuiltInMetadata.RowCount.DEF);
       }
     }
   }
@@ -213,8 +219,8 @@ public class RelMetadataQuery {
       try {
         return maxRowCountHandler.getMaxRowCount(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        maxRowCountHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.MaxRowCount.DEF);
+        maxRowCountHandler =
+            revise(e.relClass, BuiltInMetadata.MaxRowCount.DEF);
       }
     }
   }
@@ -232,8 +238,8 @@ public class RelMetadataQuery {
       try {
         return cumulativeCostHandler.getCumulativeCost(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        cumulativeCostHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.CumulativeCost.DEF);
+        cumulativeCostHandler =
+            revise(e.relClass, BuiltInMetadata.CumulativeCost.DEF);
       }
     }
   }
@@ -251,8 +257,8 @@ public class RelMetadataQuery {
       try {
         return nonCumulativeCostHandler.getNonCumulativeCost(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        nonCumulativeCostHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.NonCumulativeCost.DEF);
+        nonCumulativeCostHandler =
+            revise(e.relClass, BuiltInMetadata.NonCumulativeCost.DEF);
       }
     }
   }
@@ -273,8 +279,8 @@ public class RelMetadataQuery {
             percentageOriginalRowsHandler.getPercentageOriginalRows(rel, this);
         return validatePercentage(result);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        percentageOriginalRowsHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.PercentageOriginalRows.DEF);
+        percentageOriginalRowsHandler =
+            revise(e.relClass, BuiltInMetadata.PercentageOriginalRows.DEF);
       }
     }
   }
@@ -295,8 +301,8 @@ public class RelMetadataQuery {
       try {
         return columnOriginHandler.getColumnOrigins(rel, this, column);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        columnOriginHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.ColumnOrigin.DEF);
+        columnOriginHandler =
+            revise(e.relClass, BuiltInMetadata.ColumnOrigin.DEF);
       }
     }
   }
@@ -359,8 +365,8 @@ public class RelMetadataQuery {
         Double result = selectivityHandler.getSelectivity(rel, this, predicate);
         return validatePercentage(result);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        selectivityHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Selectivity.DEF);
+        selectivityHandler =
+            revise(e.relClass, BuiltInMetadata.Selectivity.DEF);
       }
     }
   }
@@ -396,8 +402,8 @@ public class RelMetadataQuery {
       try {
         return uniqueKeysHandler.getUniqueKeys(rel, this, ignoreNulls);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        uniqueKeysHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.UniqueKeys.DEF);
+        uniqueKeysHandler =
+            revise(e.relClass, BuiltInMetadata.UniqueKeys.DEF);
       }
     }
   }
@@ -455,8 +461,8 @@ public class RelMetadataQuery {
         return columnUniquenessHandler.areColumnsUnique(rel, this, columns,
             ignoreNulls);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        columnUniquenessHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.ColumnUniqueness.DEF);
+        columnUniquenessHandler =
+            revise(e.relClass, BuiltInMetadata.ColumnUniqueness.DEF);
       }
     }
   }
@@ -475,8 +481,7 @@ public class RelMetadataQuery {
       try {
         return collationHandler.collations(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        collationHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Collation.DEF);
+        collationHandler = revise(e.relClass, BuiltInMetadata.Collation.DEF);
       }
     }
   }
@@ -495,8 +500,8 @@ public class RelMetadataQuery {
       try {
         return distributionHandler.distribution(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        distributionHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Distribution.DEF);
+        distributionHandler =
+            revise(e.relClass, BuiltInMetadata.Distribution.DEF);
       }
     }
   }
@@ -521,8 +526,8 @@ public class RelMetadataQuery {
             populationSizeHandler.getPopulationSize(rel, this, groupKey);
         return validateResult(result);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        populationSizeHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.PopulationSize.DEF);
+        populationSizeHandler =
+            revise(e.relClass, BuiltInMetadata.PopulationSize.DEF);
       }
     }
   }
@@ -540,8 +545,7 @@ public class RelMetadataQuery {
       try {
         return sizeHandler.averageRowSize(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        sizeHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Size.DEF);
+        sizeHandler = revise(e.relClass, BuiltInMetadata.Size.DEF);
       }
     }
   }
@@ -561,8 +565,7 @@ public class RelMetadataQuery {
       try {
         return sizeHandler.averageColumnSizes(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        sizeHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Size.DEF);
+        sizeHandler = revise(e.relClass, BuiltInMetadata.Size.DEF);
       }
     }
   }
@@ -591,8 +594,8 @@ public class RelMetadataQuery {
       try {
         return parallelismHandler.isPhaseTransition(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        parallelismHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Parallelism.DEF);
+        parallelismHandler =
+            revise(e.relClass, BuiltInMetadata.Parallelism.DEF);
       }
     }
   }
@@ -610,8 +613,8 @@ public class RelMetadataQuery {
       try {
         return parallelismHandler.splitCount(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        parallelismHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Parallelism.DEF);
+        parallelismHandler =
+            revise(e.relClass, BuiltInMetadata.Parallelism.DEF);
       }
     }
   }
@@ -631,8 +634,7 @@ public class RelMetadataQuery {
       try {
         return memoryHandler.memory(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        memoryHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Memory.DEF);
+        memoryHandler = revise(e.relClass, BuiltInMetadata.Memory.DEF);
       }
     }
   }
@@ -652,8 +654,7 @@ public class RelMetadataQuery {
       try {
         return memoryHandler.cumulativeMemoryWithinPhase(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        memoryHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Memory.DEF);
+        memoryHandler = revise(e.relClass, BuiltInMetadata.Memory.DEF);
       }
     }
   }
@@ -673,8 +674,7 @@ public class RelMetadataQuery {
       try {
         return memoryHandler.cumulativeMemoryWithinPhaseSplit(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        memoryHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Memory.DEF);
+        memoryHandler = revise(e.relClass, BuiltInMetadata.Memory.DEF);
       }
     }
   }
@@ -701,8 +701,8 @@ public class RelMetadataQuery {
                 predicate);
         return validateResult(result);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        distinctRowCountHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.DistinctRowCount.DEF);
+        distinctRowCountHandler =
+            revise(e.relClass, BuiltInMetadata.DistinctRowCount.DEF);
       }
     }
   }
@@ -720,8 +720,7 @@ public class RelMetadataQuery {
       try {
         return predicatesHandler.getPredicates(rel, this);
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        predicatesHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.Predicates.DEF);
+        predicatesHandler = revise(e.relClass, BuiltInMetadata.Predicates.DEF);
       }
     }
   }
@@ -744,8 +743,8 @@ public class RelMetadataQuery {
             explainLevel);
         return b == null || b;
       } catch (JaninoRelMetadataProvider.NoHandler e) {
-        explainVisibilityHandler = metadataProvider.revise(e.relClass,
-            BuiltInMetadata.ExplainVisibility.DEF);
+        explainVisibilityHandler =
+            revise(e.relClass, BuiltInMetadata.ExplainVisibility.DEF);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 729326a..7a962a0 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -48,6 +48,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMultiset;
 import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.UncheckedExecutionException;
 
 import net.hydromatic.foodmart.data.hsqldb.FoodmartHsqldb;
 import net.hydromatic.scott.data.hsqldb.ScottHsqldb;
@@ -1072,9 +1073,9 @@ public class CalciteAssert {
     public Connection createConnection() throws SQLException {
       try {
         return Pool.POOL.get(factory);
-      } catch (ExecutionException e) {
+      } catch (UncheckedExecutionException | ExecutionException e) {
         throw new SQLException(
-            "Unable to get pooled connection for " + factory, e);
+            "Unable to get pooled connection for " + factory, e.getCause());
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index ab7e02d..562286a 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.test;
 
 import org.apache.calcite.adapter.enumerable.EnumerableMergeJoin;
+import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptPredicateList;
@@ -51,6 +52,7 @@ import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
 import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
 import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
+import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.MetadataDef;
 import org.apache.calcite.rel.metadata.MetadataHandler;
@@ -71,6 +73,7 @@ import org.apache.calcite.tools.Frameworks;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
 
+import com.google.common.base.Function;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -99,6 +102,7 @@ import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * Unit test for {@link DefaultRelMetadataProvider}. See
@@ -860,38 +864,88 @@ public class RelMetadataTest extends SqlToRelTestBase {
     assertUniqueConsistent(rel);
   }
 
+  @Test public void testBrokenCustomProvider() {
+    final List<String> buf = Lists.newArrayList();
+    ColTypeImpl.THREAD_LIST.set(buf);
+
+    final String sql = "select deptno, count(*) from emp where deptno > 10 "
+        + "group by deptno having count(*) = 0";
+    final RelRoot root = tester
+        .withClusterFactory(
+            new Function<RelOptCluster, RelOptCluster>() {
+              public RelOptCluster apply(RelOptCluster cluster) {
+                cluster.setMetadataProvider(
+                    ChainedRelMetadataProvider.of(
+                        ImmutableList.of(BrokenColTypeImpl.SOURCE,
+                            cluster.getMetadataProvider())));
+                return cluster;
+              }
+            })
+        .convertSqlToRel(sql);
+
+    final RelNode rel = root.rel;
+    assertThat(rel, instanceOf(LogicalFilter.class));
+    final MyRelMetadataQuery mq = new MyRelMetadataQuery();
+
+    try {
+      assertThat(colType(mq, rel, 0), equalTo("DEPTNO-rel"));
+      fail("expected error");
+    } catch (IllegalArgumentException e) {
+      final String value = "No handler for method [public abstract java.lang.String "
+          + "org.apache.calcite.test.RelMetadataTest$ColType.getColType(int)] "
+          + "applied to argument of type [interface org.apache.calcite.rel.RelNode]; "
+          + "we recommend you create a catch-all (RelNode) handler";
+      assertThat(e.getMessage(), is(value));
+    }
+  }
+
+  public String colType(RelMetadataQuery mq, RelNode rel, int column) {
+    if (mq instanceof MyRelMetadataQuery) {
+      return ((MyRelMetadataQuery) mq).colType(rel, column);
+    } else {
+      return rel.metadata(ColType.class, mq).getColType(column);
+    }
+  }
+
   @Test public void testCustomProvider() {
     final List<String> buf = Lists.newArrayList();
     ColTypeImpl.THREAD_LIST.set(buf);
 
-    RelNode rel =
-        convertSql("select deptno, count(*) from emp where deptno > 10 "
-            + "group by deptno having count(*) = 0");
-    rel.getCluster().setMetadataProvider(
-        ChainedRelMetadataProvider.of(
-            ImmutableList.of(
-                ColTypeImpl.SOURCE, rel.getCluster().getMetadataProvider())));
+    final String sql = "select deptno, count(*) from emp where deptno > 10 "
+        + "group by deptno having count(*) = 0";
+    final RelRoot root = tester
+        .withClusterFactory(
+            new Function<RelOptCluster, RelOptCluster>() {
+              public RelOptCluster apply(RelOptCluster cluster) {
+                // Create a custom provider that includes ColType.
+                // Include the same provider twice just to be devious.
+                final ImmutableList<RelMetadataProvider> list =
+                    ImmutableList.of(ColTypeImpl.SOURCE, ColTypeImpl.SOURCE,
+                        cluster.getMetadataProvider());
+                cluster.setMetadataProvider(
+                    ChainedRelMetadataProvider.of(list));
+                return cluster;
+              }
+            })
+        .convertSqlToRel(sql);
+    final RelNode rel = root.rel;
 
     // Top node is a filter. Its metadata uses getColType(RelNode, int).
     assertThat(rel, instanceOf(LogicalFilter.class));
     final RelMetadataQuery mq = RelMetadataQuery.instance();
-    assertThat(rel.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-rel"));
-    assertThat(rel.metadata(ColType.class, mq).getColType(1),
-        equalTo("EXPR$1-rel"));
+    assertThat(colType(mq, rel, 0), equalTo("DEPTNO-rel"));
+    assertThat(colType(mq, rel, 1), equalTo("EXPR$1-rel"));
 
     // Next node is an aggregate. Its metadata uses
     // getColType(LogicalAggregate, int).
     final RelNode input = rel.getInput(0);
     assertThat(input, instanceOf(LogicalAggregate.class));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
 
     // There is no caching. Another request causes another call to the provider.
     assertThat(buf.toString(), equalTo("[DEPTNO-rel, EXPR$1-rel, DEPTNO-agg]"));
     assertThat(buf.size(), equalTo(3));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(4));
 
     // Now add a cache. Only the first request for each piece of metadata
@@ -900,31 +954,24 @@ public class RelMetadataTest extends SqlToRelTestBase {
     rel.getCluster().setMetadataProvider(
         new CachingRelMetadataProvider(
             rel.getCluster().getMetadataProvider(), planner));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(5));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(5));
-    assertThat(input.metadata(ColType.class, mq).getColType(1),
-        equalTo("EXPR$1-agg"));
+    assertThat(colType(mq, input, 1), equalTo("EXPR$1-agg"));
     assertThat(buf.size(), equalTo(6));
-    assertThat(input.metadata(ColType.class, mq).getColType(1),
-        equalTo("EXPR$1-agg"));
+    assertThat(colType(mq, input, 1), equalTo("EXPR$1-agg"));
     assertThat(buf.size(), equalTo(6));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(6));
 
     // With a different timestamp, a metadata item is re-computed on first call.
     long timestamp = planner.getRelMetadataTimestamp(rel);
     assertThat(timestamp, equalTo(0L));
     ((MockRelOptPlanner) planner).setRelMetadataTimestamp(timestamp + 1);
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(7));
-    assertThat(input.metadata(ColType.class, mq).getColType(0),
-        equalTo("DEPTNO-agg"));
+    assertThat(colType(mq, input, 0), equalTo("DEPTNO-agg"));
     assertThat(buf.size(), equalTo(7));
   }
 
@@ -1383,28 +1430,27 @@ public class RelMetadataTest extends SqlToRelTestBase {
 
   /** Custom metadata interface. */
   public interface ColType extends Metadata {
+    Method METHOD = Types.lookupMethod(ColType.class, "getColType", int.class);
+
+    MetadataDef<ColType> DEF =
+        MetadataDef.of(ColType.class, ColType.Handler.class, METHOD);
+
     String getColType(int column);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<ColType> {
+      String getColType(RelNode r, RelMetadataQuery mq, int column);
+    }
   }
 
   /** A provider for {@link org.apache.calcite.test.RelMetadataTest.ColType} via
    * reflection. */
-  public static class ColTypeImpl implements MetadataHandler {
+  public abstract static class PartialColTypeImpl
+      implements MetadataHandler<ColType> {
     static final ThreadLocal<List<String>> THREAD_LIST = new ThreadLocal<>();
-    static final Method METHOD;
-    static {
-      try {
-        METHOD = ColType.class.getMethod("getColType", int.class);
-      } catch (NoSuchMethodException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    public static final RelMetadataProvider SOURCE =
-        ReflectiveRelMetadataProvider.reflectiveSource(
-            METHOD, new ColTypeImpl());
 
-    public MetadataDef getDef() {
-      throw new UnsupportedOperationException();
+    public MetadataDef<ColType> getDef() {
+      return ColType.DEF;
     }
 
     /** Implementation of {@link ColType#getColType(int)} for
@@ -1417,6 +1463,13 @@ public class RelMetadataTest extends SqlToRelTestBase {
       THREAD_LIST.get().add(name);
       return name;
     }
+  }
+
+  /** A provider for {@link org.apache.calcite.test.RelMetadataTest.ColType} via
+   * reflection. */
+  public static class ColTypeImpl extends PartialColTypeImpl {
+    public static final RelMetadataProvider SOURCE =
+        ReflectiveRelMetadataProvider.reflectiveSource(ColType.METHOD, new ColTypeImpl());
 
     /** Implementation of {@link ColType#getColType(int)} for
      * {@link RelNode}, called via reflection. */
@@ -1428,6 +1481,35 @@ public class RelMetadataTest extends SqlToRelTestBase {
       return name;
     }
   }
+
+  /** Implementation of {@link ColType} that has no fall-back for {@link RelNode}. */
+  public static class BrokenColTypeImpl extends PartialColTypeImpl {
+    public static final RelMetadataProvider SOURCE =
+        ReflectiveRelMetadataProvider.reflectiveSource(ColType.METHOD,
+            new BrokenColTypeImpl());
+  }
+
+  /** Extension to {@link RelMetadataQuery} to support {@link ColType}.
+   *
+   * <p>Illustrates how you would package up a user-defined metadata type. */
+  private static class MyRelMetadataQuery extends RelMetadataQuery {
+    private ColType.Handler colTypeHandler;
+
+    public MyRelMetadataQuery() {
+      super(THREAD_PROVIDERS.get(), EMPTY);
+      colTypeHandler = initialHandler(ColType.Handler.class);
+    }
+
+    public String colType(RelNode rel, int column) {
+      for (;;) {
+        try {
+          return colTypeHandler.getColType(rel, this, column);
+        } catch (JaninoRelMetadataProvider.NoHandler e) {
+          colTypeHandler = revise(e.relClass, ColType.DEF);
+        }
+      }
+    }
+  }
 }
 
 // End RelMetadataTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/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 7bd85cb..9a2c3db 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -2036,7 +2036,7 @@ public class RelOptRulesTest extends RelOptTestBase {
    * Wrong collation trait in SortJoinTransposeRule for right joins</a>. */
   @Test public void testSortJoinTranspose4() {
     // Create a customized test with RelCollation trait in the test cluster.
-    Tester tester = new TesterImpl(getDiffRepos(), true, true, false, null) {
+    Tester tester = new TesterImpl(getDiffRepos(), true, true, false, null, null) {
       @Override public RelOptPlanner createPlanner() {
         return new MockRelOptPlanner() {
           @Override public List<RelTraitDef> getRelTraitDefs() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/94f8837c/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
index 7e818af..3d7f609 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
@@ -92,7 +92,7 @@ public abstract class SqlToRelTestBase {
   }
 
   protected Tester createTester() {
-    return new TesterImpl(getDiffRepos(), false, false, true, null);
+    return new TesterImpl(getDiffRepos(), false, false, true, null, null);
   }
 
   /**
@@ -221,6 +221,8 @@ public abstract class SqlToRelTestBase {
 
     /** Returns a tester that optionally trims unused fields. */
     Tester withTrim(boolean enable);
+
+    Tester withClusterFactory(Function<RelOptCluster, RelOptCluster> function);
   }
 
   //~ Inner Classes ----------------------------------------------------------
@@ -463,6 +465,7 @@ public abstract class SqlToRelTestBase {
     private final boolean enableExpand;
     private final Function<RelDataTypeFactory, Prepare.CatalogReader>
     catalogReaderFactory;
+    private final Function<RelOptCluster, RelOptCluster> clusterFactory;
     private RelDataTypeFactory typeFactory;
 
     /**
@@ -473,16 +476,19 @@ public abstract class SqlToRelTestBase {
      * @param enableTrim Whether to trim unused fields
      * @param enableExpand Whether to expand sub-queries
      * @param catalogReaderFactory Function to create catalog reader, or null
+     * @param clusterFactory Called after a cluster has been created
      */
     protected TesterImpl(DiffRepository diffRepos, boolean enableDecorrelate,
         boolean enableTrim, boolean enableExpand,
         Function<RelDataTypeFactory, Prepare.CatalogReader>
-            catalogReaderFactory) {
+            catalogReaderFactory,
+        Function<RelOptCluster, RelOptCluster> clusterFactory) {
       this.diffRepos = diffRepos;
       this.enableDecorrelate = enableDecorrelate;
       this.enableTrim = enableTrim;
       this.enableExpand = enableExpand;
       this.catalogReaderFactory = catalogReaderFactory;
+      this.clusterFactory = clusterFactory;
     }
 
     public RelRoot convertSqlToRel(String sql) {
@@ -528,8 +534,11 @@ public abstract class SqlToRelTestBase {
         final Prepare.CatalogReader catalogReader,
         final RelDataTypeFactory typeFactory) {
       final RexBuilder rexBuilder = new RexBuilder(typeFactory);
-      final RelOptCluster cluster =
+      RelOptCluster cluster =
           RelOptCluster.create(getPlanner(), rexBuilder);
+      if (clusterFactory != null) {
+        cluster = clusterFactory.apply(cluster);
+      }
       return new SqlToRelConverter(null, validator, catalogReader, cluster,
           StandardConvertletTable.INSTANCE);
     }
@@ -659,27 +668,33 @@ public abstract class SqlToRelTestBase {
       return this.enableDecorrelate == enable
           ? this
           : new TesterImpl(diffRepos, enable, enableTrim, enableExpand,
-              catalogReaderFactory);
+              catalogReaderFactory, clusterFactory);
     }
 
     public Tester withTrim(boolean enable) {
       return this.enableTrim == enable
           ? this
           : new TesterImpl(diffRepos, enableDecorrelate, enable, enableExpand,
-              catalogReaderFactory);
+              catalogReaderFactory, clusterFactory);
     }
 
     public Tester withExpand(boolean expand) {
       return this.enableExpand == expand
           ? this
           : new TesterImpl(diffRepos, enableDecorrelate, enableTrim, expand,
-              catalogReaderFactory);
+              catalogReaderFactory, clusterFactory);
     }
 
     public Tester withCatalogReaderFactory(
         Function<RelDataTypeFactory, Prepare.CatalogReader> factory) {
       return new TesterImpl(diffRepos, enableDecorrelate, false, enableExpand,
-          factory);
+          factory, clusterFactory);
+    }
+
+    public Tester withClusterFactory(
+        Function<RelOptCluster, RelOptCluster> clusterFactory) {
+      return new TesterImpl(diffRepos, enableDecorrelate, false, enableExpand,
+          catalogReaderFactory, clusterFactory);
     }
   }
 


[5/8] calcite git commit: [CALCITE-1153] Invalid cast created during SQL Join in Oracle (Chris Atkinson)

Posted by jh...@apache.org.
[CALCITE-1153] Invalid cast created during SQL Join in Oracle (Chris Atkinson)

Close apache/calcite#211


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

Branch: refs/heads/master
Commit: 9dd4993ad2ae41e0309eb2631af1a91e64f693e7
Parents: caa1cd3
Author: Chris Atkinson <ca...@gmail.com>
Authored: Mon Mar 14 10:47:10 2016 +0000
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:00 2016 -0700

----------------------------------------------------------------------
 core/src/main/java/org/apache/calcite/sql/SqlDialect.java | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/9dd4993a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index 88c6d63..2e38d42 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -465,6 +465,7 @@ public class SqlDialect {
     case HSQLDB:
     case PHOENIX:
     case POSTGRESQL:
+    case ORACLE:
       return false;
     default:
       return true;


[4/8] calcite git commit: Add test case for [CALCITE-1153]

Posted by jh...@apache.org.
Add test case for [CALCITE-1153]


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

Branch: refs/heads/master
Commit: 22aa8a8072bd98d4d81d17aad7db5a972e03478e
Parents: 9dd4993
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Mar 14 14:14:56 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:00 2016 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/test/JdbcTest.java  | 24 ++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/22aa8a80/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 b57feee..cfebfb2 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -4387,16 +4387,36 @@ public class JdbcTest {
     CalciteAssert.model(FOODMART_MODEL)
         .query("select count(*) as c from \"customer\" "
             + "where \"lname\" = 'this string is longer than 30 characters'")
-        .enable(CalciteAssert.DB != CalciteAssert.DatabaseInstance.ORACLE)
         .returns("C=0\n");
 
     CalciteAssert.model(FOODMART_MODEL)
         .query("select count(*) as c from \"customer\" "
             + "where cast(\"customer_id\" as char(20)) = 'this string is longer than 30 characters'")
-        .enable(CalciteAssert.DB != CalciteAssert.DatabaseInstance.ORACLE)
         .returns("C=0\n");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1153">[CALCITE-1153]
+   * Invalid CAST when push JOIN down to Oracle</a>. */
+  @Test public void testJoinMismatchedVarchar() {
+    final String sql = "select count(*) as c\n"
+        + "from \"customer\" as c\n"
+        + "join \"product\" as p on c.\"lname\" = p.\"brand_name\"";
+    CalciteAssert.model(FOODMART_MODEL)
+        .query(sql)
+        .returns("C=607\n");
+  }
+
+  @Test public void testIntersectMismatchedVarchar() {
+    final String sql = "select count(*) as c from (\n"
+        + "  select \"lname\" from \"customer\" as c\n"
+        + "  intersect\n"
+        + "  select \"brand_name\" from \"product\" as p)";
+    CalciteAssert.model(FOODMART_MODEL)
+        .query(sql)
+        .returns("C=12\n");
+  }
+
   /** Tests the NOT IN operator. Problems arose in code-generation because
    * the column allows nulls. */
   @Test public void testNotIn() {


[8/8] calcite git commit: Add test case for [DRILL-4407] Group by subquery causes Java NPE

Posted by jh...@apache.org.
Add test case for [DRILL-4407] Group by subquery causes Java NPE


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

Branch: refs/heads/master
Commit: 82470e32de3d42125ab89ce9c5cbad97f5833df1
Parents: 409fd3a
Author: Julian Hyde <jh...@apache.org>
Authored: Wed Feb 17 13:33:09 2016 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:01 2016 -0700

----------------------------------------------------------------------
 core/src/test/resources/sql/subquery.iq | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/82470e32/core/src/test/resources/sql/subquery.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/subquery.iq b/core/src/test/resources/sql/subquery.iq
index 8f0eb70..d4f3a10 100644
--- a/core/src/test/resources/sql/subquery.iq
+++ b/core/src/test/resources/sql/subquery.iq
@@ -274,6 +274,17 @@ GROUP BY emp.deptno;
 
 !ok
 
+# [DRILL-4407] Group by subquery causes Java NPE
+select count(*) as c
+from "scott".emp
+group by (select deptno from "scott".emp where empno = 10);
+ C
+----
+ 14
+(1 row)
+
+!ok
+
 !if (fixed.calcite1045) {
 # Correlated IN sub-query in WHERE clause of JOIN
 select empno from "scott".emp as e


[2/8] calcite git commit: [CALCITE-1146] Wrong path in CSV example model (wanglan)

Posted by jh...@apache.org.
[CALCITE-1146] Wrong path in CSV example model (wanglan)

Close apache/calcite#208


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

Branch: refs/heads/master
Commit: caa1cd326f545ee120aa34ae3abb480a30f2a3b4
Parents: 5e3db15
Author: wanglan <la...@huawei.com>
Authored: Thu Mar 10 14:38:07 2016 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:00 2016 -0700

----------------------------------------------------------------------
 example/csv/src/test/resources/model-with-view.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/caa1cd32/example/csv/src/test/resources/model-with-view.json
----------------------------------------------------------------------
diff --git a/example/csv/src/test/resources/model-with-view.json b/example/csv/src/test/resources/model-with-view.json
index 11d8058..2c190e9 100644
--- a/example/csv/src/test/resources/model-with-view.json
+++ b/example/csv/src/test/resources/model-with-view.json
@@ -25,7 +25,7 @@
       type: 'custom',
       factory: 'org.apache.calcite.adapter.csv.CsvSchemaFactory',
       operand: {
-        directory: 'target/test-classes/sales'
+        directory: 'sales'
       },
       tables: [
         {


[7/8] calcite git commit: [CALCITE-1116] Extend simplify for reducing expressions

Posted by jh...@apache.org.
[CALCITE-1116] Extend simplify for reducing expressions

Close apache/calcite#202


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

Branch: refs/heads/master
Commit: 409fd3ab7c4e31ddfd4a30d35f29d7a7c92bda4d
Parents: 94f8837
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Tue Mar 1 13:28:19 2016 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:01 2016 -0700

----------------------------------------------------------------------
 .../rel/rules/ReduceExpressionsRule.java        |  38 ++-
 .../java/org/apache/calcite/rex/RexUtil.java    | 249 +++++++++++++++++--
 .../java/org/apache/calcite/sql/SqlKind.java    |  21 ++
 .../org/apache/calcite/tools/RelBuilder.java    |   2 +-
 .../apache/calcite/test/RelOptRulesTest.java    |  50 ++++
 .../org/apache/calcite/test/RexProgramTest.java |  62 +++++
 .../org/apache/calcite/test/RelOptRulesTest.xml |  97 +++++++-
 7 files changed, 500 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index 6c0238a..44870af 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -145,7 +145,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
       final RelMetadataQuery mq = RelMetadataQuery.instance();
       final RelOptPredicateList predicates =
           mq.getPulledUpPredicates(filter.getInput());
-      if (reduceExpressions(filter, expList, predicates)) {
+      if (reduceExpressions(filter, expList, predicates, true)) {
         assert expList.size() == 1;
         newConditionExp = expList.get(0);
         reduced = true;
@@ -435,6 +435,42 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
    */
   protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
       RelOptPredicateList predicates) {
+    return reduceExpressions(rel, expList, predicates, false);
+  }
+
+  /**
+   * Reduces a list of expressions.
+   *
+   * @param rel     Relational expression
+   * @param expList List of expressions, modified in place
+   * @param predicates Constraints known to hold on input expressions
+   * @param unknownAsFalse Whether UNKNOWN will be treated as FALSE
+   *
+   * @return whether reduction found something to change, and succeeded
+   */
+  protected static boolean reduceExpressions(RelNode rel, List<RexNode> expList,
+      RelOptPredicateList predicates, boolean unknownAsFalse) {
+    RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+
+    boolean reduced = reduceExpressionsInternal(rel, expList, predicates);
+
+    // Simplify preds in place
+    boolean simplified = false;
+    for (int i = 0; i < expList.size(); i++) {
+      RexNode newExp = RexUtil.simplify(rexBuilder, expList.get(i),
+          unknownAsFalse);
+      if (!newExp.toString().equals(expList.get(i).toString())) {
+        expList.remove(i);
+        expList.add(i, newExp);
+        simplified = true;
+      }
+    }
+
+    return reduced || simplified;
+  }
+
+  protected static boolean reduceExpressionsInternal(RelNode rel, List<RexNode> expList,
+      RelOptPredicateList predicates) {
     RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
 
     // Replace predicates on CASE to CASE on predicates.

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 12ca400..ee46051 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -52,8 +52,11 @@ import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -306,6 +309,31 @@ public class RexUtil {
     return node.accept(ConstantFinder.INSTANCE);
   }
 
+  /**
+   * Returns whether a given expression is deterministic.
+   *
+   * @param e Expression
+   * @return true if tree result is deterministic, false otherwise
+   */
+  public static boolean isDeterministic(RexNode e) {
+    try {
+      RexVisitor<Void> visitor =
+          new RexVisitorImpl<Void>(true) {
+            @Override public Void visitCall(RexCall call) {
+              if (!call.getOperator().isDeterministic()) {
+                throw Util.FoundOne.NULL;
+              }
+              return null;
+            }
+          };
+      e.accept(visitor);
+      return true;
+    } catch (Util.FoundOne ex) {
+      Util.swallow(ex, null);
+      return false;
+    }
+  }
+
    /**
    * Returns whether a given node contains a RexCall with a specified operator
    *
@@ -1283,15 +1311,20 @@ public class RexUtil {
    * </ul>
    */
   public static RexNode simplify(RexBuilder rexBuilder, RexNode e) {
+    return simplify(rexBuilder, e, false);
+  }
+
+  public static RexNode simplify(RexBuilder rexBuilder, RexNode e,
+      boolean unknownAsFalse) {
     switch (e.getKind()) {
     case AND:
-      return simplifyAnd(rexBuilder, (RexCall) e);
+      return simplifyAnd(rexBuilder, (RexCall) e, unknownAsFalse);
     case OR:
-      return simplifyOr(rexBuilder, (RexCall) e);
+      return simplifyOr(rexBuilder, (RexCall) e, unknownAsFalse);
     case NOT:
-      return simplifyNot(rexBuilder, (RexCall) e);
+      return simplifyNot(rexBuilder, (RexCall) e, unknownAsFalse);
     case CASE:
-      return simplifyCase(rexBuilder, (RexCall) e);
+      return simplifyCase(rexBuilder, (RexCall) e, unknownAsFalse);
     }
     switch (e.getKind()) {
     case IS_NULL:
@@ -1301,7 +1334,7 @@ public class RexUtil {
     case IS_FALSE:
     case IS_NOT_FALSE:
       assert e instanceof RexCall;
-      return simplifyIs(rexBuilder, (RexCall) e);
+      return simplifyIs(rexBuilder, (RexCall) e, unknownAsFalse);
     default:
       return e;
     }
@@ -1312,26 +1345,37 @@ public class RexUtil {
    */
   public static RexNode simplifyAnds(RexBuilder rexBuilder,
       Iterable<? extends RexNode> nodes) {
+    return simplifyAnds(rexBuilder, nodes, false);
+  }
+
+  public static RexNode simplifyAnds(RexBuilder rexBuilder,
+      Iterable<? extends RexNode> nodes, boolean unknownAsFalse) {
     final List<RexNode> terms = new ArrayList<>();
     final List<RexNode> notTerms = new ArrayList<>();
     for (RexNode e : nodes) {
       RelOptUtil.decomposeConjunction(e, terms, notTerms);
     }
+    if (unknownAsFalse) {
+      return simplifyAnd2ForFilter(rexBuilder, terms, notTerms);
+    }
     return simplifyAnd2(rexBuilder, terms, notTerms);
   }
 
-  private static RexNode simplifyNot(RexBuilder rexBuilder, RexCall call) {
+  private static RexNode simplifyNot(RexBuilder rexBuilder, RexCall call,
+      boolean unknownAsFalse) {
     final RexNode a = call.getOperands().get(0);
     switch (a.getKind()) {
     case NOT:
       // NOT NOT x ==> x
-      return simplify(rexBuilder, ((RexCall) a).getOperands().get(0));
+      return simplify(rexBuilder, ((RexCall) a).getOperands().get(0),
+          unknownAsFalse);
     }
     final SqlKind negateKind = a.getKind().negate();
     if (a.getKind() != negateKind) {
       return simplify(rexBuilder,
           rexBuilder.makeCall(op(negateKind),
-              ImmutableList.of(((RexCall) a).getOperands().get(0))));
+              ImmutableList.of(((RexCall) a).getOperands().get(0))),
+          unknownAsFalse);
     }
     return call;
   }
@@ -1347,7 +1391,8 @@ public class RexUtil {
     }
   }
 
-  private static RexNode simplifyIs(RexBuilder rexBuilder, RexCall call) {
+  private static RexNode simplifyIs(RexBuilder rexBuilder, RexCall call,
+      boolean unknownAsFalse) {
     final SqlKind kind = call.getKind();
     final RexNode a = call.getOperands().get(0);
     if (!a.getType().isNullable()) {
@@ -1361,13 +1406,13 @@ public class RexUtil {
       case IS_NOT_FALSE:
         // x IS TRUE ==> x (if x is not nullable)
         // x IS NOT FALSE ==> x (if x is not nullable)
-        return simplify(rexBuilder, a);
+        return simplify(rexBuilder, a, unknownAsFalse);
       case IS_FALSE:
       case IS_NOT_TRUE:
         // x IS NOT TRUE ==> NOT x (if x is not nullable)
         // x IS FALSE ==> NOT x (if x is not nullable)
         return simplify(rexBuilder,
-            rexBuilder.makeCall(SqlStdOperatorTable.NOT, a));
+            rexBuilder.makeCall(SqlStdOperatorTable.NOT, a), unknownAsFalse);
       }
     }
     switch (a.getKind()) {
@@ -1380,9 +1425,9 @@ public class RexUtil {
       // because of null values.
       return simplify(rexBuilder,
           rexBuilder.makeCall(op(kind.negate()),
-              ((RexCall) a).getOperands().get(0)));
+              ((RexCall) a).getOperands().get(0)), unknownAsFalse);
     }
-    RexNode a2 = simplify(rexBuilder, a);
+    RexNode a2 = simplify(rexBuilder, a, unknownAsFalse);
     if (a != a2) {
       return rexBuilder.makeCall(op(kind), ImmutableList.of(a2));
     }
@@ -1405,12 +1450,25 @@ public class RexUtil {
       return SqlStdOperatorTable.IS_NOT_TRUE;
     case IS_NOT_NULL:
       return SqlStdOperatorTable.IS_NOT_NULL;
+    case EQUALS:
+      return SqlStdOperatorTable.EQUALS;
+    case NOT_EQUALS:
+      return SqlStdOperatorTable.NOT_EQUALS;
+    case LESS_THAN:
+      return SqlStdOperatorTable.LESS_THAN;
+    case GREATER_THAN:
+      return SqlStdOperatorTable.GREATER_THAN;
+    case LESS_THAN_OR_EQUAL:
+      return SqlStdOperatorTable.LESS_THAN_OR_EQUAL;
+    case GREATER_THAN_OR_EQUAL:
+      return SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
     default:
       throw new AssertionError(kind);
     }
   }
 
-  private static RexNode simplifyCase(RexBuilder rexBuilder, RexCall call) {
+  private static RexNode simplifyCase(RexBuilder rexBuilder, RexCall call,
+      boolean unknownAsFalse) {
     final List<RexNode> operands = call.getOperands();
     final List<RexNode> newOperands = new ArrayList<>();
     for (int i = 0; i < operands.size(); i++) {
@@ -1432,6 +1490,9 @@ public class RexUtil {
     assert newOperands.size() % 2 == 1;
     switch (newOperands.size()) {
     case 1:
+      if (!call.getType().equals(newOperands.get(0).getType())) {
+        return rexBuilder.makeCast(call.getType(), newOperands.get(0));
+      }
       return newOperands.get(0);
     }
   trueFalse:
@@ -1483,10 +1544,14 @@ public class RexUtil {
     return builder.build();
   }
 
-  public static RexNode simplifyAnd(RexBuilder rexBuilder, RexCall e) {
+  public static RexNode simplifyAnd(RexBuilder rexBuilder, RexCall e,
+      boolean unknownAsFalse) {
     final List<RexNode> terms = new ArrayList<>();
     final List<RexNode> notTerms = new ArrayList<>();
     RelOptUtil.decomposeConjunction(e, terms, notTerms);
+    if (unknownAsFalse) {
+      return simplifyAnd2ForFilter(rexBuilder, terms, notTerms);
+    }
     return simplifyAnd2(rexBuilder, terms, notTerms);
   }
 
@@ -1524,8 +1589,160 @@ public class RexUtil {
     return composeConjunction(rexBuilder, terms, false);
   }
 
+  /** As {@link #simplifyAnd2(RexBuilder, List, List)} but we assume that if the expression returns
+   * UNKNOWN it will be interpreted as FALSE. */
+  public static RexNode simplifyAnd2ForFilter(RexBuilder rexBuilder,
+      List<RexNode> terms, List<RexNode> notTerms) {
+    if (terms.contains(rexBuilder.makeLiteral(false))) {
+      return rexBuilder.makeLiteral(false);
+    }
+    if (terms.isEmpty() && notTerms.isEmpty()) {
+      return rexBuilder.makeLiteral(true);
+    }
+    if (terms.size() == 1 && notTerms.isEmpty()) {
+      // Make sure "x OR y OR x" (a single-term conjunction) gets simplified.
+      return simplify(rexBuilder, terms.get(0), true);
+    }
+    // Try to simplify the expression
+    final Set<String> negatedTerms = new HashSet<>();
+    final Set<String> nullOperands = new HashSet<>();
+    final Set<RexNode> notNullOperands = new LinkedHashSet<>();
+    final Set<String> comparedOperands = new HashSet<>();
+    for (int i = 0; i < terms.size(); i++) {
+      final RexNode term = terms.get(i);
+      if (!isDeterministic(term)) {
+        continue;
+      }
+      switch (term.getKind()) {
+      case EQUALS:
+      case NOT_EQUALS:
+      case LESS_THAN:
+      case GREATER_THAN:
+      case LESS_THAN_OR_EQUAL:
+      case GREATER_THAN_OR_EQUAL:
+        RexCall call = (RexCall) term;
+        RexNode left = call.getOperands().get(0);
+        comparedOperands.add(left.toString());
+        // if it is a cast, we include the inner reference
+        if (left.getKind() == SqlKind.CAST) {
+          RexCall leftCast = (RexCall) left;
+          comparedOperands.add(leftCast.getOperands().get(0).toString());
+        }
+        RexNode right = call.getOperands().get(1);
+        comparedOperands.add(right.toString());
+        // if it is a cast, we include the inner reference
+        if (right.getKind() == SqlKind.CAST) {
+          RexCall rightCast = (RexCall) right;
+          comparedOperands.add(rightCast.getOperands().get(0).toString());
+        }
+        // Assume the expression a > 5 is part of a Filter condition.
+        // Then we can derive the negated term: a <= 5.
+        // But as the comparison is string based and thus operands order dependent,
+        // we should also add the inverted negated term: 5 >= a.
+        // Observe that for creating the inverted term we invert the list of operands.
+        RexNode negatedTerm = negate(rexBuilder, call);
+        if (negatedTerm != null) {
+          negatedTerms.add(negatedTerm.toString());
+          RexNode invertNegatedTerm = invert(rexBuilder, (RexCall) negatedTerm);
+          if (invertNegatedTerm != null) {
+            negatedTerms.add(invertNegatedTerm.toString());
+          }
+        }
+        break;
+      case IN:
+        comparedOperands.add(((RexCall) term).operands.get(0).toString());
+        break;
+      case BETWEEN:
+        comparedOperands.add(((RexCall) term).operands.get(1).toString());
+        break;
+      case IS_NOT_NULL:
+        notNullOperands.add(((RexCall) term).getOperands().get(0));
+        terms.remove(i);
+        --i;
+        break;
+      case IS_NULL:
+        nullOperands.add(((RexCall) term).getOperands().get(0).toString());
+      }
+    }
+    // If one column should be null and is in a comparison predicate,
+    // it is not satisfiable.
+    // Example. IS NULL(x) AND x < 5  - not satisfiable
+    if (!Collections.disjoint(nullOperands, comparedOperands)) {
+      return rexBuilder.makeLiteral(false);
+    }
+    // Remove not necessary IS NOT NULL expressions.
+    //
+    // Example. IS NOT NULL(x) AND x < 5  : x < 5
+    for (RexNode operand : notNullOperands) {
+      if (!comparedOperands.contains(operand.toString())) {
+        terms.add(
+            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operand));
+      }
+    }
+    // If one of the not-disjunctions is a disjunction that is wholly
+    // contained in the disjunctions list, the expression is not
+    // satisfiable.
+    //
+    // Example #1. x AND y AND z AND NOT (x AND y)  - not satisfiable
+    // Example #2. x AND y AND NOT (x AND y)        - not satisfiable
+    // Example #3. x AND y AND NOT (x AND y AND z)  - may be satisfiable
+    final Set<String> termsSet = Sets.newHashSet(RexUtil.strings(terms));
+    for (RexNode notDisjunction : notTerms) {
+      if (!isDeterministic(notDisjunction)) {
+        continue;
+      }
+      final List<String> terms2Set = RexUtil.strings(
+              RelOptUtil.conjunctions(notDisjunction));
+      if (termsSet.containsAll(terms2Set)) {
+        return rexBuilder.makeLiteral(false);
+      }
+    }
+    // Add the NOT disjunctions back in.
+    for (RexNode notDisjunction : notTerms) {
+      terms.add(
+          simplify(rexBuilder,
+              rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction), true));
+    }
+    // The negated terms: only deterministic expressions
+    for (String negatedTerm : negatedTerms) {
+      if (termsSet.contains(negatedTerm)) {
+        return rexBuilder.makeLiteral(false);
+      }
+    }
+    return composeConjunction(rexBuilder, terms, false);
+  }
+
+  public static RexNode negate(RexBuilder rexBuilder, RexCall call) {
+    switch (call.getKind()) {
+    case EQUALS:
+    case NOT_EQUALS:
+    case LESS_THAN:
+    case GREATER_THAN:
+    case LESS_THAN_OR_EQUAL:
+    case GREATER_THAN_OR_EQUAL:
+      final SqlOperator op = op(call.getKind().negateNullSafe());
+      return rexBuilder.makeCall(op, call.getOperands());
+    }
+    return null;
+  }
+
+  public static RexNode invert(RexBuilder rexBuilder, RexCall call) {
+    switch (call.getKind()) {
+    case EQUALS:
+    case NOT_EQUALS:
+    case LESS_THAN:
+    case GREATER_THAN:
+    case LESS_THAN_OR_EQUAL:
+    case GREATER_THAN_OR_EQUAL:
+      final SqlOperator op = op(call.getKind().reverse());
+      return rexBuilder.makeCall(op, Lists.reverse(call.getOperands()));
+    }
+    return null;
+  }
+
   /** Simplifies OR(x, x) into x, and similar. */
-  public static RexNode simplifyOr(RexBuilder rexBuilder, RexCall call) {
+  public static RexNode simplifyOr(RexBuilder rexBuilder, RexCall call,
+      boolean unknownAsFalse) {
     assert call.getKind() == SqlKind.OR;
     final List<RexNode> terms = RelOptUtil.disjunctions(call);
     for (int i = 0; i < terms.size(); i++) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 07d2bca..bb84547 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -999,6 +999,27 @@ public enum SqlKind {
     }
   }
 
+  /** Returns the kind that you get if you negate this kind.
+   * To conform to null semantics, null value should not be compared. */
+  public SqlKind negateNullSafe() {
+    switch (this) {
+    case EQUALS:
+      return NOT_EQUALS;
+    case NOT_EQUALS:
+      return EQUALS;
+    case LESS_THAN:
+      return GREATER_THAN_OR_EQUAL;
+    case GREATER_THAN:
+      return LESS_THAN_OR_EQUAL;
+    case LESS_THAN_OR_EQUAL:
+      return GREATER_THAN;
+    case GREATER_THAN_OR_EQUAL:
+      return LESS_THAN;
+    default:
+      return this.negate();
+    }
+  }
+
   /**
    * Returns whether this {@code SqlKind} belongs to a given category.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/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 54398a6..414f1ca 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -697,7 +697,7 @@ public class RelBuilder {
    * and optimized in a similar way to the {@link #and} method.
    * If the result is TRUE no filter is created. */
   public RelBuilder filter(Iterable<? extends RexNode> predicates) {
-    final RexNode x = RexUtil.simplifyAnds(cluster.getRexBuilder(), predicates);
+    final RexNode x = RexUtil.simplifyAnds(cluster.getRexBuilder(), predicates, true);
     if (x.isAlwaysFalse()) {
       return empty();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/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 9a2c3db..f28f4e0 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -1227,6 +1227,56 @@ public class RelOptRulesTest extends RelOptTestBase {
             + " where a - b < 21");
   }
 
+  @Test public void testReduceCase() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.PROJECT_INSTANCE)
+        .build();
+
+    final String sql = "select\n"
+        + "  case when false then cast(2.1 as float)\n"
+        + "   else cast(1 as integer) end as newcol\n"
+        + "from emp";
+    checkPlanning(program, sql);
+  }
+
+  @Test public void testReduceConstantsIsNull() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .build();
+
+    checkPlanning(program,
+        "select empno from emp where empno=10 and empno is null");
+  }
+
+  @Test public void testReduceConstantsIsNotNull() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .build();
+
+    final String sql = "select empno from emp\n"
+        + "where empno=10 and empno is not null";
+    checkPlanning(program, sql);
+  }
+
+  @Test public void testReduceConstantsNegated() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .build();
+
+    final String sql = "select empno from emp\n"
+        + "where empno=10 and not(empno=10)";
+    checkPlanning(program, sql);
+  }
+
+  @Test public void testReduceConstantsNegatedInverted() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .build();
+
+    final String sql = "select empno from emp where empno>10 and empno<=10";
+    checkPlanning(program, sql);
+  }
+
   @Ignore // Calcite does not support INSERT yet
   @Test public void testReduceValuesNull() throws Exception {
     // The NULL literal presents pitfalls for value-reduction. Only

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 1efb8cc..ed56f90 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -94,6 +94,11 @@ public class RexProgramTest {
         equalTo(expected));
   }
 
+  private void checkSimplifyFilter(RexNode node, String expected) {
+    assertThat(RexUtil.simplify(rexBuilder, node, true).toString(),
+        equalTo(expected));
+  }
+
   /** Returns the number of nodes (including leaves) in a Rex tree. */
   private static int nodeCount(RexNode node) {
     int n = 1;
@@ -137,6 +142,22 @@ public class RexProgramTest {
     return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, n1, n2);
   }
 
+  private RexNode le(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, n1, n2);
+  }
+
+  private RexNode lt(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, n1, n2);
+  }
+
+  private RexNode ge(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, n1, n2);
+  }
+
+  private RexNode gt(RexNode n1, RexNode n2) {
+    return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN, n1, n2);
+  }
+
   /**
    * Tests construction of a RexProgram.
    */
@@ -759,6 +780,7 @@ public class RexProgramTest {
     final RexNode eRef = rexBuilder.makeFieldAccess(range, 4);
     final RexLiteral true_ = rexBuilder.makeLiteral(true);
     final RexLiteral false_ = rexBuilder.makeLiteral(false);
+    final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
 
     // and: remove duplicates
     checkSimplify(and(aRef, bRef, aRef), "AND(?0.a, ?0.b)");
@@ -830,6 +852,46 @@ public class RexProgramTest {
     // is not null, applied to not-null value
     checkSimplify(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, aRef),
         "true");
+
+    // condition, and the inverse - nothing to do due to null values
+    checkSimplify(and(le(aRef, literal1), gt(aRef, literal1)),
+        "AND(<=(?0.a, 1), >(?0.a, 1))");
+
+    checkSimplify(and(le(aRef, literal1), ge(aRef, literal1)),
+        "AND(<=(?0.a, 1), >=(?0.a, 1))");
+
+    checkSimplify(and(lt(aRef, literal1), eq(aRef, literal1), ge(aRef, literal1)),
+        "AND(<(?0.a, 1), =(?0.a, 1), >=(?0.a, 1))");
+  }
+
+  @Test public void testSimplifyFilter() {
+    final RelDataType booleanType =
+        typeFactory.createSqlType(SqlTypeName.BOOLEAN);
+    final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
+    final RelDataType rowType = typeFactory.builder()
+        .add("a", booleanType)
+        .add("b", booleanType)
+        .add("c", booleanType)
+        .add("d", booleanType)
+        .add("e", booleanType)
+        .add("f", booleanType)
+        .add("g", booleanType)
+        .add("h", intType)
+        .build();
+
+    final RexDynamicParam range = rexBuilder.makeDynamicParam(rowType, 0);
+    final RexNode aRef = rexBuilder.makeFieldAccess(range, 0);
+    final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
+
+    // condition, and the inverse
+    checkSimplifyFilter(and(le(aRef, literal1), gt(aRef, literal1)),
+        "false");
+
+    checkSimplifyFilter(and(le(aRef, literal1), ge(aRef, literal1)),
+        "AND(<=(?0.a, 1), >=(?0.a, 1))");
+
+    checkSimplifyFilter(and(lt(aRef, literal1), eq(aRef, literal1), ge(aRef, literal1)),
+        "false");
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/409fd3ab/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 c138c3f..1600c7e 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3606,6 +3606,26 @@ LogicalProject(DEPTNO=[$0], ENAME=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testReduceCase">
+        <Resource name="sql">
+            <![CDATA[select
+  case when false then cast(2.1 as float)
+   else cast(1 as integer) end as newcol
+from emp]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(NEWCOL=[CASE(false, 2.1, 1)])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(NEWCOL=[1E0])
+  LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testReduceConstantsRequiresExecutor">
         <Resource name="sql">
             <![CDATA[select * from (values (1,2)) where 1 + 2 > 3 + CAST(NULL AS INTEGER)]]>
@@ -3763,6 +3783,81 @@ LogicalProject(EMPNO=[$0], EXPR$1=[$2])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testReduceConstantsIsNotNull">
+        <Resource name="sql">
+            <![CDATA[select empno from emp
+where empno=10 and empno is not null]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalFilter(condition=[AND(=($0, 10), IS NOT NULL($0))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalFilter(condition=[=($0, 10)])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testReduceConstantsIsNull">
+        <Resource name="sql">
+            <![CDATA[select empno from emp where empno=10 and empno is null]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalFilter(condition=[AND(=($0, 10), IS NULL($0))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalValues(tuples=[[]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testReduceConstantsNegated">
+        <Resource name="sql">
+            <![CDATA[select empno from emp
+where empno=10 and not(empno=10)]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalFilter(condition=[AND(=($0, 10), NOT(=($0, 10)))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalValues(tuples=[[]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testReduceConstantsNegatedInverted">
+        <Resource name="sql">
+            <![CDATA[select empno from emp where empno>10 and empno<=10]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalFilter(condition=[AND(>($0, 10), <=($0, 10))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0])
+  LogicalValues(tuples=[[]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testSwapOuterJoin">
         <Resource name="sql">
             <![CDATA[select 1 from sales.dept d left outer join sales.emp e on d.deptno = e.deptno]]>
@@ -4732,7 +4827,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
         <Resource name="planAfter">
             <![CDATA[
 LogicalProject(EMPNO=[10], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-  LogicalFilter(condition=[AND(=($7, 7), =($7, 8), =($0, 10), IS NULL($3), =($0, 10))])
+  LogicalFilter(condition=[AND(=($7, 7), =($7, 8), =($0, 10), IS NULL($3))])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>


[3/8] calcite git commit: [CALCITE-1077] Release notes and announcement for Avatica 1.7.1

Posted by jh...@apache.org.
[CALCITE-1077] Release notes and announcement for Avatica 1.7.1


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

Branch: refs/heads/master
Commit: 5e3db15392cc928a0096cfc3d8fd11c0db1f2784
Parents: c2d84ab
Author: Josh Elser <el...@apache.org>
Authored: Mon Mar 14 11:58:27 2016 -0400
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Mar 17 12:32:00 2016 -0700

----------------------------------------------------------------------
 avatica/site/_docs/history.md                   | 25 ++++--
 avatica/site/_posts/2016-03-18-release-1.7.1.md | 87 ++++++++++++++++++++
 avatica/site/downloads/index.md                 | 12 ++-
 site/_config.yml                                |  2 +-
 4 files changed, 116 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/5e3db153/avatica/site/_docs/history.md
----------------------------------------------------------------------
diff --git a/avatica/site/_docs/history.md b/avatica/site/_docs/history.md
index 896e6b0..ae7bd64 100644
--- a/avatica/site/_docs/history.md
+++ b/avatica/site/_docs/history.md
@@ -28,8 +28,8 @@ For a full list of releases, see
 Downloads are available on the
 [downloads page]({{ site.baseurl }}/downloads/).
 
-## 1.7.0 / (Under Development)
-{: #v1-7-0}
+## <a href="https://github.com/apache/calcite/releases/tag/calcite-avatica-1.7.1">1.7.1</a> / 2016-03-18
+{: #v1-7-1}
 
 This is the first release of Avatica as an independent project. (It
 is still governed by Apache Calcite's PMC, and stored in the same git
@@ -41,7 +41,7 @@ with [SLF4J](http://slf4j.org/). SLF4J provides an API that Avatica can use
 independent of the logging implementation. This is more
 flexible for users: they can configure Avatica's logging within their
 own chosen logging framework. This work was done in
-[CALCITE-669](https://issues.apache.org/jira/browse/CALCITE-669).
+[[CALCITE-669](https://issues.apache.org/jira/browse/CALCITE-669)].
 
 If you have configured JUL in Calcite/Avatica previously, you'll
 notice some differences, because JUL's `FINE`, `FINER` and `FINEST`
@@ -60,6 +60,10 @@ other software versions as specified in `pom.xml`.
 
 Features and bug fixes
 
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1156">CALCITE-1156</a>]
+  Upgrade Jetty from 9.2.7.v20150116 to 9.2.15.v20160210
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1141">CALCITE-1141</a>]
+  Bump `version.minor` for Avatica
 * [<a href="https://issues.apache.org/jira/browse/CALCITE-1132">CALCITE-1132</a>]
   Update `artifactId`, `groupId` and `name` for Avatica
 * [<a href="https://issues.apache.org/jira/browse/CALCITE-1064">CALCITE-1064</a>]
@@ -100,6 +104,16 @@ Features and bug fixes
 
 Web site and documentation
 
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1142">CALCITE-1142</a>]
+  Create a `README` for Avatica
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1144">CALCITE-1144</a>]
+  Fix `LICENSE` for Avatica
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1143">CALCITE-1143</a>]
+  Remove unnecessary `NOTICE` for Avatica
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1139">CALCITE-1139</a>]
+  Update Calcite's `KEYS` and add a copy for Avatica
+* [<a href="https://issues.apache.org/jira/browse/CALCITE-1140">CALCITE-1140</a>]
+  Release notes and website updates for Avatica 1.7
 * Instructions for Avatica site
 * New logo and color scheme for Avatica site
 * [<a href="https://issues.apache.org/jira/browse/CALCITE-1079">CALCITE-1079</a>]
@@ -107,9 +121,10 @@ Web site and documentation
 
 ## Past releases
 
-Prior to release 1.7.0, Avatica was released as part of Calcite. Maven
+Prior to release 1.7.1, Avatica was released as part of Calcite. Maven
 modules had groupId 'org.apache.calcite' and module names
 'calcite-avatica', 'calcite-avatica-server' etc.
 
-Please refer to the [Calcite release page](https://calcite.apache.org/docs/history.html)
+Please refer to the
+[Calcite release page](https://calcite.apache.org/docs/history.html)
 for information about previous Avatica releases.

http://git-wip-us.apache.org/repos/asf/calcite/blob/5e3db153/avatica/site/_posts/2016-03-18-release-1.7.1.md
----------------------------------------------------------------------
diff --git a/avatica/site/_posts/2016-03-18-release-1.7.1.md b/avatica/site/_posts/2016-03-18-release-1.7.1.md
new file mode 100644
index 0000000..7ee678c
--- /dev/null
+++ b/avatica/site/_posts/2016-03-18-release-1.7.1.md
@@ -0,0 +1,87 @@
+---
+layout: news_item
+date: "2016-03-18 12:00:00 +0000"
+author: elserj
+version: 1.7.1
+categories: [release]
+tag: v1-7-1
+sha: 11cb0a8
+---
+<!--
+{% comment %}
+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.
+{% endcomment %}
+-->
+
+Apache Calcite's PMC has just released Avatica 1.7.1, the first
+release of [Avatica](http://calcite.apache.org/avatica/) as an
+independent project. We're excited because it makes Avatica easier to
+use, and allows us to be more agile in our release schedule.
+
+(Avatica was previously released as part of Calcite. Avatica is still
+governed by Apache Calcite's PMC, and stored in the same git
+repository as Calcite, but releases are no longer synchronized, and
+Avatica does not depend on any Calcite modules.)
+
+A significant portion of the work of this release was "Maven work" to
+separate the build and release processes, but there were several
+important bug fixes, including a security fix for Jetty (see below).
+
+Performance, specifically on the write path, was a big focus in this
+release.
+[[CALCITE-1091](https://issues.apache.org/jira/browse/CALCITE-1091)]
+contained a number of important changes. Some of these changes (e.g.
+[[CALCITE-1092](https://issues.apache.org/jira/browse/CALCITE-1092)]
+and
+[[CALCITE-1093](https://issues.apache.org/jira/browse/CALCITE-1093)])
+were related to heap usage in the Avatica server, while
+[[CALCITE-1094](https://issues.apache.org/jira/browse/CALCITE-1094)]
+and
+[[CALCITE-1117](https://issues.apache.org/jira/browse/CALCITE-1117)]
+were strictly performance-related.
+
+The latter improved performance the most. Switching to the
+[Apache Commons HttpComponents Client](http://hc.apache.org/)
+library instead of using the Java platform's built-in
+[HttpURLConnection](https://docs.oracle.com/javase/7/docs/api/java/net/HttpURLConnection.html),
+we noticed a 15% improvement in pure write workloads.
+
+Three dependent library upgrades:
+
+* We completely removed
+  [Commons Logging](https://commons.apache.org/proper/commons-logging/)
+  in favor of [SLF4J](http://slf4j.org/) in
+  [[CALCITE-669](https://issues.apache.org/jira/browse/CALCITE-669)].
+  This logging framework update will allow downstream integrators to
+  use the logging implementation of their choice instead of being
+  forced to inherit Commons Logging.
+* We upgraded Jackson from 2.1.1 to 2.6.3 in
+  [[CALCITE-1021](https://issues.apache.org/jira/browse/CALCITE-1021)].
+* We upgraded Jetty from 9.2.7.v20150116 to 9.2.15.v20160210 in
+  [[CALCITE-1156](https://issues.apache.org/jira/browse/CALCITE-1156)]
+  to fix a
+  [security issue](https://blog.gdssecurity.com/labs/2015/2/25/jetleak-vulnerability-remote-leakage-of-shared-buffers-in-je.html).
+
+Note that Avatica's Maven coordinates have changed. The `groupId` is
+now "org.apache.calcite.avatica" (previously "org.apache.calcite"),
+and `artifactId`s are
+"avatica",
+"avatica-metrics",
+"avatica-metrics-dropwizardmetrics3",
+"avatica-noop-driver",
+"avatica-server"
+(previously "calcite-avatica", etc.). Make sure to update these when
+upgrading to this version.

http://git-wip-us.apache.org/repos/asf/calcite/blob/5e3db153/avatica/site/downloads/index.md
----------------------------------------------------------------------
diff --git a/avatica/site/downloads/index.md b/avatica/site/downloads/index.md
index 74e9225..7051c96 100644
--- a/avatica/site/downloads/index.md
+++ b/avatica/site/downloads/index.md
@@ -32,7 +32,7 @@ Release          | Date       | Commit   | Download
 {% endcomment %}{% if post.fullVersion %}{% comment %}
 {% endcomment %}{% assign v = post.fullVersion %}{% comment %}
 {% endcomment %}{% else %}{% comment %}
-{% endcomment %}{% capture v %}apache-calcite-{{ post.version }}{% endcapture %}{% comment %}
+{% endcomment %}{% capture v %}apache-calcite-avatica-{{ post.version }}{% endcapture %}{% comment %}
 {% endcomment %}{% endif %}{% comment %}
 {% endcomment %}{% if forloop.index0 < 1 %}{% comment %}
 {% endcomment %}{% capture p %}http://www.apache.org/dyn/closer.lua?filename=calcite/{{ v }}{% endcapture %}{% comment %}
@@ -69,8 +69,7 @@ download has completed OK.
 
 For fast downloads, current source distributions are hosted on mirror servers;
 older source distributions are in the
-[archive](http://archive.apache.org/dist/calcite/)
-or [incubator archive](http://archive.apache.org/dist/incubator/calcite/).
+[archive](http://archive.apache.org/dist/calcite/).
 If a download from a mirror fails, retry, and the second download will likely
 succeed.
 
@@ -89,7 +88,12 @@ Add the following to the dependencies section of your `pom.xml` file:
 <dependencies>
   <dependency>
     <groupId>org.apache.calcite.avatica</groupId>
-    <artifactId>calcite-avatica</artifactId>
+    <artifactId>avatica</artifactId>
+    <version>{{ current_release.version }}</version>
+  </dependency>
+  <dependency>
+    <groupId>org.apache.calcite.avatica</groupId>
+    <artifactId>avatica-server</artifactId>
     <version>{{ current_release.version }}</version>
   </dependency>
 </dependencies>

http://git-wip-us.apache.org/repos/asf/calcite/blob/5e3db153/site/_config.yml
----------------------------------------------------------------------
diff --git a/site/_config.yml b/site/_config.yml
index 9b8de85..8c78d7e 100644
--- a/site/_config.yml
+++ b/site/_config.yml
@@ -20,7 +20,7 @@ excerpt_separator: ""
 repository: https://github.com/apache/calcite
 destination: target
 exclude: [README.md,Gemfile*]
-keep_files: [".git", ".svn", "apidocs", "testapidocs"]
+keep_files: [".git", ".svn", "apidocs", "testapidocs", "avatica"]
 
 collections:
   docs: