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/02/24 22:38:32 UTC

[1/3] calcite git commit: [CALCITE-1063] Flat lists for 4, 5, 6 elements

Repository: calcite
Updated Branches:
  refs/heads/master 884f01066 -> d14040c52


[CALCITE-1063] Flat lists for 4, 5, 6 elements


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

Branch: refs/heads/master
Commit: 8cba7fc421488cb27ce0756ecd2d422a71f72650
Parents: 884f010
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Jan 21 12:58:12 2016 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Feb 23 11:50:07 2016 -0800

----------------------------------------------------------------------
 .../adapter/enumerable/JavaRowFormat.java       |  24 +
 .../adapter/enumerable/PhysTypeImpl.java        |  27 +
 .../org/apache/calcite/runtime/FlatLists.java   | 684 ++++++++++++++++++-
 .../org/apache/calcite/util/BuiltInMethod.java  |   6 +
 .../java/org/apache/calcite/util/UtilTest.java  | 106 +++
 5 files changed, 846 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/8cba7fc4/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java
index 316af0d..46a4373 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/JavaRowFormat.java
@@ -141,6 +141,30 @@ public enum JavaRowFormat {
                 BuiltInMethod.LIST3.method,
                 expressions),
             List.class);
+      case 4:
+        return Expressions.convert_(
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST4.method,
+                expressions),
+            List.class);
+      case 5:
+        return Expressions.convert_(
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST5.method,
+                expressions),
+            List.class);
+      case 6:
+        return Expressions.convert_(
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST6.method,
+                expressions),
+            List.class);
       default:
         return Expressions.convert_(
             Expressions.call(

http://git-wip-us.apache.org/repos/asf/calcite/blob/8cba7fc4/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
index 0f83889..f321572 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
@@ -591,6 +591,33 @@ public class PhysTypeImpl implements PhysType {
                 BuiltInMethod.LIST3.method,
                 list),
             v1);
+      case 4:
+        return Expressions.lambda(
+            Function1.class,
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST4.method,
+                list),
+            v1);
+      case 5:
+        return Expressions.lambda(
+            Function1.class,
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST5.method,
+                list),
+            v1);
+      case 6:
+        return Expressions.lambda(
+            Function1.class,
+            Expressions.call(
+                List.class,
+                null,
+                BuiltInMethod.LIST6.method,
+                list),
+            v1);
       default:
         return Expressions.lambda(
             Function1.class,

http://git-wip-us.apache.org/repos/asf/calcite/blob/8cba7fc4/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
index 64ce699..bf0c4f2 100644
--- a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
+++ b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
@@ -38,6 +38,17 @@ public class FlatLists {
   public static final ComparableEmptyList COMPARABLE_EMPTY_LIST =
       new ComparableEmptyList();
 
+  /** Creates a flat list with 0 elements. */
+  public static <T> List<T> of() {
+    //noinspection unchecked
+    return COMPARABLE_EMPTY_LIST;
+  }
+
+  /** Creates a flat list with 1 element. */
+  public static <T> List<T> of(T t0) {
+    return new Flat1List<T>(t0);
+  }
+
   /** Creates a flat list with 2 elements. */
   public static <T> List<T> of(T t0, T t1) {
     return new Flat2List<T>(t0, t1);
@@ -48,6 +59,21 @@ public class FlatLists {
     return new Flat3List<T>(t0, t1, t2);
   }
 
+  /** Creates a flat list with 4 elements. */
+  public static <T> List<T> of(T t0, T t1, T t2, T t3) {
+    return new Flat4List<T>(t0, t1, t2, t3);
+  }
+
+  /** Creates a flat list with 6 elements. */
+  public static <T> List<T> of(T t0, T t1, T t2, T t3, T t4) {
+    return new Flat5List<T>(t0, t1, t2, t3, t4);
+  }
+
+  /** Creates a flat list with 6 elements. */
+  public static <T> List<T> of(T t0, T t1, T t2, T t3, T t4, T t5) {
+    return new Flat6List<T>(t0, t1, t2, t3, t4, t5);
+  }
+
   /**
    * Creates a memory-, CPU- and cache-efficient immutable list.
    *
@@ -122,6 +148,12 @@ public class FlatLists {
       return new Flat2List<T>(t[0], t[1]);
     case 3:
       return new Flat3List<T>(t[0], t[1], t[2]);
+    case 4:
+      return new Flat4List<T>(t[0], t[1], t[2], t[3]);
+    case 5:
+      return new Flat5List<T>(t[0], t[1], t[2], t[3], t[4]);
+    case 6:
+      return new Flat6List<T>(t[0], t[1], t[2], t[3], t[4], t[5]);
     default:
       // REVIEW: AbstractList contains a modCount field; we could
       //   write our own implementation and reduce creation overhead a
@@ -147,11 +179,17 @@ public class FlatLists {
       //noinspection unchecked
       return COMPARABLE_EMPTY_LIST;
     case 1:
-      return Collections.singletonList(t[0]);
+      return new Flat1List<>(t[0]);
     case 2:
       return new Flat2List<>(t[0], t[1]);
     case 3:
       return new Flat3List<>(t[0], t[1], t[2]);
+    case 4:
+      return new Flat4List<>(t[0], t[1], t[2], t[3]);
+    case 5:
+      return new Flat5List<>(t[0], t[1], t[2], t[3], t[4]);
+    case 6:
+      return new Flat6List<>(t[0], t[1], t[2], t[3], t[4], t[5]);
     default:
       return ImmutableNullableList.copyOf(t);
     }
@@ -176,6 +214,13 @@ public class FlatLists {
       return new Flat2List<>(t.get(0), t.get(1));
     case 3:
       return new Flat3List<>(t.get(0), t.get(1), t.get(2));
+    case 4:
+      return new Flat4List<>(t.get(0), t.get(1), t.get(2), t.get(3));
+    case 5:
+      return new Flat5List<>(t.get(0), t.get(1), t.get(2), t.get(3), t.get(4));
+    case 6:
+      return new Flat6List<>(t.get(0), t.get(1), t.get(2), t.get(3), t.get(4),
+          t.get(5));
     default:
       // REVIEW: AbstractList contains a modCount field; we could
       //   write our own implementation and reduce creation overhead a
@@ -268,6 +313,108 @@ public class FlatLists {
   }
 
   /**
+   * List that stores its one elements in the one members of the class.
+   * Unlike {@link java.util.ArrayList} or
+   * {@link java.util.Arrays#asList(Object[])} there is
+   * no array, only one piece of memory allocated, therefore is very compact
+   * and cache and CPU efficient.
+   *
+   * <p>The list is read-only and cannot be modified or re-sized.
+   * The element may be null.
+   *
+   * <p>The list is created via {@link FlatLists#of}.
+   *
+   * @param <T> Element type
+   */
+  protected static class Flat1List<T>
+      extends AbstractFlatList<T>
+      implements ComparableList<T> {
+    private final T t0;
+
+    Flat1List(T t0) {
+      this.t0 = t0;
+    }
+
+    public String toString() {
+      return "[" + t0 + "]";
+    }
+
+    public T get(int index) {
+      switch (index) {
+      case 0:
+        return t0;
+      default:
+        throw new IndexOutOfBoundsException("index " + index);
+      }
+    }
+
+    public int size() {
+      return 1;
+    }
+
+    public Iterator<T> iterator() {
+      return Collections.singletonList(t0).iterator();
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o instanceof Flat1List) {
+        Flat1List that = (Flat1List) o;
+        return Objects.equals(this.t0, that.t0);
+      }
+      return Collections.singletonList(t0).equals(o);
+    }
+
+    public int hashCode() {
+      int h = 1;
+      h = h * 31 + Utilities.hash(t0);
+      return h;
+    }
+
+    public int indexOf(Object o) {
+      if (o == null) {
+        if (t0 == null) {
+          return 0;
+        }
+      } else {
+        if (t0.equals(o)) {
+          return 0;
+        }
+      }
+      return -1;
+    }
+
+    public int lastIndexOf(Object o) {
+      if (o == null) {
+        if (t0 == null) {
+          return 0;
+        }
+      } else {
+        if (t0.equals(o)) {
+          return 0;
+        }
+      }
+      return -1;
+    }
+
+    @SuppressWarnings({"unchecked" })
+    public <T2> T2[] toArray(T2[] a) {
+      a[0] = (T2) t0;
+      return a;
+    }
+
+    public Object[] toArray() {
+      return new Object[] {t0};
+    }
+
+    public int compareTo(List o) {
+      return ComparableListImpl.compare((List) this, o);
+    }
+  }
+
+  /**
    * List that stores its two elements in the two members of the class.
    * Unlike {@link java.util.ArrayList} or
    * {@link java.util.Arrays#asList(Object[])} there is
@@ -528,6 +675,541 @@ public class FlatLists {
     }
   }
 
+  /**
+   * List that stores its four elements in the four members of the class.
+   * Unlike {@link java.util.ArrayList} or
+   * {@link java.util.Arrays#asList(Object[])} there is
+   * no array, only one piece of memory allocated, therefore is very compact
+   * and cache and CPU efficient.
+   *
+   * <p>The list is read-only, cannot be modified or re-sized.
+   * The elements may be null.
+   *
+   * <p>The list is created via {@link FlatLists#of(java.util.List)}.
+   *
+   * @param <T> Element type
+   */
+  protected static class Flat4List<T>
+      extends AbstractFlatList<T>
+      implements ComparableList<T> {
+    private final T t0;
+    private final T t1;
+    private final T t2;
+    private final T t3;
+
+    Flat4List(T t0, T t1, T t2, T t3) {
+      this.t0 = t0;
+      this.t1 = t1;
+      this.t2 = t2;
+      this.t3 = t3;
+    }
+
+    public String toString() {
+      return "[" + t0 + ", " + t1 + ", " + t2 + "," + t3 + "]";
+    }
+
+    public T get(int index) {
+      switch (index) {
+      case 0:
+        return t0;
+      case 1:
+        return t1;
+      case 2:
+        return t2;
+      case 3:
+        return t3;
+      default:
+        throw new IndexOutOfBoundsException("index " + index);
+      }
+    }
+
+    public int size() {
+      return 4;
+    }
+
+    public Iterator<T> iterator() {
+      return Arrays.asList(t0, t1, t2, t3).iterator();
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o instanceof Flat4List) {
+        Flat4List that = (Flat4List) o;
+        return Objects.equals(this.t0, that.t0)
+            && Objects.equals(this.t1, that.t1)
+            && Objects.equals(this.t2, that.t2)
+            && Objects.equals(this.t3, that.t3);
+      }
+      return o.equals(this);
+    }
+
+    public int hashCode() {
+      int h = 1;
+      h = h * 31 + Utilities.hash(t0);
+      h = h * 31 + Utilities.hash(t1);
+      h = h * 31 + Utilities.hash(t2);
+      h = h * 31 + Utilities.hash(t3);
+      return h;
+    }
+
+    public int indexOf(Object o) {
+      if (o == null) {
+        if (t0 == null) {
+          return 0;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t3 == null) {
+          return 3;
+        }
+      } else {
+        if (t0.equals(o)) {
+          return 0;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t3.equals(o)) {
+          return 3;
+        }
+      }
+      return -1;
+    }
+
+    public int lastIndexOf(Object o) {
+      if (o == null) {
+        if (t3 == null) {
+          return 3;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t0 == null) {
+          return 0;
+        }
+      } else {
+        if (t3.equals(o)) {
+          return 3;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t0.equals(o)) {
+          return 0;
+        }
+      }
+      return -1;
+    }
+
+    @SuppressWarnings({"unchecked" })
+    public <T2> T2[] toArray(T2[] a) {
+      a[0] = (T2) t0;
+      a[1] = (T2) t1;
+      a[2] = (T2) t2;
+      a[3] = (T2) t3;
+      return a;
+    }
+
+    public Object[] toArray() {
+      return new Object[] {t0, t1, t2, t3};
+    }
+
+    public int compareTo(List o) {
+      return ComparableListImpl.compare((List) this, o);
+    }
+  }
+
+  /**
+   * List that stores its five elements in the five members of the class.
+   * Unlike {@link java.util.ArrayList} or
+   * {@link java.util.Arrays#asList(Object[])} there is
+   * no array, only one piece of memory allocated, therefore is very compact
+   * and cache and CPU efficient.
+   *
+   * <p>The list is read-only, cannot be modified or re-sized.
+   * The elements may be null.
+   *
+   * <p>The list is created via {@link FlatLists#of(java.util.List)}.
+   *
+   * @param <T> Element type
+   */
+  protected static class Flat5List<T>
+      extends AbstractFlatList<T>
+      implements ComparableList<T> {
+    private final T t0;
+    private final T t1;
+    private final T t2;
+    private final T t3;
+    private final T t4;
+
+    Flat5List(T t0, T t1, T t2, T t3, T t4) {
+      this.t0 = t0;
+      this.t1 = t1;
+      this.t2 = t2;
+      this.t3 = t3;
+      this.t4 = t4;
+    }
+
+    public String toString() {
+      return "[" + t0 + ", " + t1 + ", " + t2 + "," + t3 + ", " + t4 + "]";
+    }
+
+    public T get(int index) {
+      switch (index) {
+      case 0:
+        return t0;
+      case 1:
+        return t1;
+      case 2:
+        return t2;
+      case 3:
+        return t3;
+      case 4:
+        return t4;
+      default:
+        throw new IndexOutOfBoundsException("index " + index);
+      }
+    }
+
+    public int size() {
+      return 5;
+    }
+
+    public Iterator<T> iterator() {
+      return Arrays.asList(t0, t1, t2, t3, t4).iterator();
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o instanceof Flat5List) {
+        Flat5List that = (Flat5List) o;
+        return Objects.equals(this.t0, that.t0)
+            && Objects.equals(this.t1, that.t1)
+            && Objects.equals(this.t2, that.t2)
+            && Objects.equals(this.t3, that.t3)
+            && Objects.equals(this.t4, that.t4);
+      }
+      return o.equals(this);
+    }
+
+    public int hashCode() {
+      int h = 1;
+      h = h * 31 + Utilities.hash(t0);
+      h = h * 31 + Utilities.hash(t1);
+      h = h * 31 + Utilities.hash(t2);
+      h = h * 31 + Utilities.hash(t3);
+      h = h * 31 + Utilities.hash(t4);
+      return h;
+    }
+
+    public int indexOf(Object o) {
+      if (o == null) {
+        if (t0 == null) {
+          return 0;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t3 == null) {
+          return 3;
+        }
+        if (t4 == null) {
+          return 4;
+        }
+      } else {
+        if (t0.equals(o)) {
+          return 0;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t3.equals(o)) {
+          return 3;
+        }
+        if (t4.equals(o)) {
+          return 4;
+        }
+      }
+      return -1;
+    }
+
+    public int lastIndexOf(Object o) {
+      if (o == null) {
+        if (t4 == null) {
+          return 4;
+        }
+        if (t3 == null) {
+          return 3;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t0 == null) {
+          return 0;
+        }
+      } else {
+        if (t4.equals(o)) {
+          return 4;
+        }
+        if (t3.equals(o)) {
+          return 3;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t0.equals(o)) {
+          return 0;
+        }
+      }
+      return -1;
+    }
+
+    @SuppressWarnings({"unchecked" })
+    public <T2> T2[] toArray(T2[] a) {
+      a[0] = (T2) t0;
+      a[1] = (T2) t1;
+      a[2] = (T2) t2;
+      a[3] = (T2) t3;
+      a[4] = (T2) t4;
+      return a;
+    }
+
+    public Object[] toArray() {
+      return new Object[] {t0, t1, t2, t3, t4};
+    }
+
+    public int compareTo(List o) {
+      return ComparableListImpl.compare((List) this, o);
+    }
+  }
+
+  /**
+   * List that stores its six elements in the six members of the class.
+   * Unlike {@link java.util.ArrayList} or
+   * {@link java.util.Arrays#asList(Object[])} there is
+   * no array, only one piece of memory allocated, therefore is very compact
+   * and cache and CPU efficient.
+   *
+   * <p>The list is read-only, cannot be modified or re-sized.
+   * The elements may be null.
+   *
+   * <p>The list is created via {@link FlatLists#of(java.util.List)}.
+   *
+   * @param <T> Element type
+   */
+  protected static class Flat6List<T>
+      extends AbstractFlatList<T>
+      implements ComparableList<T> {
+    private final T t0;
+    private final T t1;
+    private final T t2;
+    private final T t3;
+    private final T t4;
+    private final T t5;
+
+    Flat6List(T t0, T t1, T t2, T t3, T t4, T t5) {
+      this.t0 = t0;
+      this.t1 = t1;
+      this.t2 = t2;
+      this.t3 = t3;
+      this.t4 = t4;
+      this.t5 = t5;
+    }
+
+    public String toString() {
+      return "[" + t0 + ", " + t1 + ", " + t2 + "," + t3 + ", " + t4
+          + ", " + t5 + "]";
+    }
+
+    public T get(int index) {
+      switch (index) {
+      case 0:
+        return t0;
+      case 1:
+        return t1;
+      case 2:
+        return t2;
+      case 3:
+        return t3;
+      case 4:
+        return t4;
+      case 5:
+        return t5;
+      default:
+        throw new IndexOutOfBoundsException("index " + index);
+      }
+    }
+
+    public int size() {
+      return 6;
+    }
+
+    public Iterator<T> iterator() {
+      return Arrays.asList(t0, t1, t2, t3, t4, t5).iterator();
+    }
+
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o instanceof Flat6List) {
+        Flat6List that = (Flat6List) o;
+        return Objects.equals(this.t0, that.t0)
+            && Objects.equals(this.t1, that.t1)
+            && Objects.equals(this.t2, that.t2)
+            && Objects.equals(this.t3, that.t3)
+            && Objects.equals(this.t4, that.t4)
+            && Objects.equals(this.t5, that.t5);
+      }
+      return o.equals(this);
+    }
+
+    public int hashCode() {
+      int h = 1;
+      h = h * 31 + Utilities.hash(t0);
+      h = h * 31 + Utilities.hash(t1);
+      h = h * 31 + Utilities.hash(t2);
+      h = h * 31 + Utilities.hash(t3);
+      h = h * 31 + Utilities.hash(t4);
+      h = h * 31 + Utilities.hash(t5);
+      return h;
+    }
+
+    public int indexOf(Object o) {
+      if (o == null) {
+        if (t0 == null) {
+          return 0;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t3 == null) {
+          return 3;
+        }
+        if (t4 == null) {
+          return 4;
+        }
+        if (t5 == null) {
+          return 5;
+        }
+      } else {
+        if (t0.equals(o)) {
+          return 0;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t3.equals(o)) {
+          return 3;
+        }
+        if (t4.equals(o)) {
+          return 4;
+        }
+        if (t5.equals(o)) {
+          return 5;
+        }
+      }
+      return -1;
+    }
+
+    public int lastIndexOf(Object o) {
+      if (o == null) {
+        if (t5 == null) {
+          return 5;
+        }
+        if (t4 == null) {
+          return 4;
+        }
+        if (t3 == null) {
+          return 3;
+        }
+        if (t2 == null) {
+          return 2;
+        }
+        if (t1 == null) {
+          return 1;
+        }
+        if (t0 == null) {
+          return 0;
+        }
+      } else {
+        if (t5.equals(o)) {
+          return 5;
+        }
+        if (t4.equals(o)) {
+          return 4;
+        }
+        if (t3.equals(o)) {
+          return 3;
+        }
+        if (t2.equals(o)) {
+          return 2;
+        }
+        if (t1.equals(o)) {
+          return 1;
+        }
+        if (t0.equals(o)) {
+          return 0;
+        }
+      }
+      return -1;
+    }
+
+    @SuppressWarnings({"unchecked" })
+    public <T2> T2[] toArray(T2[] a) {
+      a[0] = (T2) t0;
+      a[1] = (T2) t1;
+      a[2] = (T2) t2;
+      a[3] = (T2) t3;
+      a[4] = (T2) t4;
+      a[5] = (T2) t5;
+      return a;
+    }
+
+    public Object[] toArray() {
+      return new Object[] {t0, t1, t2, t3, t4, t5};
+    }
+
+    public int compareTo(List o) {
+      return ComparableListImpl.compare((List) this, o);
+    }
+  }
+
   /** Empty list that implements the {@link Comparable} interface. */
   private static class ComparableEmptyList<T>
       extends AbstractList<T>

http://git-wip-us.apache.org/repos/asf/calcite/blob/8cba7fc4/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 9ea40b4..b9924f1 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -179,6 +179,12 @@ public enum BuiltInMethod {
   LIST_N(FlatLists.class, "copyOf", Comparable[].class),
   LIST2(FlatLists.class, "of", Object.class, Object.class),
   LIST3(FlatLists.class, "of", Object.class, Object.class, Object.class),
+  LIST4(FlatLists.class, "of", Object.class, Object.class, Object.class,
+      Object.class),
+  LIST5(FlatLists.class, "of", Object.class, Object.class, Object.class,
+      Object.class, Object.class),
+  LIST6(FlatLists.class, "of", Object.class, Object.class, Object.class,
+      Object.class, Object.class, Object.class),
   COMPARABLE_EMPTY_LIST(FlatLists.class, "COMPARABLE_EMPTY_LIST", true),
   IDENTITY_COMPARER(Functions.class, "identityComparer"),
   IDENTITY_SELECTOR(Functions.class, "identitySelector"),

http://git-wip-us.apache.org/repos/asf/calcite/blob/8cba7fc4/core/src/test/java/org/apache/calcite/util/UtilTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java
index 42a2d78..5a9d0b7 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -985,6 +985,112 @@ public class UtilTest {
     assertThat(cab.compareTo(anb) > 0, is(true));
   }
 
+  @Test public void testFlatList2() {
+    checkFlatList(0);
+    checkFlatList(1);
+    checkFlatList(2);
+    checkFlatList(3);
+    checkFlatList(4);
+    checkFlatList(5);
+    checkFlatList(6);
+    checkFlatList(7);
+  }
+
+  private void checkFlatList(int n) {
+    final List<String> emp;
+    final List<Object> emp1;
+    final List<String> eNull;
+    switch (n) {
+    case 0:
+      emp = FlatLists.of();
+      emp1 = FlatLists.<Object>copyOf();
+      eNull = null;
+      break;
+    case 1:
+      emp = FlatLists.of("A");
+      emp1 = FlatLists.copyOf((Object) "A");
+      eNull = null;
+      break;
+    case 2:
+      emp = FlatLists.of("A", "B");
+      emp1 = FlatLists.of((Object) "A", "B");
+      eNull = FlatLists.of("A", null);
+      break;
+    case 3:
+      emp = FlatLists.of("A", "B", "C");
+      emp1 = FlatLists.copyOf((Object) "A", "B", "C");
+      eNull = FlatLists.of("A", null, "C");
+      break;
+    case 4:
+      emp = FlatLists.of("A", "B", "C", "D");
+      emp1 = FlatLists.copyOf((Object) "A", "B", "C", "D");
+      eNull = FlatLists.of("A", null, "C", "D");
+      break;
+    case 5:
+      emp = FlatLists.of("A", "B", "C", "D", "E");
+      emp1 = FlatLists.copyOf((Object) "A", "B", "C", "D", "E");
+      eNull = FlatLists.of("A", null, "C", "D", "E");
+      break;
+    case 6:
+      emp = FlatLists.of("A", "B", "C", "D", "E", "F");
+      emp1 = FlatLists.copyOf((Object) "A", "B", "C", "D", "E", "F");
+      eNull = FlatLists.of("A", null, "C", "D", "E", "F");
+      break;
+    case 7:
+      emp = FlatLists.of("A", "B", "C", "D", "E", "F", "G");
+      emp1 = FlatLists.copyOf((Object) "A", "B", "C", "D", "E", "F", "G");
+      eNull = FlatLists.of("A", null, "C", "D", "E", "F", "G");
+      break;
+    default:
+      throw new AssertionError(n);
+    }
+    final List<String> emp0 =
+        Arrays.asList("A", "B", "C", "D", "E", "F", "G").subList(0, n);
+    final List<String> eNull0 =
+        Arrays.asList("A", null, "C", "D", "E", "F", "G").subList(0, n);
+    assertEquals(emp, emp0);
+    assertEquals(emp, emp1);
+    assertEquals(emp0, emp1);
+    assertEquals(emp1, emp0);
+    assertEquals(emp.hashCode(), emp0.hashCode());
+    assertEquals(emp.hashCode(), emp1.hashCode());
+
+    assertThat(emp.size(), is(n));
+    if (eNull != null) {
+      assertThat(eNull.size(), is(n));
+    }
+
+    final List<String> an = FlatLists.of("A", null);
+    final List<String> an0 = Arrays.asList("A", null);
+    assertEquals(an, an0);
+    assertEquals(an.hashCode(), an0.hashCode());
+
+    if (eNull != null) {
+      assertEquals(eNull, eNull0);
+      assertEquals(eNull.hashCode(), eNull0.hashCode());
+    }
+
+    assertThat(emp.toString(), is(emp1.toString()));
+    if (eNull != null) {
+      assertThat(eNull.toString().length(), is(emp1.toString().length() + 3));
+    }
+
+    // Comparisons
+    assertThat(emp, instanceOf(Comparable.class));
+    if (n < 7) {
+      assertThat(emp1, instanceOf(Comparable.class));
+    }
+    if (eNull != null) {
+      assertThat(eNull, instanceOf(Comparable.class));
+    }
+    @SuppressWarnings("unchecked")
+    final Comparable<List> cemp = (Comparable) emp;
+    assertThat(cemp.compareTo(emp), is(0));
+    if (eNull != null) {
+      assertThat(cemp.compareTo(eNull) < 0, is(false));
+    }
+  }
+
   /**
    * Unit test for {@link AvaticaUtils#toCamelCase(String)}.
    */


[2/3] calcite git commit: [CALCITE-604] Tune metadata by generating a dispatcher at runtime

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
index ea647a6..c9113a1 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
@@ -44,13 +44,18 @@ import org.apache.calcite.util.Util;
  * RelMdRowCount supplies a default implementation of
  * {@link RelMetadataQuery#getRowCount} for the standard logical algebra.
  */
-public class RelMdRowCount {
+public class RelMdRowCount
+    implements MetadataHandler<BuiltInMetadata.RowCount> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.ROW_COUNT.method, new RelMdRowCount());
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.RowCount> getDef() {
+    return BuiltInMetadata.RowCount.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.RowCount#getRowCount()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
index b7ffcdd..5aa55bd 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
@@ -38,7 +38,8 @@ import java.util.List;
  * RelMdSelectivity supplies a default implementation of
  * {@link RelMetadataQuery#getSelectivity} for the standard logical algebra.
  */
-public class RelMdSelectivity {
+public class RelMdSelectivity
+    implements MetadataHandler<BuiltInMetadata.Selectivity> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.SELECTIVITY.method, new RelMdSelectivity());
@@ -50,6 +51,10 @@ public class RelMdSelectivity {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Selectivity> getDef() {
+    return BuiltInMetadata.Selectivity.DEF;
+  }
+
   public Double getSelectivity(Union rel, RelMetadataQuery mq,
       RexNode predicate) {
     if ((rel.getInputs().size() == 0) || (predicate == null)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
index 8c2502e..0fd808e 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSize.java
@@ -56,7 +56,7 @@ import java.util.List;
  * @see RelMetadataQuery#getAverageColumnSizes
  * @see RelMetadataQuery#getAverageColumnSizesNotNull
  */
-public class RelMdSize {
+public class RelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
   /** Source for
    * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Size}. */
   public static final RelMetadataProvider SOURCE =
@@ -73,6 +73,10 @@ public class RelMdSize {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Size> getDef() {
+    return BuiltInMetadata.Size.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Size#averageRowSize()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
index cb1de64..4f5c4d4 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUniqueKeys.java
@@ -43,7 +43,8 @@ import java.util.Set;
  * RelMdUniqueKeys supplies a default implementation of
  * {@link RelMetadataQuery#getUniqueKeys} for the standard logical algebra.
  */
-public class RelMdUniqueKeys {
+public class RelMdUniqueKeys
+    implements MetadataHandler<BuiltInMetadata.UniqueKeys> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.UNIQUE_KEYS.method, new RelMdUniqueKeys());
@@ -54,6 +55,10 @@ public class RelMdUniqueKeys {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.UniqueKeys> getDef() {
+    return BuiltInMetadata.UniqueKeys.DEF;
+  }
+
   public Set<ImmutableBitSet> getUniqueKeys(Filter rel, RelMetadataQuery mq,
       boolean ignoreNulls) {
     return mq.getUniqueKeys(rel.getInput(), ignoreNulls);

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 a2fd272..c1d5f39 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,6 +18,9 @@ package org.apache.calcite.rel.metadata;
 
 import org.apache.calcite.rel.RelNode;
 
+import java.lang.reflect.Method;
+import java.util.Map;
+
 /**
  * RelMetadataProvider defines an interface for obtaining metadata about
  * relational expressions. This interface is weakly-typed and is not intended to
@@ -26,6 +29,10 @@ import org.apache.calcite.rel.RelNode;
  *
  * <p>For background and motivation, see <a
  * href="http://wiki.eigenbase.org/RelationalExpressionMetadata">wiki</a>.
+ *
+ * <p>If your provider is not a singleton, we recommend that you implement
+ * {@link Object#equals(Object)} and {@link Object#hashCode()} methods. This
+ * makes the cache of {@link JaninoRelMetadataProvider} more effective.
  */
 public interface RelMetadataProvider {
   //~ Methods ----------------------------------------------------------------
@@ -57,6 +64,9 @@ public interface RelMetadataProvider {
   <M extends Metadata> UnboundMetadata<M>
   apply(Class<? extends RelNode> relClass,
       Class<? extends M> metadataClass);
+
+  <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def);
 }
 
 // End RelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 b6eed5b..abca58e 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
@@ -26,12 +26,17 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.util.ImmutableBitSet;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -69,9 +74,75 @@ import java.util.Set;
  * providers. Then supply that instance to the planner via the appropriate
  * plugin mechanism.
  */
-public abstract class RelMetadataQuery {
-  /** Set of active metadata queries. */
-  public final Set<List> set = new HashSet<>();
+public class RelMetadataQuery {
+  /** Set of active metadata queries, and cache of previous results. */
+  public final Map<List, Object> map = new HashMap<>();
+
+  public final JaninoRelMetadataProvider metadataProvider;
+
+  private static final RelMetadataQuery EMPTY = new RelMetadataQuery(false);
+
+  private BuiltInMetadata.Collation.Handler collationHandler;
+  private BuiltInMetadata.ColumnOrigin.Handler columnOriginHandler;
+  private BuiltInMetadata.ColumnUniqueness.Handler columnUniquenessHandler;
+  private BuiltInMetadata.CumulativeCost.Handler cumulativeCostHandler;
+  private BuiltInMetadata.DistinctRowCount.Handler distinctRowCountHandler;
+  private BuiltInMetadata.Distribution.Handler distributionHandler;
+  private BuiltInMetadata.ExplainVisibility.Handler explainVisibilityHandler;
+  private BuiltInMetadata.MaxRowCount.Handler maxRowCountHandler;
+  private BuiltInMetadata.Memory.Handler memoryHandler;
+  private BuiltInMetadata.NonCumulativeCost.Handler nonCumulativeCostHandler;
+  private BuiltInMetadata.Parallelism.Handler parallelismHandler;
+  private BuiltInMetadata.PercentageOriginalRows.Handler percentageOriginalRowsHandler;
+  private BuiltInMetadata.PopulationSize.Handler populationSizeHandler;
+  private BuiltInMetadata.Predicates.Handler predicatesHandler;
+  private BuiltInMetadata.RowCount.Handler rowCountHandler;
+  private BuiltInMetadata.Selectivity.Handler selectivityHandler;
+  private BuiltInMetadata.Size.Handler sizeHandler;
+  private BuiltInMetadata.UniqueKeys.Handler uniqueKeysHandler;
+
+  public static final ThreadLocal<JaninoRelMetadataProvider> THREAD_PROVIDERS =
+      new ThreadLocal<JaninoRelMetadataProvider>() {
+        protected JaninoRelMetadataProvider initialValue() {
+          return JaninoRelMetadataProvider.DEFAULT;
+        }
+      };
+
+  private RelMetadataQuery(JaninoRelMetadataProvider metadataProvider,
+      RelMetadataQuery prototype) {
+    this.metadataProvider = Preconditions.checkNotNull(metadataProvider);
+    this.collationHandler = prototype.collationHandler;
+    this.columnOriginHandler = prototype.columnOriginHandler;
+    this.columnUniquenessHandler = prototype.columnUniquenessHandler;
+    this.cumulativeCostHandler = prototype.cumulativeCostHandler;
+    this.distinctRowCountHandler = prototype.distinctRowCountHandler;
+    this.distributionHandler = prototype.distributionHandler;
+    this.explainVisibilityHandler = prototype.explainVisibilityHandler;
+    this.maxRowCountHandler = prototype.maxRowCountHandler;
+    this.memoryHandler = prototype.memoryHandler;
+    this.nonCumulativeCostHandler = prototype.nonCumulativeCostHandler;
+    this.parallelismHandler = prototype.parallelismHandler;
+    this.percentageOriginalRowsHandler = prototype.percentageOriginalRowsHandler;
+    this.populationSizeHandler = prototype.populationSizeHandler;
+    this.predicatesHandler = prototype.predicatesHandler;
+    this.rowCountHandler = prototype.rowCountHandler;
+    this.selectivityHandler = prototype.selectivityHandler;
+    this.sizeHandler = prototype.sizeHandler;
+    this.uniqueKeysHandler = prototype.uniqueKeysHandler;
+  }
+
+  private static <H> H initialHandler(Class<H> handlerClass) {
+    return handlerClass.cast(
+        Proxy.newProxyInstance(RelMetadataQuery.class.getClassLoader(),
+            new Class[] {handlerClass},
+            new InvocationHandler() {
+              public Object invoke(Object proxy, Method method, Object[] args)
+                  throws Throwable {
+                final RelNode r = (RelNode) args[0];
+                throw new JaninoRelMetadataProvider.NoHandler(r.getClass());
+              }
+            }));
+  }
 
   //~ Methods ----------------------------------------------------------------
 
@@ -80,8 +151,32 @@ public abstract class RelMetadataQuery {
    * occur while computing metadata.
    */
   public static RelMetadataQuery instance() {
-    return new RelMetadataQuery() {
-    };
+    return new RelMetadataQuery(THREAD_PROVIDERS.get(), EMPTY);
+  }
+
+  /** Creates and initializes the instance that will serve as a prototype for
+   * all other instances. */
+  private RelMetadataQuery(boolean dummy) {
+    this.metadataProvider = null;
+    this.collationHandler = initialHandler(BuiltInMetadata.Collation.Handler.class);
+    this.columnOriginHandler = initialHandler(BuiltInMetadata.ColumnOrigin.Handler.class);
+    this.columnUniquenessHandler = initialHandler(BuiltInMetadata.ColumnUniqueness.Handler.class);
+    this.cumulativeCostHandler = initialHandler(BuiltInMetadata.CumulativeCost.Handler.class);
+    this.distinctRowCountHandler = initialHandler(BuiltInMetadata.DistinctRowCount.Handler.class);
+    this.distributionHandler = initialHandler(BuiltInMetadata.Distribution.Handler.class);
+    this.explainVisibilityHandler = initialHandler(BuiltInMetadata.ExplainVisibility.Handler.class);
+    this.maxRowCountHandler = initialHandler(BuiltInMetadata.MaxRowCount.Handler.class);
+    this.memoryHandler = initialHandler(BuiltInMetadata.Memory.Handler.class);
+    this.nonCumulativeCostHandler = initialHandler(BuiltInMetadata.NonCumulativeCost.Handler.class);
+    this.parallelismHandler = initialHandler(BuiltInMetadata.Parallelism.Handler.class);
+    this.percentageOriginalRowsHandler =
+        initialHandler(BuiltInMetadata.PercentageOriginalRows.Handler.class);
+    this.populationSizeHandler = initialHandler(BuiltInMetadata.PopulationSize.Handler.class);
+    this.predicatesHandler = initialHandler(BuiltInMetadata.Predicates.Handler.class);
+    this.rowCountHandler = initialHandler(BuiltInMetadata.RowCount.Handler.class);
+    this.selectivityHandler = initialHandler(BuiltInMetadata.Selectivity.Handler.class);
+    this.sizeHandler = initialHandler(BuiltInMetadata.Size.Handler.class);
+    this.uniqueKeysHandler = initialHandler(BuiltInMetadata.UniqueKeys.Handler.class);
   }
 
   /**
@@ -94,10 +189,15 @@ public abstract class RelMetadataQuery {
    * determined
    */
   public Double getRowCount(RelNode rel) {
-    final BuiltInMetadata.RowCount metadata =
-        rel.metadata(BuiltInMetadata.RowCount.class, this);
-    Double result = metadata.getRowCount();
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result = rowCountHandler.getRowCount(rel, this);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        rowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.RowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -109,9 +209,14 @@ public abstract class RelMetadataQuery {
    * @return max row count
    */
   public Double getMaxRowCount(RelNode rel) {
-    final BuiltInMetadata.MaxRowCount metadata =
-        rel.metadata(BuiltInMetadata.MaxRowCount.class, this);
-    return metadata.getMaxRowCount();
+    for (;;) {
+      try {
+        return maxRowCountHandler.getMaxRowCount(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        maxRowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.MaxRowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -123,9 +228,14 @@ public abstract class RelMetadataQuery {
    * @return estimated cost, or null if no reliable estimate can be determined
    */
   public RelOptCost getCumulativeCost(RelNode rel) {
-    final BuiltInMetadata.CumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.CumulativeCost.class, this);
-    return metadata.getCumulativeCost();
+    for (;;) {
+      try {
+        return cumulativeCostHandler.getCumulativeCost(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        cumulativeCostHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.CumulativeCost.DEF);
+      }
+    }
   }
 
   /**
@@ -137,9 +247,14 @@ public abstract class RelMetadataQuery {
    * @return estimated cost, or null if no reliable estimate can be determined
    */
   public RelOptCost getNonCumulativeCost(RelNode rel) {
-    final BuiltInMetadata.NonCumulativeCost metadata =
-        rel.metadata(BuiltInMetadata.NonCumulativeCost.class, this);
-    return metadata.getNonCumulativeCost();
+    for (;;) {
+      try {
+        return nonCumulativeCostHandler.getNonCumulativeCost(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        nonCumulativeCostHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.NonCumulativeCost.DEF);
+      }
+    }
   }
 
   /**
@@ -152,11 +267,16 @@ public abstract class RelMetadataQuery {
    * reliable estimate can be determined
    */
   public Double getPercentageOriginalRows(RelNode rel) {
-    final BuiltInMetadata.PercentageOriginalRows metadata =
-        rel.metadata(BuiltInMetadata.PercentageOriginalRows.class, this);
-    Double result = metadata.getPercentageOriginalRows();
-    assert isPercentage(result, true);
-    return result;
+    for (;;) {
+      try {
+        Double result =
+            percentageOriginalRowsHandler.getPercentageOriginalRows(rel, this);
+        return validatePercentage(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        percentageOriginalRowsHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.PercentageOriginalRows.DEF);
+      }
+    }
   }
 
   /**
@@ -171,9 +291,14 @@ public abstract class RelMetadataQuery {
    * all)
    */
   public Set<RelColumnOrigin> getColumnOrigins(RelNode rel, int column) {
-    final BuiltInMetadata.ColumnOrigin metadata =
-        rel.metadata(BuiltInMetadata.ColumnOrigin.class, this);
-    return metadata.getColumnOrigins(column);
+    for (;;) {
+      try {
+        return columnOriginHandler.getColumnOrigins(rel, this, column);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        columnOriginHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ColumnOrigin.DEF);
+      }
+    }
   }
 
   /**
@@ -229,11 +354,15 @@ public abstract class RelMetadataQuery {
    * reliable estimate can be determined
    */
   public Double getSelectivity(RelNode rel, RexNode predicate) {
-    final BuiltInMetadata.Selectivity metadata =
-        rel.metadata(BuiltInMetadata.Selectivity.class, this);
-    Double result = metadata.getSelectivity(predicate);
-    assert isPercentage(result, true);
-    return result;
+    for (;;) {
+      try {
+        Double result = selectivityHandler.getSelectivity(rel, this, predicate);
+        return validatePercentage(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        selectivityHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Selectivity.DEF);
+      }
+    }
   }
 
   /**
@@ -246,9 +375,7 @@ public abstract class RelMetadataQuery {
    * (whereas empty set indicates definitely no keys at all)
    */
   public Set<ImmutableBitSet> getUniqueKeys(RelNode rel) {
-    final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
-    return metadata.getUniqueKeys(false);
+    return getUniqueKeys(rel, false);
   }
 
   /**
@@ -265,9 +392,14 @@ public abstract class RelMetadataQuery {
    */
   public Set<ImmutableBitSet> getUniqueKeys(RelNode rel,
       boolean ignoreNulls) {
-    final BuiltInMetadata.UniqueKeys metadata =
-        rel.metadata(BuiltInMetadata.UniqueKeys.class, this);
-    return metadata.getUniqueKeys(ignoreNulls);
+    for (;;) {
+      try {
+        return uniqueKeysHandler.getUniqueKeys(rel, this, ignoreNulls);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        uniqueKeysHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.UniqueKeys.DEF);
+      }
+    }
   }
 
   /**
@@ -282,11 +414,9 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public Boolean areRowsUnique(RelNode rel) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
     final ImmutableBitSet columns =
         ImmutableBitSet.range(rel.getRowType().getFieldCount());
-    return metadata.areColumnsUnique(columns, false);
+    return areColumnsUnique(rel, columns, false);
   }
 
   /**
@@ -302,9 +432,7 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
-    return metadata.areColumnsUnique(columns, false);
+    return areColumnsUnique(rel, columns, false);
   }
 
   /**
@@ -322,9 +450,15 @@ public abstract class RelMetadataQuery {
    */
   public Boolean areColumnsUnique(RelNode rel, ImmutableBitSet columns,
       boolean ignoreNulls) {
-    final BuiltInMetadata.ColumnUniqueness metadata =
-        rel.metadata(BuiltInMetadata.ColumnUniqueness.class, this);
-    return metadata.areColumnsUnique(columns, ignoreNulls);
+    for (;;) {
+      try {
+        return columnUniquenessHandler.areColumnsUnique(rel, this, columns,
+            ignoreNulls);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        columnUniquenessHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ColumnUniqueness.DEF);
+      }
+    }
   }
 
   /**
@@ -337,9 +471,14 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public ImmutableList<RelCollation> collations(RelNode rel) {
-    final BuiltInMetadata.Collation metadata =
-        rel.metadata(BuiltInMetadata.Collation.class, this);
-    return metadata.collations();
+    for (;;) {
+      try {
+        return collationHandler.collations(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        collationHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Collation.DEF);
+      }
+    }
   }
 
   /**
@@ -352,9 +491,14 @@ public abstract class RelMetadataQuery {
    * null if not enough information is available to make that determination
    */
   public RelDistribution distribution(RelNode rel) {
-    final BuiltInMetadata.Distribution metadata =
-        rel.metadata(BuiltInMetadata.Distribution.class, this);
-    return metadata.distribution();
+    for (;;) {
+      try {
+        return distributionHandler.distribution(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        distributionHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Distribution.DEF);
+      }
+    }
   }
 
   /**
@@ -371,10 +515,16 @@ public abstract class RelMetadataQuery {
    */
   public Double getPopulationSize(RelNode rel,
       ImmutableBitSet groupKey) {
-    final BuiltInMetadata.PopulationSize metadata =
-        rel.metadata(BuiltInMetadata.PopulationSize.class, this);
-    Double result = metadata.getPopulationSize(groupKey);
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result =
+            populationSizeHandler.getPopulationSize(rel, this, groupKey);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        populationSizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.PopulationSize.DEF);
+      }
+    }
   }
 
   /**
@@ -386,9 +536,14 @@ public abstract class RelMetadataQuery {
    * @return average size of a row, in bytes, or null if not known
      */
   public Double getAverageRowSize(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    return metadata.averageRowSize();
+    for (;;) {
+      try {
+        return sizeHandler.averageRowSize(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        sizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Size.DEF);
+      }
+    }
   }
 
   /**
@@ -402,17 +557,20 @@ public abstract class RelMetadataQuery {
    * metadata is not available
    */
   public List<Double> getAverageColumnSizes(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    return metadata.averageColumnSizes();
+    for (;;) {
+      try {
+        return sizeHandler.averageColumnSizes(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        sizeHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Size.DEF);
+      }
+    }
   }
 
   /** As {@link #getAverageColumnSizes(org.apache.calcite.rel.RelNode)} but
    * never returns a null list, only ever a list of nulls. */
   public List<Double> getAverageColumnSizesNotNull(RelNode rel) {
-    final BuiltInMetadata.Size metadata =
-        rel.metadata(BuiltInMetadata.Size.class, this);
-    final List<Double> averageColumnSizes = metadata.averageColumnSizes();
+    final List<Double> averageColumnSizes = getAverageColumnSizes(rel);
     return averageColumnSizes == null
         ? Collections.<Double>nCopies(rel.getRowType().getFieldCount(), null)
         : averageColumnSizes;
@@ -429,9 +587,14 @@ public abstract class RelMetadataQuery {
    * known
    */
   public Boolean isPhaseTransition(RelNode rel) {
-    final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class, this);
-    return metadata.isPhaseTransition();
+    for (;;) {
+      try {
+        return parallelismHandler.isPhaseTransition(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        parallelismHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Parallelism.DEF);
+      }
+    }
   }
 
   /**
@@ -443,9 +606,14 @@ public abstract class RelMetadataQuery {
    * @return the number of distinct splits of the data, or null if not known
    */
   public Integer splitCount(RelNode rel) {
-    final BuiltInMetadata.Parallelism metadata =
-        rel.metadata(BuiltInMetadata.Parallelism.class, this);
-    return metadata.splitCount();
+    for (;;) {
+      try {
+        return parallelismHandler.splitCount(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        parallelismHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Parallelism.DEF);
+      }
+    }
   }
 
   /**
@@ -459,9 +627,14 @@ public abstract class RelMetadataQuery {
    * or null if not known
    */
   public Double memory(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.memory();
+    for (;;) {
+      try {
+        return memoryHandler.memory(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -475,9 +648,14 @@ public abstract class RelMetadataQuery {
    * operators within the same phase, across all splits, or null if not known
    */
   public Double cumulativeMemoryWithinPhase(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.cumulativeMemoryWithinPhase();
+    for (;;) {
+      try {
+        return memoryHandler.cumulativeMemoryWithinPhase(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -491,9 +669,14 @@ public abstract class RelMetadataQuery {
    * operators within the same phase, within each split, or null if not known
    */
   public Double cumulativeMemoryWithinPhaseSplit(RelNode rel) {
-    final BuiltInMetadata.Memory metadata =
-        rel.metadata(BuiltInMetadata.Memory.class, this);
-    return metadata.cumulativeMemoryWithinPhaseSplit();
+    for (;;) {
+      try {
+        return memoryHandler.cumulativeMemoryWithinPhaseSplit(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        memoryHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Memory.DEF);
+      }
+    }
   }
 
   /**
@@ -511,10 +694,17 @@ public abstract class RelMetadataQuery {
       RelNode rel,
       ImmutableBitSet groupKey,
       RexNode predicate) {
-    final BuiltInMetadata.DistinctRowCount metadata =
-        rel.metadata(BuiltInMetadata.DistinctRowCount.class, this);
-    Double result = metadata.getDistinctRowCount(groupKey, predicate);
-    return validateResult(result);
+    for (;;) {
+      try {
+        Double result =
+            distinctRowCountHandler.getDistinctRowCount(rel, this, groupKey,
+                predicate);
+        return validateResult(result);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        distinctRowCountHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.DistinctRowCount.DEF);
+      }
+    }
   }
 
   /**
@@ -526,9 +716,14 @@ public abstract class RelMetadataQuery {
    * @return Predicates that can be pulled above this RelNode
    */
   public RelOptPredicateList getPulledUpPredicates(RelNode rel) {
-    final BuiltInMetadata.Predicates metadata =
-        rel.metadata(BuiltInMetadata.Predicates.class, this);
-    return metadata.getPredicates();
+    for (;;) {
+      try {
+        return predicatesHandler.getPredicates(rel, this);
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        predicatesHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.Predicates.DEF);
+      }
+    }
   }
 
   /**
@@ -543,10 +738,21 @@ public abstract class RelMetadataQuery {
    */
   public boolean isVisibleInExplain(RelNode rel,
       SqlExplainLevel explainLevel) {
-    final BuiltInMetadata.ExplainVisibility metadata =
-        rel.metadata(BuiltInMetadata.ExplainVisibility.class, this);
-    Boolean b = metadata.isVisibleInExplain(explainLevel);
-    return b == null || b;
+    for (;;) {
+      try {
+        Boolean b = explainVisibilityHandler.isVisibleInExplain(rel, this,
+            explainLevel);
+        return b == null || b;
+      } catch (JaninoRelMetadataProvider.NoHandler e) {
+        explainVisibilityHandler = metadataProvider.revise(e.relClass,
+            BuiltInMetadata.ExplainVisibility.DEF);
+      }
+    }
+  }
+
+  private static Double validatePercentage(Double result) {
+    assert isPercentage(result, true);
+    return result;
   }
 
   /**
@@ -609,6 +815,7 @@ public abstract class RelMetadataQuery {
     }
     return result;
   }
+
 }
 
 // End RelMetadataQuery.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 1fe4177..b271945 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -60,7 +60,9 @@ import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.logical.LogicalTableScan;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelColumnMapping;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.stream.Delta;
 import org.apache.calcite.rel.stream.LogicalDelta;
 import org.apache.calcite.rel.type.RelDataType;
@@ -575,6 +577,8 @@ public class SqlToRelConverter {
       query = validator.validate(query);
     }
 
+    RelMetadataQuery.THREAD_PROVIDERS.set(
+        JaninoRelMetadataProvider.of(cluster.getMetadataProvider()));
     RelNode result = convertQueryRecursive(query, top, null).rel;
     if (top) {
       if (isStream(query)) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Programs.java b/core/src/main/java/org/apache/calcite/tools/Programs.java
index a52387d..5f2f098 100644
--- a/core/src/main/java/org/apache/calcite/tools/Programs.java
+++ b/core/src/main/java/org/apache/calcite/tools/Programs.java
@@ -102,11 +102,11 @@ public class Programs {
 
   /** Program that converts filters and projects to {@link Calc}s. */
   public static final Program CALC_PROGRAM =
-      calc(new DefaultRelMetadataProvider());
+      calc(DefaultRelMetadataProvider.INSTANCE);
 
   /** Program that expands sub-queries. */
   public static final Program SUB_QUERY_PROGRAM =
-      subquery(new DefaultRelMetadataProvider());
+      subquery(DefaultRelMetadataProvider.INSTANCE);
 
   public static final ImmutableSet<RelOptRule> RULE_SET =
       ImmutableSet.of(
@@ -233,7 +233,7 @@ public class Programs {
               .addRuleInstance(JoinToMultiJoinRule.INSTANCE)
               .build();
           final Program program1 =
-              of(hep, false, new DefaultRelMetadataProvider());
+              of(hep, false, DefaultRelMetadataProvider.INSTANCE);
 
           // Create a program that contains a rule to expand a MultiJoin
           // into heuristically ordered joins.
@@ -279,7 +279,7 @@ public class Programs {
 
   /** Returns the standard program used by Prepare. */
   public static Program standard() {
-    return standard(new DefaultRelMetadataProvider());
+    return standard(DefaultRelMetadataProvider.INSTANCE);
   }
 
   /** Returns the standard program with user metadata provider. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 56c458e..ab7e02d 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -52,6 +52,8 @@ 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.Metadata;
+import org.apache.calcite.rel.metadata.MetadataDef;
+import org.apache.calcite.rel.metadata.MetadataHandler;
 import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelColumnOrigin;
 import org.apache.calcite.rel.metadata.RelMdCollation;
@@ -137,8 +139,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
 
   private RelNode convertSql(String sql) {
     final RelRoot root = tester.convertSqlToRel(sql);
-    DefaultRelMetadataProvider provider = new DefaultRelMetadataProvider();
-    root.rel.getCluster().setMetadataProvider(provider);
+    root.rel.getCluster().setMetadataProvider(DefaultRelMetadataProvider.INSTANCE);
     return root.rel;
   }
 
@@ -1387,7 +1388,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
 
   /** A provider for {@link org.apache.calcite.test.RelMetadataTest.ColType} via
    * reflection. */
-  public static class ColTypeImpl {
+  public static class ColTypeImpl implements MetadataHandler {
     static final ThreadLocal<List<String>> THREAD_LIST = new ThreadLocal<>();
     static final Method METHOD;
     static {
@@ -1402,6 +1403,10 @@ public class RelMetadataTest extends SqlToRelTestBase {
         ReflectiveRelMetadataProvider.reflectiveSource(
             METHOD, new ColTypeImpl());
 
+    public MetadataDef getDef() {
+      throw new UnsupportedOperationException();
+    }
+
     /** Implementation of {@link ColType#getColType(int)} for
      * {@link org.apache.calcite.rel.logical.LogicalAggregate}, called via
      * reflection. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 bfdd32b..0e0e5a0 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -1630,9 +1630,7 @@ public class RelOptRulesTest extends RelOptTestBase {
     assertTrue(relInitial != null);
 
     List<RelMetadataProvider> list = Lists.newArrayList();
-    DefaultRelMetadataProvider defaultProvider =
-        new DefaultRelMetadataProvider();
-    list.add(defaultProvider);
+    list.add(DefaultRelMetadataProvider.INSTANCE);
     planner.registerMetadataProviders(list);
     RelMetadataProvider plannerChain = ChainedRelMetadataProvider.of(list);
     relInitial.getCluster().setMetadataProvider(

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
index 7f191a3..c12aa30 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptTestBase.java
@@ -132,9 +132,7 @@ abstract class RelOptTestBase extends SqlToRelTestBase {
     assertTrue(relInitial != null);
 
     List<RelMetadataProvider> list = Lists.newArrayList();
-    DefaultRelMetadataProvider defaultProvider =
-        new DefaultRelMetadataProvider();
-    list.add(defaultProvider);
+    list.add(DefaultRelMetadataProvider.INSTANCE);
     planner.registerMetadataProviders(list);
     RelMetadataProvider plannerChain =
         ChainedRelMetadataProvider.of(list);


[3/3] calcite git commit: [CALCITE-604] Tune metadata by generating a dispatcher at runtime

Posted by jh...@apache.org.
[CALCITE-604] Tune metadata by generating a dispatcher at runtime

Currently we dispatch to metadata providers using reflection. With
this change, we generate a dispatcher class and compile using Janino.

Cache results within a metadata call. This should yield a performance
improvement when, say, a Join requires many kinds of metadata
(rowCount, averageRowSize, selectivity) and they all rely on the
rowCount of the input, and that rowCount is expensive to compute.


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

Branch: refs/heads/master
Commit: d14040c52f8b0bc351443cff1584e8b587378698
Parents: 8cba7fc
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jan 18 16:22:06 2016 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Feb 24 13:37:49 2016 -0800

----------------------------------------------------------------------
 .../org/apache/calcite/plan/RelOptCluster.java  |   2 +-
 .../calcite/plan/RelOptMaterialization.java     |   4 +-
 .../plan/hep/HepRelMetadataProvider.java        |  20 +
 .../calcite/plan/volcano/VolcanoPlanner.java    |  13 +
 .../volcano/VolcanoRelMetadataProvider.java     |  20 +
 .../calcite/rel/metadata/BuiltInMetadata.java   | 163 ++++++
 .../metadata/CachingRelMetadataProvider.java    |   5 +
 .../metadata/ChainedRelMetadataProvider.java    |  23 +
 .../metadata/DefaultRelMetadataProvider.java    |   8 +-
 .../rel/metadata/JaninoRelMetadataProvider.java | 522 +++++++++++++++++++
 .../calcite/rel/metadata/MetadataDef.java       |  67 +++
 .../calcite/rel/metadata/MetadataHandler.java   |  28 +
 .../calcite/rel/metadata/NullSentinel.java      |  14 +-
 .../metadata/ReflectiveRelMetadataProvider.java | 185 ++++---
 .../calcite/rel/metadata/RelMdCollation.java    |   7 +-
 .../rel/metadata/RelMdColumnOrigins.java        |   7 +-
 .../rel/metadata/RelMdColumnUniqueness.java     |   7 +-
 .../rel/metadata/RelMdDistinctRowCount.java     |   7 +-
 .../calcite/rel/metadata/RelMdDistribution.java |   7 +-
 .../rel/metadata/RelMdExplainVisibility.java    |   7 +-
 .../calcite/rel/metadata/RelMdMaxRowCount.java  |   7 +-
 .../calcite/rel/metadata/RelMdMemory.java       |   6 +-
 .../calcite/rel/metadata/RelMdParallelism.java  |   7 +-
 .../metadata/RelMdPercentageOriginalRows.java   |   7 +-
 .../rel/metadata/RelMdPopulationSize.java       |   7 +-
 .../calcite/rel/metadata/RelMdPredicates.java   |   7 +-
 .../calcite/rel/metadata/RelMdRowCount.java     |   7 +-
 .../calcite/rel/metadata/RelMdSelectivity.java  |   7 +-
 .../apache/calcite/rel/metadata/RelMdSize.java  |   6 +-
 .../calcite/rel/metadata/RelMdUniqueKeys.java   |   7 +-
 .../rel/metadata/RelMetadataProvider.java       |  10 +
 .../calcite/rel/metadata/RelMetadataQuery.java  | 391 ++++++++++----
 .../calcite/sql2rel/SqlToRelConverter.java      |   4 +
 .../java/org/apache/calcite/tools/Programs.java |   8 +-
 .../apache/calcite/test/RelMetadataTest.java    |  11 +-
 .../apache/calcite/test/RelOptRulesTest.java    |   4 +-
 .../org/apache/calcite/test/RelOptTestBase.java |   4 +-
 37 files changed, 1418 insertions(+), 198 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
index 241f4e0..f88e232 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
@@ -81,7 +81,7 @@ public class RelOptCluster {
 
     // set up a default rel metadata provider,
     // giving the planner first crack at everything
-    setMetadataProvider(new DefaultRelMetadataProvider());
+    setMetadataProvider(DefaultRelMetadataProvider.INSTANCE);
     this.emptyTraitSet = planner.emptyTraitSet();
     assert emptyTraitSet.size() == planner.getRelTraitDefs().size();
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
index 4108cff..1da831d 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
@@ -204,7 +204,7 @@ public class RelOptMaterialization {
             AggregateProjectMergeRule.INSTANCE,
             AggregateFilterTransposeRule.INSTANCE),
         false,
-        new DefaultRelMetadataProvider());
+        DefaultRelMetadataProvider.INSTANCE);
     return program.run(null, rel2, null);
   }
 
@@ -273,7 +273,7 @@ public class RelOptMaterialization {
             FilterJoinRule.FilterIntoJoinRule.FILTER_ON_JOIN,
             ProjectMergeRule.INSTANCE),
         false,
-        new DefaultRelMetadataProvider());
+        DefaultRelMetadataProvider.INSTANCE);
     if (CalcitePrepareImpl.DEBUG) {
       System.out.println(
           RelOptUtil.dumpPlan(

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 3f1170a..317fe94 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
@@ -18,10 +18,17 @@ package org.apache.calcite.plan.hep;
 
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.metadata.Metadata;
+import org.apache.calcite.rel.metadata.MetadataDef;
+import org.apache.calcite.rel.metadata.MetadataHandler;
 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 java.lang.reflect.Method;
+import java.util.Map;
+
 /**
  * HepRelMetadataProvider implements the {@link RelMetadataProvider} interface
  * by combining metadata from the rels inside of a {@link HepRelVertex}.
@@ -29,6 +36,14 @@ import org.apache.calcite.rel.metadata.UnboundMetadata;
 class HepRelMetadataProvider implements RelMetadataProvider {
   //~ Methods ----------------------------------------------------------------
 
+  @Override public boolean equals(Object obj) {
+    return obj instanceof HepRelMetadataProvider;
+  }
+
+  @Override public int hashCode() {
+    return 107;
+  }
+
   public <M extends Metadata> UnboundMetadata<M>
   apply(Class<? extends RelNode> relClass,
       final Class<? extends M> metadataClass) {
@@ -46,6 +61,11 @@ class HepRelMetadataProvider implements RelMetadataProvider {
       }
     };
   }
+
+  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def) {
+    return ImmutableMap.of();
+  }
 }
 
 // End HepRelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index 2565ca7..8cb91b0 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -48,6 +48,7 @@ import org.apache.calcite.rel.RelVisitor;
 import org.apache.calcite.rel.convert.Converter;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.rules.AggregateJoinTransposeRule;
@@ -322,6 +323,11 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
   }
 
   public void setRoot(RelNode rel) {
+    // We're registered all the rules, and therefore RelNode classes,
+    // we're interested in, and have not yet started calling metadata providers.
+    // So now is a good time to tell the metadata layer what to expect.
+    registerMetadataRels();
+
     this.root = registerImpl(rel, null);
     if (this.originalRoot == null) {
       this.originalRoot = rel;
@@ -842,6 +848,13 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
     return cheapest;
   }
 
+  /** Informs {@link JaninoRelMetadataProvider} about the different kinds of
+   * {@link RelNode} that we will be dealing with. It will reduce the number
+   * of times that we need to re-generate the provider. */
+  private void registerMetadataRels() {
+    JaninoRelMetadataProvider.DEFAULT.register(classOperands.keySet());
+  }
+
   /** Ensures that the subset that is the root relational expression contains
    * converters to all other subsets in its equivalence set.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 201670f..c32d7d3 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
@@ -18,10 +18,17 @@ package org.apache.calcite.plan.volcano;
 
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.metadata.Metadata;
+import org.apache.calcite.rel.metadata.MetadataDef;
+import org.apache.calcite.rel.metadata.MetadataHandler;
 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 java.lang.reflect.Method;
+import java.util.Map;
+
 /**
  * VolcanoRelMetadataProvider implements the {@link RelMetadataProvider}
  * interface by combining metadata from the rels making up an equivalence class.
@@ -29,6 +36,14 @@ import org.apache.calcite.rel.metadata.UnboundMetadata;
 public class VolcanoRelMetadataProvider implements RelMetadataProvider {
   //~ Methods ----------------------------------------------------------------
 
+  @Override public boolean equals(Object obj) {
+    return obj instanceof VolcanoRelMetadataProvider;
+  }
+
+  @Override public int hashCode() {
+    return 103;
+  }
+
   public <M extends Metadata> UnboundMetadata<M>
   apply(Class<? extends RelNode> relClass,
       final Class<? extends M> metadataClass) {
@@ -99,6 +114,11 @@ public class VolcanoRelMetadataProvider implements RelMetadataProvider {
       }
     };
   }
+
+  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def) {
+    return ImmutableMap.of();
+  }
 }
 
 // End VolcanoRelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
index 87fbbf4..0ff0515 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
@@ -23,6 +23,7 @@ import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlExplainLevel;
+import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
 
 import com.google.common.collect.ImmutableList;
@@ -37,6 +38,9 @@ public abstract class BuiltInMetadata {
 
   /** Metadata about the selectivity of a predicate. */
   public interface Selectivity extends Metadata {
+    MetadataDef<Selectivity> DEF = MetadataDef.of(Selectivity.class,
+        Selectivity.Handler.class, BuiltInMethod.SELECTIVITY.method);
+
     /**
      * Estimates the percentage of an expression's output rows which satisfy a
      * given predicate. Returns null to indicate that no reliable estimate can
@@ -48,10 +52,18 @@ public abstract class BuiltInMetadata {
      * reliable estimate can be determined
      */
     Double getSelectivity(RexNode predicate);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Selectivity> {
+      Double getSelectivity(RelNode r, RelMetadataQuery mq, RexNode predicate);
+    }
   }
 
   /** Metadata about which combinations of columns are unique identifiers. */
   public interface UniqueKeys extends Metadata {
+    MetadataDef<UniqueKeys> DEF = MetadataDef.of(UniqueKeys.class,
+        UniqueKeys.Handler.class, BuiltInMethod.UNIQUE_KEYS.method);
+
     /**
      * Determines the set of unique minimal keys for this expression. A key is
      * represented as an {@link org.apache.calcite.util.ImmutableBitSet}, where
@@ -66,10 +78,19 @@ public abstract class BuiltInMetadata {
      * (whereas empty set indicates definitely no keys at all)
      */
     Set<ImmutableBitSet> getUniqueKeys(boolean ignoreNulls);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<UniqueKeys> {
+      Set<ImmutableBitSet> getUniqueKeys(RelNode r, RelMetadataQuery mq,
+          boolean ignoreNulls);
+    }
   }
 
   /** Metadata about whether a set of columns uniquely identifies a row. */
   public interface ColumnUniqueness extends Metadata {
+    MetadataDef<ColumnUniqueness> DEF = MetadataDef.of(ColumnUniqueness.class,
+        ColumnUniqueness.Handler.class, BuiltInMethod.COLUMN_UNIQUENESS.method);
+
     /**
      * Determines whether a specified set of columns from a specified relational
      * expression are unique.
@@ -93,12 +114,26 @@ public abstract class BuiltInMetadata {
      * null if not enough information is available to make that determination
      */
     Boolean areColumnsUnique(ImmutableBitSet columns, boolean ignoreNulls);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<ColumnUniqueness> {
+      Boolean areColumnsUnique(RelNode r, RelMetadataQuery mq,
+          ImmutableBitSet columns, boolean ignoreNulls);
+    }
   }
 
   /** Metadata about which columns are sorted. */
   public interface Collation extends Metadata {
+    MetadataDef<Collation> DEF = MetadataDef.of(Collation.class,
+        Collation.Handler.class, BuiltInMethod.COLLATIONS.method);
+
     /** Determines which columns are sorted. */
     ImmutableList<RelCollation> collations();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Collation> {
+      ImmutableList<RelCollation> collations(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about how a relational expression is distributed.
@@ -113,12 +148,23 @@ public abstract class BuiltInMetadata {
    * among nodes, but it may be partitioned among threads running on the same
    * node. */
   public interface Distribution extends Metadata {
+    MetadataDef<Distribution> DEF = MetadataDef.of(Distribution.class,
+        Distribution.Handler.class, BuiltInMethod.DISTRIBUTION.method);
+
     /** Determines how the rows are distributed. */
     RelDistribution distribution();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Distribution> {
+      RelDistribution distribution(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the number of rows returned by a relational expression. */
   public interface RowCount extends Metadata {
+    MetadataDef<RowCount> DEF = MetadataDef.of(RowCount.class,
+        RowCount.Handler.class, BuiltInMethod.ROW_COUNT.method);
+
     /**
      * Estimates the number of rows which will be returned by a relational
      * expression. The default implementation for this query asks the rel itself
@@ -129,11 +175,19 @@ public abstract class BuiltInMetadata {
      * determined
      */
     Double getRowCount();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<RowCount> {
+      Double getRowCount(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the maximum number of rows returned by a relational
    * expression. */
   public interface MaxRowCount extends Metadata {
+    MetadataDef<MaxRowCount> DEF = MetadataDef.of(MaxRowCount.class,
+        MaxRowCount.Handler.class, BuiltInMethod.MAX_ROW_COUNT.method);
+
     /**
      * Estimates the max number of rows which will be returned by a relational
      * expression.
@@ -145,11 +199,19 @@ public abstract class BuiltInMetadata {
      * @return upper bound on the number of rows returned
      */
     Double getMaxRowCount();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<MaxRowCount> {
+      Double getMaxRowCount(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the number of distinct rows returned by a set of columns
    * in a relational expression. */
   public interface DistinctRowCount extends Metadata {
+    MetadataDef<DistinctRowCount> DEF = MetadataDef.of(DistinctRowCount.class,
+        DistinctRowCount.Handler.class, BuiltInMethod.DISTINCT_ROW_COUNT.method);
+
     /**
      * Estimates the number of rows which would be produced by a GROUP BY on the
      * set of columns indicated by groupKey, where the input to the GROUP BY has
@@ -163,11 +225,22 @@ public abstract class BuiltInMetadata {
      * if no reliable estimate can be determined
      */
     Double getDistinctRowCount(ImmutableBitSet groupKey, RexNode predicate);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<DistinctRowCount> {
+      Double getDistinctRowCount(RelNode r, RelMetadataQuery mq,
+          ImmutableBitSet groupKey, RexNode predicate);
+    }
   }
 
   /** Metadata about the proportion of original rows that remain in a relational
    * expression. */
   public interface PercentageOriginalRows extends Metadata {
+    MetadataDef<PercentageOriginalRows> DEF =
+        MetadataDef.of(PercentageOriginalRows.class,
+            PercentageOriginalRows.Handler.class,
+            BuiltInMethod.PERCENTAGE_ORIGINAL_ROWS.method);
+
     /**
      * Estimates the percentage of the number of rows actually produced by a
      * relational expression out of the number of rows it would produce if all
@@ -177,11 +250,19 @@ public abstract class BuiltInMetadata {
      * reliable estimate can be determined
      */
     Double getPercentageOriginalRows();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<PercentageOriginalRows> {
+      Double getPercentageOriginalRows(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the number of distinct values in the original source of a
    * column or set of columns. */
   public interface PopulationSize extends Metadata {
+    MetadataDef<PopulationSize> DEF = MetadataDef.of(PopulationSize.class,
+        PopulationSize.Handler.class, BuiltInMethod.POPULATION_SIZE.method);
+
     /**
      * Estimates the distinct row count in the original source for the given
      * {@code groupKey}, ignoring any filtering being applied by the expression.
@@ -194,10 +275,20 @@ public abstract class BuiltInMetadata {
      * estimate can be determined
      */
     Double getPopulationSize(ImmutableBitSet groupKey);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<PopulationSize> {
+      Double getPopulationSize(RelNode r, RelMetadataQuery mq,
+          ImmutableBitSet groupKey);
+    }
   }
 
   /** Metadata about the size of rows and columns. */
   public interface Size extends Metadata {
+    MetadataDef<Size> DEF = MetadataDef.of(Size.class, Size.Handler.class,
+        BuiltInMethod.AVERAGE_ROW_SIZE.method,
+        BuiltInMethod.AVERAGE_COLUMN_SIZES.method);
+
     /**
      * Determines the average size (in bytes) of a row from this relational
      * expression.
@@ -222,10 +313,19 @@ public abstract class BuiltInMetadata {
      * the metadata is not available
      */
     List<Double> averageColumnSizes();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Size> {
+      Double averageRowSize(RelNode r, RelMetadataQuery mq);
+      List<Double> averageColumnSizes(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the origins of columns. */
   public interface ColumnOrigin extends Metadata {
+    MetadataDef<ColumnOrigin> DEF = MetadataDef.of(ColumnOrigin.class,
+        ColumnOrigin.Handler.class, BuiltInMethod.COLUMN_ORIGIN.method);
+
     /**
      * For a given output column of an expression, determines all columns of
      * underlying tables which contribute to result values. An output column may
@@ -239,11 +339,20 @@ public abstract class BuiltInMetadata {
      * all)
      */
     Set<RelColumnOrigin> getColumnOrigins(int outputColumn);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<ColumnOrigin> {
+      Set<RelColumnOrigin> getColumnOrigins(RelNode r, RelMetadataQuery mq,
+          int outputColumn);
+    }
   }
 
   /** Metadata about the cost of evaluating a relational expression, including
    * all of its inputs. */
   public interface CumulativeCost extends Metadata {
+    MetadataDef<CumulativeCost> DEF = MetadataDef.of(CumulativeCost.class,
+        CumulativeCost.Handler.class, BuiltInMethod.CUMULATIVE_COST.method);
+
     /**
      * Estimates the cost of executing a relational expression, including the
      * cost of its inputs. The default implementation for this query adds
@@ -255,11 +364,20 @@ public abstract class BuiltInMetadata {
      * determined
      */
     RelOptCost getCumulativeCost();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<CumulativeCost> {
+      RelOptCost getCumulativeCost(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the cost of evaluating a relational expression, not
    * including its inputs. */
   public interface NonCumulativeCost extends Metadata {
+    MetadataDef<NonCumulativeCost> DEF = MetadataDef.of(NonCumulativeCost.class,
+        NonCumulativeCost.Handler.class,
+        BuiltInMethod.NON_CUMULATIVE_COST.method);
+
     /**
      * Estimates the cost of executing a relational expression, not counting the
      * cost of its inputs. (However, the non-cumulative cost is still usually
@@ -271,10 +389,19 @@ public abstract class BuiltInMetadata {
      * determined
      */
     RelOptCost getNonCumulativeCost();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<NonCumulativeCost> {
+      RelOptCost getNonCumulativeCost(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about whether a relational expression should appear in a plan. */
   public interface ExplainVisibility extends Metadata {
+    MetadataDef<ExplainVisibility> DEF = MetadataDef.of(ExplainVisibility.class,
+        ExplainVisibility.Handler.class,
+        BuiltInMethod.EXPLAIN_VISIBILITY.method);
+
     /**
      * Determines whether a relational expression should be visible in EXPLAIN
      * PLAN output at a particular level of detail.
@@ -283,11 +410,20 @@ public abstract class BuiltInMetadata {
      * @return true for visible, false for invisible
      */
     Boolean isVisibleInExplain(SqlExplainLevel explainLevel);
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<ExplainVisibility> {
+      Boolean isVisibleInExplain(RelNode r, RelMetadataQuery mq,
+          SqlExplainLevel explainLevel);
+    }
   }
 
   /** Metadata about the predicates that hold in the rows emitted from a
    * relational expression. */
   public interface Predicates extends Metadata {
+    MetadataDef<Predicates> DEF = MetadataDef.of(Predicates.class,
+        Predicates.Handler.class, BuiltInMethod.PREDICATES.method);
+
     /**
      * Derives the predicates that hold on rows emitted from a relational
      * expression.
@@ -295,12 +431,21 @@ public abstract class BuiltInMetadata {
      * @return Predicate list
      */
     RelOptPredicateList getPredicates();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Predicates> {
+      RelOptPredicateList getPredicates(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the degree of parallelism of a relational expression, and
    * how its operators are assigned to processes with independent resource
    * pools. */
   public interface Parallelism extends Metadata {
+    MetadataDef<Parallelism> DEF = MetadataDef.of(Parallelism.class,
+        Parallelism.Handler.class, BuiltInMethod.IS_PHASE_TRANSITION.method,
+        BuiltInMethod.SPLIT_COUNT.method);
+
     /** Returns whether each physical operator implementing this relational
      * expression belongs to a different process than its inputs.
      *
@@ -321,10 +466,21 @@ public abstract class BuiltInMetadata {
      * each operator instance.
      */
     Integer splitCount();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Parallelism> {
+      Boolean isPhaseTransition(RelNode r, RelMetadataQuery mq);
+      Integer splitCount(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** Metadata about the memory use of an operator. */
   public interface Memory extends Metadata {
+    MetadataDef<Memory> DEF = MetadataDef.of(Memory.class,
+        Memory.Handler.class, BuiltInMethod.MEMORY.method,
+        BuiltInMethod.CUMULATIVE_MEMORY_WITHIN_PHASE.method,
+        BuiltInMethod.CUMULATIVE_MEMORY_WITHIN_PHASE_SPLIT.method);
+
     /** Returns the expected amount of memory, in bytes, required by a physical
      * operator implementing this relational expression, across all splits.
      *
@@ -356,6 +512,13 @@ public abstract class BuiltInMetadata {
      *     = cumulativeMemoryWithinPhase / Parallelism.splitCount</blockquote>
      */
     Double cumulativeMemoryWithinPhaseSplit();
+
+    /** Handler API. */
+    interface Handler extends MetadataHandler<Memory> {
+      Double memory(RelNode r, RelMetadataQuery mq);
+      Double cumulativeMemoryWithinPhase(RelNode r, RelMetadataQuery mq);
+      Double cumulativeMemoryWithinPhaseSplit(RelNode r, RelMetadataQuery mq);
+    }
   }
 
   /** The built-in forms of metadata. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 8438a8d..202bf4c 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
@@ -77,6 +77,11 @@ public class CachingRelMetadataProvider implements RelMetadataProvider {
     };
   }
 
+  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def) {
+    return underlyingProvider.handlers(def);
+  }
+
   //~ Inner Classes ----------------------------------------------------------
 
   /** An entry in the cache. Consists of the cached object and the timestamp

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 eb8aec5..698bc9c 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,6 +20,7 @@ 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.Lists;
 
 import java.lang.reflect.InvocationHandler;
@@ -28,6 +29,7 @@ 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}
@@ -51,10 +53,21 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
   protected ChainedRelMetadataProvider(
       ImmutableList<RelMetadataProvider> providers) {
     this.providers = providers;
+    assert !providers.contains(this);
   }
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public boolean equals(Object obj) {
+    return obj == this
+        || obj instanceof ChainedRelMetadataProvider
+        && providers.equals(((ChainedRelMetadataProvider) obj).providers);
+  }
+
+  @Override public int hashCode() {
+    return providers.hashCode();
+  }
+
   public <M extends Metadata> UnboundMetadata<M>
   apply(Class<? extends RelNode> relClass,
       final Class<? extends M> metadataClass) {
@@ -91,6 +104,16 @@ public class ChainedRelMetadataProvider implements RelMetadataProvider {
     }
   }
 
+  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def) {
+    final ImmutableMap.Builder<Method, MetadataHandler<M>> builder =
+        ImmutableMap.builder();
+    for (RelMetadataProvider provider : providers.reverse()) {
+      builder.putAll(provider.handlers(def));
+    }
+    return builder.build();
+  }
+
   /** Creates a chain. */
   public static RelMetadataProvider of(List<RelMetadataProvider> list) {
     return new ChainedRelMetadataProvider(ImmutableList.copyOf(list));

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
index e40f1d4..2abd5b5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
@@ -25,14 +25,20 @@ import com.google.common.collect.ImmutableList;
  * the methods declared in {@link RelMetadataQuery}.
  */
 public class DefaultRelMetadataProvider extends ChainedRelMetadataProvider {
+  public static final DefaultRelMetadataProvider INSTANCE =
+      new DefaultRelMetadataProvider();
+
   //~ Constructors -----------------------------------------------------------
 
   /**
    * Creates a new default provider. This provider defines "catch-all"
    * handlers for generic RelNodes, so it should always be given lowest
    * priority when chaining.
+   *
+   * <p>Use this constructor only from a sub-class. Otherwise use the singleton
+   * instance, {@link #INSTANCE}.
    */
-  public DefaultRelMetadataProvider() {
+  protected DefaultRelMetadataProvider() {
     super(
         ImmutableList.of(
             RelMdPercentageOriginalRows.SOURCE,

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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
new file mode 100644
index 0000000..c861106
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/JaninoRelMetadataProvider.java
@@ -0,0 +1,522 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.adapter.enumerable.EnumerableAggregate;
+import org.apache.calcite.adapter.enumerable.EnumerableFilter;
+import org.apache.calcite.adapter.enumerable.EnumerableJoin;
+import org.apache.calcite.adapter.enumerable.EnumerableProject;
+import org.apache.calcite.adapter.enumerable.EnumerableTableScan;
+import org.apache.calcite.interpreter.JaninoRexCompiler;
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.tree.ClassDeclaration;
+import org.apache.calcite.linq4j.tree.MemberDeclaration;
+import org.apache.calcite.linq4j.tree.Primitive;
+import org.apache.calcite.plan.hep.HepRelVertex;
+import org.apache.calcite.plan.volcano.AbstractConverter;
+import org.apache.calcite.plan.volcano.RelSubset;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
+import org.apache.calcite.rel.AbstractRelNode;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterImpl;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalCalc;
+import org.apache.calcite.rel.logical.LogicalCorrelate;
+import org.apache.calcite.rel.logical.LogicalExchange;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalIntersect;
+import org.apache.calcite.rel.logical.LogicalJoin;
+import org.apache.calcite.rel.logical.LogicalMinus;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalSort;
+import org.apache.calcite.rel.logical.LogicalTableFunctionScan;
+import org.apache.calcite.rel.logical.LogicalTableModify;
+import org.apache.calcite.rel.logical.LogicalTableScan;
+import org.apache.calcite.rel.logical.LogicalUnion;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.logical.LogicalWindow;
+import org.apache.calcite.rel.stream.LogicalChi;
+import org.apache.calcite.rel.stream.LogicalDelta;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.ControlFlowException;
+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.collect.ImmutableList;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.codehaus.commons.compiler.CompileException;
+import org.codehaus.commons.compiler.CompilerFactoryFactory;
+import org.codehaus.commons.compiler.IClassBodyEvaluator;
+import org.codehaus.commons.compiler.ICompilerFactory;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
+import javax.annotation.Nonnull;
+
+/**
+ * Implementation of the {@link RelMetadataProvider} interface that generates
+ * a class that dispatches to the underlying providers.
+ */
+public class JaninoRelMetadataProvider implements RelMetadataProvider {
+  private final RelMetadataProvider provider;
+
+  // Constants and static fields
+
+  public static final JaninoRelMetadataProvider DEFAULT =
+      JaninoRelMetadataProvider.of(DefaultRelMetadataProvider.INSTANCE);
+
+  private static final Set<Class<? extends RelNode>> ALL_RELS =
+      new CopyOnWriteArraySet<>();
+
+  /** Cache of pre-generated handlers by provider and kind of metadata.
+   * For the cache to be effective, providers should implement identity
+   * correctly. */
+  private static final LoadingCache<Key, MetadataHandler> HANDLERS =
+      CacheBuilder.newBuilder().build(
+          new CacheLoader<Key, MetadataHandler>() {
+            public MetadataHandler load(@Nonnull Key key) {
+              //noinspection unchecked
+              return load3(key.def, key.provider.handlers(key.def),
+                  key.relClasses);
+            }
+          });
+
+  // Pre-register the most common relational operators, to reduce the number of
+  // times we re-generate.
+  static {
+    DEFAULT.register(
+        Arrays.asList(RelNode.class,
+            AbstractRelNode.class,
+            RelSubset.class,
+            HepRelVertex.class,
+            ConverterImpl.class,
+            AbstractConverter.class,
+
+            LogicalAggregate.class,
+            LogicalCalc.class,
+            LogicalCorrelate.class,
+            LogicalExchange.class,
+            LogicalFilter.class,
+            LogicalIntersect.class,
+            LogicalJoin.class,
+            LogicalMinus.class,
+            LogicalProject.class,
+            LogicalSort.class,
+            LogicalTableFunctionScan.class,
+            LogicalTableModify.class,
+            LogicalTableScan.class,
+            LogicalUnion.class,
+            LogicalValues.class,
+            LogicalWindow.class,
+            LogicalChi.class,
+            LogicalDelta.class,
+
+            EnumerableAggregate.class,
+            EnumerableFilter.class,
+            EnumerableProject.class,
+            EnumerableJoin.class,
+            EnumerableTableScan.class));
+  }
+
+  /** Private constructor; use {@link #of}. */
+  private JaninoRelMetadataProvider(RelMetadataProvider provider) {
+    this.provider = provider;
+  }
+
+  /** Creates a JaninoRelMetadataProvider.
+   *
+   * @param provider Underlying provider
+   */
+  public static JaninoRelMetadataProvider of(RelMetadataProvider provider) {
+    if (provider instanceof JaninoRelMetadataProvider) {
+      return (JaninoRelMetadataProvider) provider;
+    }
+    return new JaninoRelMetadataProvider(provider);
+  }
+
+  @Override public boolean equals(Object obj) {
+    return obj == this
+        || obj instanceof JaninoRelMetadataProvider
+        && ((JaninoRelMetadataProvider) obj).provider.equals(provider);
+  }
+
+  @Override public int hashCode() {
+    return 109 + provider.hashCode();
+  }
+
+  public <M extends Metadata> UnboundMetadata<M> apply(
+      Class<? extends RelNode> relClass, Class<? extends M> metadataClass) {
+    throw new UnsupportedOperationException();
+  }
+
+  public <M extends Metadata> Map<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,
+      ImmutableList<Class<? extends RelNode>> relClasses) {
+    final StringBuilder buff = new StringBuilder();
+    final String name =
+        "GeneratedMetadataHandler_" + def.metadataClass.getSimpleName();
+    final Set<MetadataHandler> providerSet = new HashSet<>();
+    final List<Pair<String, MetadataHandler>> providerList = new ArrayList<>();
+    //noinspection unchecked
+    final ReflectiveRelMetadataProvider.Space space =
+        new ReflectiveRelMetadataProvider.Space((Map) map);
+    for (MetadataHandler provider : space.providerMap.values()) {
+      if (providerSet.add(provider)) {
+        providerList.add(Pair.of("provider" + (providerSet.size() - 1),
+            provider));
+      }
+    }
+
+    buff.append("  private final java.util.List relClasses;\n");
+    for (Pair<String, MetadataHandler> pair : providerList) {
+      buff.append("  public final ").append(pair.right.getClass().getName())
+          .append(' ').append(pair.left).append(";\n");
+    }
+    buff.append("  public ").append(name).append("(java.util.List relClasses");
+    for (Pair<String, MetadataHandler> pair : providerList) {
+      buff.append(",\n")
+          .append("      ")
+          .append(pair.right.getClass().getName())
+          .append(' ')
+          .append(pair.left);
+    }
+    buff.append(") {\n")
+        .append("    this.relClasses = relClasses;\n");
+
+    for (Pair<String, MetadataHandler> pair : providerList) {
+      buff.append("    this.").append(pair.left).append(" = ").append(pair.left)
+          .append(";\n");
+    }
+    buff.append("  }\n")
+        .append("  public org.apache.calcite.rel.metadata.MetadataDef getDef() {\n")
+        .append("    return ")
+        .append(def.metadataClass.getName())
+        .append(".DEF;\n")
+        .append("  }\n");
+    for (Ord<Method> method : Ord.zip(def.methods)) {
+      buff.append("  public ")
+          .append(method.e.getReturnType().getName())
+          .append(" ")
+          .append(method.e.getName())
+          .append("(\n")
+          .append("      org.apache.calcite.rel.RelNode r,\n")
+          .append("      org.apache.calcite.rel.metadata.RelMetadataQuery mq");
+      paramList(buff, method.e)
+          .append(") {\n");
+      buff.append("    final java.util.List key = ")
+          .append(
+              (method.e.getParameterTypes().length < 4
+              ? org.apache.calcite.runtime.FlatLists.class
+              : ImmutableList.class).getName())
+          .append(".of(")
+          .append(def.metadataClass.getName());
+      if (method.i == 0) {
+        buff.append(".DEF");
+      } else {
+        buff.append(".DEF.methods.get(")
+            .append(method.i)
+            .append(")");
+      }
+      buff.append(", r");
+      safeArgList(buff, method.e)
+          .append(");\n")
+          .append("    final Object v = mq.map.get(key);\n")
+          .append("    if (v != null) {\n")
+          .append("      if (v == ")
+          .append(NullSentinel.class.getName())
+          .append(".ACTIVE) {\n")
+          .append("        throw ")
+          .append(CyclicMetadataException.class.getName())
+          .append(".INSTANCE;\n")
+          .append("      }\n")
+          .append("      return (")
+          .append(method.e.getReturnType().getName())
+          .append(") v;\n")
+          .append("    }\n")
+          .append("    mq.map.put(key,")
+          .append(NullSentinel.class.getName())
+          .append(".ACTIVE);\n")
+          .append("    try {\n")
+          .append("      final ")
+          .append(method.e.getReturnType().getName())
+          .append(" x = ")
+          .append(method.e.getName())
+          .append("_(r, mq");
+      argList(buff, method.e)
+          .append(");\n")
+          .append("      mq.map.put(key, x);\n")
+          .append("      return x;\n")
+          .append("    } catch (")
+          .append(NoHandler.class.getName())
+          .append(" e) {\n")
+          .append("      mq.map.remove(key);\n")
+          .append("      throw e;\n")
+          .append("    }\n")
+          .append("  }\n")
+          .append("\n")
+          .append("  private ")
+          .append(method.e.getReturnType().getName())
+          .append(" ")
+          .append(method.e.getName())
+          .append("_(\n")
+          .append("      org.apache.calcite.rel.RelNode r,\n")
+          .append("      org.apache.calcite.rel.metadata.RelMetadataQuery mq");
+      paramList(buff, method.e)
+          .append(") {\n");
+      buff.append("    switch (relClasses.indexOf(r.getClass())) {\n");
+
+      // Build a list of clauses, grouping clauses that have the same action.
+      final Multimap<String, Integer> clauses = LinkedHashMultimap.create();
+      final StringBuilder buf2 = new StringBuilder();
+      for (Ord<Class<? extends RelNode>> relClass : Ord.zip(relClasses)) {
+        if (relClass.e == HepRelVertex.class) {
+          buf2.append("      return ")
+              .append(method.e.getName())
+              .append("(((")
+              .append(relClass.e.getName())
+              .append(") r).getCurrentRel(), mq");
+          argList(buf2, method.e)
+              .append(");\n");
+        } else {
+          final Method handler = space.find(relClass.e, method.e);
+          final String v = findProvider(providerList, handler.getDeclaringClass());
+          buf2.append("      return ")
+              .append(v)
+              .append(".")
+              .append(method.e.getName())
+              .append("((")
+              .append(handler.getParameterTypes()[0].getName())
+              .append(") r, mq");
+          argList(buf2, method.e)
+              .append(");\n");
+        }
+        clauses.put(buf2.toString(), relClass.i);
+        buf2.setLength(0);
+      }
+      buf2.append("      throw new ")
+          .append(NoHandler.class.getName())
+          .append("(r.getClass());\n")
+          .append("    }\n")
+          .append("  }\n");
+      clauses.put(buf2.toString(), -1);
+      for (Map.Entry<String, Collection<Integer>> pair : clauses.asMap().entrySet()) {
+        if (pair.getValue().contains(relClasses.indexOf(RelNode.class))) {
+          buff.append("    default:\n");
+        } else {
+          for (Integer integer : pair.getValue()) {
+            buff.append("    case ").append(integer).append(":\n");
+          }
+        }
+        buff.append(pair.getKey());
+      }
+    }
+    ClassDeclaration decl = new ClassDeclaration(0, name, Object.class,
+        ImmutableList.<Type>of(), ImmutableList.<MemberDeclaration>of());
+    final List<Object> argList = new ArrayList<Object>(Pair.right(providerList));
+    argList.add(0, ImmutableList.copyOf(relClasses));
+    try {
+      return compile(decl, buff.toString(), def, argList);
+    } catch (CompileException | IOException e) {
+      System.out.println(buff);
+      throw Throwables.propagate(e);
+    }
+  }
+
+  private static String
+  findProvider(List<Pair<String, MetadataHandler>> providerList,
+      Class<?> declaringClass) {
+    for (Pair<String, MetadataHandler> pair : providerList) {
+      if (declaringClass.isInstance(pair.right)) {
+        return pair.left;
+      }
+    }
+    throw new AssertionError("not found: " + declaringClass);
+  }
+
+  /** Returns e.g. ", ignoreNulls". */
+  private static StringBuilder argList(StringBuilder buff, Method method) {
+    for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) {
+      buff.append(", a").append(t.i);
+    }
+    return buff;
+  }
+
+  /** Returns e.g. ", ignoreNulls". */
+  private static StringBuilder safeArgList(StringBuilder buff, Method method) {
+    for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) {
+      if (Primitive.is(t.e)) {
+        buff.append(", a").append(t.i);
+      } else if (RexNode.class.isAssignableFrom(t.e)) {
+        // For RexNode, convert to string, because equals does not look deep.
+        //   a1 == null ? "" : a1.toString()
+        buff.append(", a").append(t.i).append(" == null ? \"\" : a")
+            .append(t.i).append(".toString()");
+      } else {
+        buff.append(", ") .append(NullSentinel.class.getName())
+            .append(".mask(a").append(t.i).append(")");
+      }
+    }
+    return buff;
+  }
+
+  /** Returns e.g. ",\n boolean ignoreNulls". */
+  private static StringBuilder paramList(StringBuilder buff, Method method) {
+    for (Ord<Class<?>> t : Ord.zip(method.getParameterTypes())) {
+      buff.append(",\n      ").append(t.e.getName()).append(" a").append(t.i);
+    }
+    return buff;
+  }
+
+  static <M extends Metadata> MetadataHandler<M>
+  compile(ClassDeclaration expr, String s, MetadataDef<M> def,
+      List<Object> argList) throws CompileException, IOException {
+    final ICompilerFactory compilerFactory;
+    try {
+      compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory();
+    } catch (Exception e) {
+      throw new IllegalStateException(
+          "Unable to instantiate java compiler", e);
+    }
+    final IClassBodyEvaluator cbe = compilerFactory.newClassBodyEvaluator();
+    cbe.setClassName(expr.name);
+    cbe.setImplementedInterfaces(new Class[]{def.handlerClass});
+    cbe.setParentClassLoader(JaninoRexCompiler.class.getClassLoader());
+    if (CalcitePrepareImpl.DEBUG) {
+      // Add line numbers to the generated janino class
+      cbe.setDebuggingInformation(true, true, true);
+      System.out.println(s);
+    }
+    cbe.cook(new StringReader(s));
+    final Constructor constructor = cbe.getClazz().getDeclaredConstructors()[0];
+    final Object o;
+    try {
+      o = constructor.newInstance(argList.toArray());
+    } catch (InstantiationException
+        | IllegalAccessException
+        | InvocationTargetException e) {
+      throw Throwables.propagate(e);
+    }
+    return def.handlerClass.cast(o);
+  }
+
+  synchronized <M extends Metadata, H extends MetadataHandler<M>> H
+  create(MetadataDef<M> def) {
+    try {
+      final Key key = new Key((MetadataDef) def, provider,
+          ImmutableList.copyOf(ALL_RELS));
+      //noinspection unchecked
+      return (H) HANDLERS.get(key);
+    } catch (ExecutionException e) {
+      throw Throwables.propagate(e);
+    }
+  }
+
+  synchronized <M extends Metadata, H extends MetadataHandler<M>> H
+  revise(Class<? extends RelNode> rClass, MetadataDef<M> def) {
+    if (ALL_RELS.add(rClass)) {
+      HANDLERS.invalidateAll();
+    }
+    //noinspection unchecked
+    return (H) create(def);
+  }
+
+  /** Registers some classes. Does not flush the providers, but next time we
+   * need to generate a provider, it will handle all of these classes. So,
+   * calling this method reduces the number of times we need to re-generate. */
+  public void register(Iterable<Class<? extends RelNode>> classes) {
+    // Register the classes and their base classes up to RelNode. Don't bother
+    // to remove duplicates; addAll will do that.
+    final List<Class<? extends RelNode>> list = Lists.newArrayList(classes);
+    for (int i = 0; i < list.size(); i++) {
+      final Class<? extends RelNode> c = list.get(i);
+      final Class s = c.getSuperclass();
+      if (s != null && RelNode.class.isAssignableFrom(s)) {
+        //noinspection unchecked
+        list.add(s);
+      }
+    }
+    if (ALL_RELS.addAll(list)) {
+      HANDLERS.invalidateAll();
+    }
+  }
+
+  /** Exception that indicates there there should be a handler for
+   * this class but there is not. The action is probably to
+   * re-generate the handler class. */
+  public static class NoHandler extends ControlFlowException {
+    public final Class<? extends RelNode> relClass;
+
+    public NoHandler(Class<? extends RelNode> relClass) {
+      this.relClass = relClass;
+    }
+  }
+
+  /** Key for the cache. */
+  private static class Key {
+    public final MetadataDef def;
+    public final RelMetadataProvider provider;
+    public final ImmutableList<Class<? extends RelNode>> relClasses;
+
+    private Key(MetadataDef def, RelMetadataProvider provider,
+        ImmutableList<Class<? extends RelNode>> relClassList) {
+      this.def = def;
+      this.provider = provider;
+      this.relClasses = relClassList;
+    }
+
+    @Override public int hashCode() {
+      return (def.hashCode() * 37
+          + provider.hashCode()) * 37
+          + relClasses.hashCode();
+    }
+
+    @Override public boolean equals(Object obj) {
+      return this == obj
+          || obj instanceof Key
+          && ((Key) obj).def.equals(def)
+          && ((Key) obj).provider.equals(provider)
+          && ((Key) obj).relClasses.equals(relClasses);
+    }
+  }
+}
+
+// End JaninoRelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java
new file mode 100644
index 0000000..e7924e7
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataDef.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Definition of metadata.
+ *
+ * @param <M> Kind of metadata
+ */
+public class MetadataDef<M extends Metadata> {
+  public final Class<M> metadataClass;
+  public final Class<? extends MetadataHandler<M>> handlerClass;
+  public final ImmutableList<Method> methods;
+
+  private MetadataDef(Class<M> metadataClass,
+      Class<? extends MetadataHandler<M>> handlerClass, Method... methods) {
+    this.metadataClass = metadataClass;
+    this.handlerClass = handlerClass;
+    this.methods = ImmutableList.copyOf(methods);
+    final Method[] handlerMethods = handlerClass.getDeclaredMethods();
+
+    // Handler must have the same methods as Metadata, each method having
+    // additional "subclass-of-RelNode, RelMetadataQuery" parameters.
+    assert handlerMethods.length == methods.length;
+    for (Pair<Method, Method> pair : Pair.zip(methods, handlerMethods)) {
+      final List<Class<?>> leftTypes =
+          Arrays.asList(pair.left.getParameterTypes());
+      final List<Class<?>> rightTypes =
+          Arrays.asList(pair.right.getParameterTypes());
+      assert leftTypes.size() + 2 == rightTypes.size();
+      assert RelNode.class.isAssignableFrom(rightTypes.get(0));
+      assert RelMetadataQuery.class == rightTypes.get(1);
+      assert leftTypes.equals(rightTypes.subList(2, rightTypes.size()));
+    }
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.metadata.MetadataDef}. */
+  public static <M extends Metadata> MetadataDef<M> of(Class<M> metadataClass,
+      Class<? extends MetadataHandler<M>> handlerClass, Method... methods) {
+    return new MetadataDef<>(metadataClass, handlerClass, methods);
+  }
+}
+
+// End MetadataDef.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java
new file mode 100644
index 0000000..95f8d62
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/MetadataHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+/**
+ * Marker interface for a handler of metadata.
+ *
+ * @param <M> Kind of metadata
+ */
+public interface MetadataHandler<M extends Metadata> {
+  MetadataDef<M> getDef();
+}
+
+// End MetadataHandler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java
index 3f5dcf6..4215211 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/NullSentinel.java
@@ -17,18 +17,22 @@
 package org.apache.calcite.rel.metadata;
 
 /** Placeholder for null values. */
-// package-private
-enum NullSentinel {
-  INSTANCE;
+public enum NullSentinel {
+  /** Placeholder for a null value. */
+  INSTANCE,
 
-  static Comparable mask(Comparable value) {
+  /** Placeholder that means that a request for metadata is already active,
+   * therefore this request forms a cycle. */
+  ACTIVE;
+
+  public static Comparable mask(Comparable value) {
     if (value == null) {
       return INSTANCE;
     }
     return value;
   }
 
-  static Object mask(Object value) {
+  public static Object mask(Object value) {
     if (value == null) {
       return INSTANCE;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/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 8cdb55a..5f6d680 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
@@ -27,9 +27,7 @@ import org.apache.calcite.util.Util;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMap;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
@@ -39,6 +37,8 @@ import java.lang.reflect.Proxy;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -63,6 +63,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;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -71,13 +72,16 @@ public class ReflectiveRelMetadataProvider
    *
    * @param map Map
    * @param metadataClass0 Metadata class
+   * @param handlerMap Methods handled and the objects to call them on
    */
   protected ReflectiveRelMetadataProvider(
       ConcurrentMap<Class<RelNode>, UnboundMetadata> map,
-      Class<? extends Metadata> metadataClass0) {
+      Class<? extends Metadata> metadataClass0,
+      Map<Method, MetadataHandler> handlerMap) {
     assert !map.isEmpty() : "are your methods named wrong?";
     this.map = map;
     this.metadataClass0 = metadataClass0;
+    this.handlerMap = ImmutableMap.copyOf(handlerMap);
   }
 
   /** Returns an implementation of {@link RelMetadataProvider} that scans for
@@ -98,54 +102,32 @@ public class ReflectiveRelMetadataProvider
    * or {@link org.apache.calcite.rel.core.Filter}.</p>
    */
   public static RelMetadataProvider reflectiveSource(Method method,
-      Object target) {
+      MetadataHandler target) {
     return reflectiveSource(target, ImmutableList.of(method));
   }
 
   /** Returns a reflective metadata provider that implements several
    * methods. */
-  public static RelMetadataProvider reflectiveSource(Object target,
+  public static RelMetadataProvider reflectiveSource(MetadataHandler target,
       Method... methods) {
     return reflectiveSource(target, ImmutableList.copyOf(methods));
   }
 
-  private static RelMetadataProvider reflectiveSource(final Object target,
+  private static RelMetadataProvider
+  reflectiveSource(final MetadataHandler target,
       final ImmutableList<Method> methods) {
-    assert methods.size() > 0;
-    final Method method0 = methods.get(0);
-    @SuppressWarnings("unchecked")
-    final Class<Metadata> metadataClass0 = (Class) method0.getDeclaringClass();
-    assert Metadata.class.isAssignableFrom(metadataClass0);
-    for (Method method : methods) {
-      assert method.getDeclaringClass() == metadataClass0;
-    }
-
-    // Find the distinct set of RelNode classes handled by this provider,
-    // ordered base-class first.
-    final Set<Class<RelNode>> classes = Sets.newHashSet();
-    final Map<Pair<Class<RelNode>, Method>, Method> handlerMap =
-        Maps.newHashMap();
-    for (final Method handlerMethod : target.getClass().getMethods()) {
-      for (Method method : methods) {
-        if (couldImplement(handlerMethod, method)) {
-          @SuppressWarnings("unchecked") final Class<RelNode> relNodeClass =
-              (Class<RelNode>) handlerMethod.getParameterTypes()[0];
-          classes.add(relNodeClass);
-          handlerMap.put(Pair.of(relNodeClass, method), handlerMethod);
-        }
-      }
-    }
+    final Space2 space = Space2.create(target, methods);
 
-    // This needs to be a councurrent map since RelMetadataProvider are cached in static
+    // This needs to be a concurrent map since RelMetadataProvider are cached in static
     // fields, thus the map is subject to concurrent modifications later.
     // See map.put in org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider.apply(
     // java.lang.Class<? extends org.apache.calcite.rel.RelNode>)
     final ConcurrentMap<Class<RelNode>, UnboundMetadata> methodsMap = new ConcurrentHashMap<>();
-    for (Class<RelNode> key : classes) {
+    for (Class<RelNode> key : space.classes) {
       ImmutableNullableList.Builder<Method> builder =
           ImmutableNullableList.builder();
       for (final Method method : methods) {
-        builder.add(find(handlerMap, key, method));
+        builder.add(space.find(key, method));
       }
       final List<Method> handlerMethods = builder.build();
       final UnboundMetadata function =
@@ -153,8 +135,8 @@ public class ReflectiveRelMetadataProvider
             public Metadata bind(final RelNode rel,
                 final RelMetadataQuery mq) {
               return (Metadata) Proxy.newProxyInstance(
-                  metadataClass0.getClassLoader(),
-                  new Class[]{metadataClass0},
+                  space.metadataClass0.getClassLoader(),
+                  new Class[]{space.metadataClass0},
                   new InvocationHandler() {
                     public Object invoke(Object proxy, Method method,
                         Object[] args) throws Throwable {
@@ -170,7 +152,7 @@ public class ReflectiveRelMetadataProvider
                       }
                       if (method.equals(
                           BuiltInMethod.OBJECT_TO_STRING.method)) {
-                        return metadataClass0.getSimpleName() + "(" + rel
+                        return space.metadataClass0.getSimpleName() + "(" + rel
                             + ")";
                       }
                       int i = methods.indexOf(method);
@@ -206,7 +188,7 @@ public class ReflectiveRelMetadataProvider
                         }
                         key = FlatLists.copyOf(args2);
                       }
-                      if (!mq.set.add(key)) {
+                      if (mq.map.put(key, NullSentinel.INSTANCE) != null) {
                         throw CyclicMetadataException.INSTANCE;
                       }
                       try {
@@ -216,7 +198,7 @@ public class ReflectiveRelMetadataProvider
                         Throwables.propagateIfPossible(e.getCause());
                         throw e;
                       } finally {
-                        mq.set.remove(key);
+                        mq.map.remove(key);
                       }
                     }
                   });
@@ -224,39 +206,21 @@ public class ReflectiveRelMetadataProvider
           };
       methodsMap.put(key, function);
     }
-    return new ReflectiveRelMetadataProvider(methodsMap, metadataClass0);
+    return new ReflectiveRelMetadataProvider(methodsMap, space.metadataClass0,
+        space.providerMap);
   }
 
-  /** Finds an implementation of a method for {@code relNodeClass} or its
-   * nearest base class. Assumes that base classes have already been added to
-   * {@code map}. */
-  @SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" })
-  private static Method find(Map<Pair<Class<RelNode>, Method>,
-      Method> handlerMap, Class<RelNode> relNodeClass, Method method) {
-    List<Class<RelNode>> newSources = Lists.newArrayList();
-    Method implementingMethod;
-    while (relNodeClass != null) {
-      implementingMethod = handlerMap.get(Pair.of(relNodeClass, method));
-      if (implementingMethod != null) {
-        return implementingMethod;
-      } else {
-        newSources.add(relNodeClass);
-      }
-      for (Class<?> clazz : relNodeClass.getInterfaces()) {
-        if (RelNode.class.isAssignableFrom(clazz)) {
-          implementingMethod = handlerMap.get(Pair.of(clazz, method));
-          if (implementingMethod != null) {
-            return implementingMethod;
-          }
-        }
-      }
-      if (RelNode.class.isAssignableFrom(relNodeClass.getSuperclass())) {
-        relNodeClass = (Class<RelNode>) relNodeClass.getSuperclass();
-      } else {
-        relNodeClass = null;
+  public <M extends Metadata> Map<Method, MetadataHandler<M>>
+  handlers(MetadataDef<M> def) {
+    final ImmutableMap.Builder<Method, MetadataHandler<M>> builder =
+        ImmutableMap.builder();
+    for (Map.Entry<Method, MetadataHandler> entry : handlerMap.entrySet()) {
+      if (def.methods.contains(entry.getKey())) {
+        //noinspection unchecked
+        builder.put(entry.getKey(), entry.getValue());
       }
     }
-    return null;
+    return builder.build();
   }
 
   private static boolean couldImplement(Method handlerMethod, Method method) {
@@ -318,6 +282,91 @@ public class ReflectiveRelMetadataProvider
       }
     }
   }
+
+  /** Workspace for computing which methods can act as handlers for
+   * given metadata methods. */
+  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;
+
+    Space(Map<Method, MetadataHandler> providerMap) {
+      this.providerMap = ImmutableMap.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()) {
+        final Method method = entry.getKey();
+        final MetadataHandler provider = entry.getValue();
+        for (final Method handlerMethod : provider.getClass().getMethods()) {
+          if (couldImplement(handlerMethod, method)) {
+            @SuppressWarnings("unchecked") final Class<RelNode> relNodeClass =
+                (Class<RelNode>) handlerMethod.getParameterTypes()[0];
+            classes.add(relNodeClass);
+            handlerMap.put(Pair.of(relNodeClass, method), handlerMethod);
+          }
+        }
+      }
+    }
+
+    /** Finds an implementation of a method for {@code relNodeClass} or its
+     * 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));
+        if (implementingMethod != null) {
+          return implementingMethod;
+        }
+        for (Class<?> clazz : relNodeClass.getInterfaces()) {
+          if (RelNode.class.isAssignableFrom(clazz)) {
+            implementingMethod = handlerMap.get(Pair.of(clazz, method));
+            if (implementingMethod != null) {
+              return implementingMethod;
+            }
+          }
+        }
+        if (RelNode.class.isAssignableFrom(relNodeClass.getSuperclass())) {
+          relNodeClass = (Class<RelNode>) relNodeClass.getSuperclass();
+        } else {
+          relNodeClass = null;
+        }
+      }
+      return null;
+    }
+  }
+
+  /** Extended work space. */
+  static class Space2 extends Space {
+    private Class<Metadata> metadataClass0;
+
+    public Space2(Class<Metadata> metadataClass0,
+        ImmutableMap<Method, MetadataHandler> providerMap) {
+      super(providerMap);
+      this.metadataClass0 = metadataClass0;
+    }
+
+    public static Space2 create(MetadataHandler target,
+        ImmutableList<Method> methods) {
+      assert methods.size() > 0;
+      final Method method0 = methods.get(0);
+      //noinspection unchecked
+      Class<Metadata> metadataClass0 = (Class) method0.getDeclaringClass();
+      assert Metadata.class.isAssignableFrom(metadataClass0);
+      for (Method method : methods) {
+        assert method.getDeclaringClass() == metadataClass0;
+      }
+
+      final ImmutableMap.Builder<Method, MetadataHandler> providerBuilder =
+          ImmutableMap.builder();
+      for (final Method method : methods) {
+        providerBuilder.put(method, target);
+      }
+      return new Space2(metadataClass0, providerBuilder.build());
+    }
+  }
 }
 
 // End ReflectiveRelMetadataProvider.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
index 92b325d..2867657 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdCollation.java
@@ -67,7 +67,8 @@ import java.util.TreeSet;
  * {@link org.apache.calcite.rel.metadata.RelMetadataQuery#collations}
  * for the standard logical algebra.
  */
-public class RelMdCollation {
+public class RelMdCollation
+    implements MetadataHandler<BuiltInMetadata.Collation> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.COLLATIONS.method, new RelMdCollation());
@@ -78,6 +79,10 @@ public class RelMdCollation {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Collation> getDef() {
+    return BuiltInMetadata.Collation.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Collation#collations()},
    * invoked using reflection, for any relational expression not

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
index 3038260..9944b17 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
@@ -42,7 +42,8 @@ import java.util.Set;
  * RelMdColumnOrigins supplies a default implementation of
  * {@link RelMetadataQuery#getColumnOrigins} for the standard logical algebra.
  */
-public class RelMdColumnOrigins {
+public class RelMdColumnOrigins
+    implements MetadataHandler<BuiltInMetadata.ColumnOrigin> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.COLUMN_ORIGIN.method, new RelMdColumnOrigins());
@@ -53,6 +54,10 @@ public class RelMdColumnOrigins {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.ColumnOrigin> getDef() {
+    return BuiltInMetadata.ColumnOrigin.DEF;
+  }
+
   public Set<RelColumnOrigin> getColumnOrigins(Aggregate rel,
       RelMetadataQuery mq, int iOutputColumn) {
     if (iOutputColumn < rel.getGroupCount()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
index c872075..60fefb5 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnUniqueness.java
@@ -55,7 +55,8 @@ import java.util.Set;
  * RelMdColumnUniqueness supplies a default implementation of
  * {@link RelMetadataQuery#areColumnsUnique} for the standard logical algebra.
  */
-public class RelMdColumnUniqueness {
+public class RelMdColumnUniqueness
+    implements MetadataHandler<BuiltInMetadata.ColumnUniqueness> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.COLUMN_UNIQUENESS.method, new RelMdColumnUniqueness());
@@ -66,6 +67,10 @@ public class RelMdColumnUniqueness {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.ColumnUniqueness> getDef() {
+    return BuiltInMetadata.ColumnUniqueness.DEF;
+  }
+
   public Boolean areColumnsUnique(TableScan rel, RelMetadataQuery mq,
       ImmutableBitSet columns, boolean ignoreNulls) {
     return rel.getTable().isKey(columns);

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
index eef245e..bf3fc3a 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistinctRowCount.java
@@ -45,7 +45,8 @@ import java.util.List;
  * {@link RelMetadataQuery#getDistinctRowCount} for the standard logical
  * algebra.
  */
-public class RelMdDistinctRowCount {
+public class RelMdDistinctRowCount
+    implements MetadataHandler<BuiltInMetadata.DistinctRowCount> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.DISTINCT_ROW_COUNT.method, new RelMdDistinctRowCount());
@@ -56,6 +57,10 @@ public class RelMdDistinctRowCount {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.DistinctRowCount> getDef() {
+    return BuiltInMetadata.DistinctRowCount.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.DistinctRowCount#getDistinctRowCount(ImmutableBitSet, RexNode)},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
index 80f4e37..b594e4d 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdDistribution.java
@@ -46,7 +46,8 @@ import java.util.List;
  * {@link RelMetadataQuery#distribution}
  * for the standard logical algebra.
  */
-public class RelMdDistribution {
+public class RelMdDistribution
+    implements MetadataHandler<BuiltInMetadata.Distribution> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.DISTRIBUTION.method, new RelMdDistribution());
@@ -57,6 +58,10 @@ public class RelMdDistribution {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Distribution> getDef() {
+    return BuiltInMetadata.Distribution.DEF;
+  }
+
   /** Fallback method to deduce distribution for any relational expression not
    * handled by a more specific method.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
index bf51f2f..473b30a 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExplainVisibility.java
@@ -24,7 +24,8 @@ import org.apache.calcite.util.BuiltInMethod;
  * RelMdExplainVisibility supplies a default implementation of
  * {@link RelMetadataQuery#isVisibleInExplain} for the standard logical algebra.
  */
-public class RelMdExplainVisibility {
+public class RelMdExplainVisibility
+    implements MetadataHandler<BuiltInMetadata.ExplainVisibility> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.EXPLAIN_VISIBILITY.method,
@@ -36,6 +37,10 @@ public class RelMdExplainVisibility {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.ExplainVisibility> getDef() {
+    return BuiltInMetadata.ExplainVisibility.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.ExplainVisibility#isVisibleInExplain(SqlExplainLevel)},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
index ab9739f..dd811a7 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMaxRowCount.java
@@ -38,13 +38,18 @@ import org.apache.calcite.util.Util;
  * RelMdMaxRowCount supplies a default implementation of
  * {@link RelMetadataQuery#getMaxRowCount} for the standard logical algebra.
  */
-public class RelMdMaxRowCount {
+public class RelMdMaxRowCount
+    implements MetadataHandler<BuiltInMetadata.MaxRowCount> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.MAX_ROW_COUNT.method, new RelMdMaxRowCount());
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.MaxRowCount> getDef() {
+    return BuiltInMetadata.MaxRowCount.DEF;
+  }
+
   public Double getMaxRowCount(Union rel, RelMetadataQuery mq) {
     double rowCount = 0.0;
     for (RelNode input : rel.getInputs()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
index c760b0e..359af35 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdMemory.java
@@ -27,7 +27,7 @@ import org.apache.calcite.util.BuiltInMethod;
  * @see RelMetadataQuery#isPhaseTransition
  * @see RelMetadataQuery#splitCount
  */
-public class RelMdMemory {
+public class RelMdMemory implements MetadataHandler<BuiltInMetadata.Memory> {
   /** Source for
    * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Memory}. */
   public static final RelMetadataProvider SOURCE =
@@ -42,6 +42,10 @@ public class RelMdMemory {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Memory> getDef() {
+    return BuiltInMetadata.Memory.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Memory#memory()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
index dcd69f1..4d144aa 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdParallelism.java
@@ -30,7 +30,8 @@ import org.apache.calcite.util.BuiltInMethod;
  * @see org.apache.calcite.rel.metadata.RelMetadataQuery#isPhaseTransition
  * @see org.apache.calcite.rel.metadata.RelMetadataQuery#splitCount
  */
-public class RelMdParallelism {
+public class RelMdParallelism
+    implements MetadataHandler<BuiltInMetadata.Parallelism> {
   /** Source for
    * {@link org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism}. */
   public static final RelMetadataProvider SOURCE =
@@ -44,6 +45,10 @@ public class RelMdParallelism {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.Parallelism> getDef() {
+    return BuiltInMetadata.Parallelism.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Parallelism#isPhaseTransition()},
    * invoked using reflection.

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
index bde07a9..e50c8d0 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPercentageOriginalRows.java
@@ -33,7 +33,8 @@ import java.util.List;
  * {@link RelMetadataQuery#getPercentageOriginalRows} for the standard logical
  * algebra.
  */
-public class RelMdPercentageOriginalRows {
+public class RelMdPercentageOriginalRows
+    implements MetadataHandler<BuiltInMetadata.PercentageOriginalRows> {
   private static final RelMdPercentageOriginalRows INSTANCE =
       new RelMdPercentageOriginalRows();
 
@@ -53,6 +54,10 @@ public class RelMdPercentageOriginalRows {
 
   private RelMdPercentageOriginalRows() {}
 
+  public MetadataDef<BuiltInMetadata.PercentageOriginalRows> getDef() {
+    return BuiltInMetadata.PercentageOriginalRows.DEF;
+  }
+
   public Double getPercentageOriginalRows(Aggregate rel, RelMetadataQuery mq) {
     // REVIEW jvs 28-Mar-2006: The assumption here seems to be that
     // aggregation does not apply any filtering, so it does not modify the

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
index 180883f..67b3fe9 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPopulationSize.java
@@ -36,7 +36,8 @@ import java.util.List;
  * RelMdPopulationSize supplies a default implementation of
  * {@link RelMetadataQuery#getPopulationSize} for the standard logical algebra.
  */
-public class RelMdPopulationSize {
+public class RelMdPopulationSize
+    implements MetadataHandler<BuiltInMetadata.PopulationSize> {
   public static final RelMetadataProvider SOURCE =
       ReflectiveRelMetadataProvider.reflectiveSource(
           BuiltInMethod.POPULATION_SIZE.method, new RelMdPopulationSize());
@@ -47,6 +48,10 @@ public class RelMdPopulationSize {
 
   //~ Methods ----------------------------------------------------------------
 
+  public MetadataDef<BuiltInMetadata.PopulationSize> getDef() {
+    return BuiltInMetadata.PopulationSize.DEF;
+  }
+
   public Double getPopulationSize(Filter rel, RelMetadataQuery mq,
       ImmutableBitSet groupKey) {
     return mq.getPopulationSize(rel.getInput(), groupKey);

http://git-wip-us.apache.org/repos/asf/calcite/blob/d14040c5/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index 4b1a894..7300624 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -115,12 +115,17 @@ import java.util.SortedMap;
  *   </ul>
  * </ol>
  */
-public class RelMdPredicates {
+public class RelMdPredicates
+    implements MetadataHandler<BuiltInMetadata.Predicates> {
   public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider
       .reflectiveSource(BuiltInMethod.PREDICATES.method, new RelMdPredicates());
 
   private static final List<RexNode> EMPTY_LIST = ImmutableList.of();
 
+  public MetadataDef<BuiltInMetadata.Predicates> getDef() {
+    return BuiltInMetadata.Predicates.DEF;
+  }
+
   /** Catch-all implementation for
    * {@link BuiltInMetadata.Predicates#getPredicates()},
    * invoked using reflection.