You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/05/31 23:23:59 UTC

[01/14] calcite git commit: [CALCITE-1248] Prevent an additional run of the tests [Forced Update!]

Repository: calcite
Updated Branches:
  refs/heads/branch-avatica-1.8 4c6d6425f -> ea25b7327 (forced update)


[CALCITE-1248] Prevent an additional run of the tests


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

Branch: refs/heads/branch-avatica-1.8
Commit: 6c633cf3db0ace763348e63ca123fbf14893c6de
Parents: 10e8c79
Author: Josh Elser <el...@apache.org>
Authored: Fri May 20 16:12:38 2016 -0400
Committer: Josh Elser <el...@apache.org>
Committed: Fri May 20 16:12:38 2016 -0400

----------------------------------------------------------------------
 avatica/site/_docs/howto.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/6c633cf3/avatica/site/_docs/howto.md
----------------------------------------------------------------------
diff --git a/avatica/site/_docs/howto.md b/avatica/site/_docs/howto.md
index 2a8c9fb..fbc7b59 100644
--- a/avatica/site/_docs/howto.md
+++ b/avatica/site/_docs/howto.md
@@ -286,7 +286,7 @@ If successful, remove the `-DdryRun` flag and run the release for real.
 mvn -DreleaseVersion=X.Y.Z -DdevelopmentVersion=X.Y.Z+1-SNAPSHOT -Dtag=calcite-avatica-X.Y.Z-rc0 -Papache-release -Duser.name=${asf.username} release:prepare
 
 # Perform checks out the tagged version, builds, and deploys to the staging repository
-mvn -Papache-release release:perform
+mvn -Papache-release -Duser.name=${asf.username} release:perform -Darguments="-DskipTests"
 {% endhighlight %}
 
 Verify the staged artifacts in the Nexus repository:


[11/14] calcite git commit: [CALCITE-1250] UNNEST applied to MAP data type (Johannes Schulte)

Posted by el...@apache.org.
[CALCITE-1250] UNNEST applied to MAP data type (Johannes Schulte)

Implicit column names are KEY and VALUE.

Make UNNEST work with multiple arguments of different types.

Close apache/calcite#235


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

Branch: refs/heads/branch-avatica-1.8
Commit: d757201f155bd0c900725b3ce2776899668fce4d
Parents: 2f937c1
Author: baunz <jo...@gmail.com>
Authored: Thu May 19 23:02:34 2016 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue May 31 00:01:23 2016 -0700

----------------------------------------------------------------------
 .../adapter/enumerable/EnumerableUncollect.java | 27 +++++--
 .../org/apache/calcite/rel/core/Uncollect.java  | 20 +++--
 .../apache/calcite/runtime/SqlFunctions.java    | 80 ++++++++++++++------
 .../apache/calcite/sql/SqlUnnestOperator.java   | 26 +++++--
 .../apache/calcite/sql/type/OperandTypes.java   |  3 +
 .../org/apache/calcite/util/BuiltInMethod.java  | 36 ++++-----
 .../java/org/apache/calcite/test/JdbcTest.java  | 45 +++++++++++
 .../apache/calcite/test/SqlValidatorTest.java   |  5 ++
 8 files changed, 184 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java
index 0dfa57f..def8e24 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java
@@ -25,6 +25,8 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Uncollect;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.runtime.SqlFunctions.FlatProductInputType;
+import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.IntList;
 
@@ -87,20 +89,33 @@ public class EnumerableUncollect extends Uncollect implements EnumerableRel {
     final Expression child_ =
         builder.append(
             "child", result.block);
+
     final List<Integer> fieldCounts = new ArrayList<>();
+    final List<FlatProductInputType> inputTypes = new ArrayList<>();
+
     for (RelDataTypeField field : child.getRowType().getFieldList()) {
       final RelDataType type = field.getType();
-      final RelDataType elementType = type.getComponentType();
-      if (elementType.isStruct()) {
-        fieldCounts.add(elementType.getFieldCount());
+      if (type instanceof MapSqlType) {
+        fieldCounts.add(2);
+        inputTypes.add(FlatProductInputType.MAP);
       } else {
-        fieldCounts.add(-1);
+        final RelDataType elementType = type.getComponentType();
+        if (elementType.isStruct()) {
+          fieldCounts.add(elementType.getFieldCount());
+          inputTypes.add(FlatProductInputType.LIST);
+        } else {
+          fieldCounts.add(-1);
+          inputTypes.add(FlatProductInputType.SCALAR);
+        }
       }
     }
+
     final Expression lambda =
         Expressions.call(BuiltInMethod.FLAT_PRODUCT.method,
             Expressions.constant(IntList.toArray(fieldCounts)),
-            Expressions.constant(withOrdinality));
+            Expressions.constant(withOrdinality),
+            Expressions.constant(
+                inputTypes.toArray(new FlatProductInputType[inputTypes.size()])));
     builder.add(
         Expressions.return_(null,
             Expressions.call(child_,
@@ -108,6 +123,8 @@ public class EnumerableUncollect extends Uncollect implements EnumerableRel {
                 lambda)));
     return implementor.result(physType, builder.toBlock());
   }
+
 }
 
+
 // End EnumerableUncollect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
index 2d46929..d391f93 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
@@ -28,6 +28,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.sql.SqlUnnestOperator;
 import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
 
 import java.util.List;
@@ -125,14 +126,19 @@ public class Uncollect extends SingleRel {
     final RelDataTypeFactory.FieldInfoBuilder builder =
         rel.getCluster().getTypeFactory().builder();
     for (RelDataTypeField field : fields) {
-      RelDataType ret = field.getType().getComponentType();
-      assert null != ret;
-      if (ret.isStruct()) {
-        builder.addAll(ret.getFieldList());
+      if (field.getType() instanceof MapSqlType) {
+        builder.add(SqlUnnestOperator.MAP_KEY_COLUMN_NAME, field.getType().getKeyType());
+        builder.add(SqlUnnestOperator.MAP_VALUE_COLUMN_NAME, field.getType().getValueType());
       } else {
-        // Element type is not a record. It may be a scalar type, say
-        // "INTEGER". Wrap it in a struct type.
-        builder.add(SqlUtil.deriveAliasFromOrdinal(field.getIndex()), ret);
+        RelDataType ret = field.getType().getComponentType();
+        assert null != ret;
+        if (ret.isStruct()) {
+          builder.addAll(ret.getFieldList());
+        } else {
+          // Element type is not a record. It may be a scalar type, say
+          // "INTEGER". Wrap it in a struct type.
+          builder.add(SqlUtil.deriveAliasFromOrdinal(field.getIndex()), ret);
+        }
       }
     }
     if (withOrdinality) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 7e2d84d..e8da557 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -29,6 +29,7 @@ import org.apache.calcite.linq4j.function.Deterministic;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.NonDeterministic;
 import org.apache.calcite.linq4j.tree.Primitive;
+import org.apache.calcite.runtime.FlatLists.ComparableList;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -42,6 +43,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.regex.Pattern;
@@ -1489,52 +1491,78 @@ public class SqlFunctions {
     }
   }
 
-  public static <E extends Comparable>
-  Function1<Object, Enumerable<FlatLists.ComparableList<E>>>
-  flatProduct(final int[] fieldCounts, final boolean withOrdinality) {
+  public static Function1<Object, Enumerable<ComparableList<Comparable>>>
+  flatProduct(final int[] fieldCounts, final boolean withOrdinality,
+      final FlatProductInputType[] inputTypes) {
     if (fieldCounts.length == 1) {
-      if (!withOrdinality) {
+      if (!withOrdinality && inputTypes[0] == FlatProductInputType.SCALAR) {
         //noinspection unchecked
         return (Function1) LIST_AS_ENUMERABLE;
       } else {
-        return new Function1<Object, Enumerable<FlatLists.ComparableList<E>>>() {
-          public Enumerable<FlatLists.ComparableList<E>> apply(Object row) {
-            return p2(new Object[] {row}, fieldCounts, true);
+        return new Function1<Object, Enumerable<ComparableList<Comparable>>>() {
+          public Enumerable<ComparableList<Comparable>> apply(Object row) {
+            return p2(new Object[] { row }, fieldCounts, withOrdinality,
+                  inputTypes);
           }
         };
       }
     }
-    return new Function1<Object, Enumerable<FlatLists.ComparableList<E>>>() {
-      public Enumerable<FlatLists.ComparableList<E>> apply(Object lists) {
-        return p2((Object[]) lists, fieldCounts, withOrdinality);
+    return new Function1<Object, Enumerable<FlatLists.ComparableList<Comparable>>>() {
+      public Enumerable<FlatLists.ComparableList<Comparable>> apply(Object lists) {
+        return p2((Object[]) lists, fieldCounts, withOrdinality,
+            inputTypes);
       }
     };
   }
 
-  private static <E extends Comparable>
-  Enumerable<FlatLists.ComparableList<E>> p2(Object[] lists, int[] fieldCounts,
-      boolean withOrdinality) {
-    final List<Enumerator<List<E>>> enumerators = new ArrayList<>();
+  private static Enumerable<FlatLists.ComparableList<Comparable>>
+  p2(Object[] lists, int[] fieldCounts, boolean withOrdinality,
+      FlatProductInputType[] inputTypes) {
+    final List<Enumerator<List<Comparable>>> enumerators = new ArrayList<>();
     int totalFieldCount = 0;
     for (int i = 0; i < lists.length; i++) {
       int fieldCount = fieldCounts[i];
-      if (fieldCount < 0) {
-        ++totalFieldCount;
-        @SuppressWarnings("unchecked")
-        List<E> list = (List<E>) lists[i];
+      FlatProductInputType inputType = inputTypes[i];
+      Object inputObject = lists[i];
+      switch (inputType) {
+      case SCALAR:
+        @SuppressWarnings("unchecked") List<Comparable> list =
+            (List<Comparable>) inputObject;
         enumerators.add(
             Linq4j.transform(
                 Linq4j.enumerator(list),
-                new Function1<E, List<E>>() {
-                  public List<E> apply(E a0) {
+                new Function1<Comparable, List<Comparable>>() {
+                  public List<Comparable> apply(Comparable a0) {
                     return FlatLists.of(a0);
                   }
                 }));
+        break;
+      case LIST:
+        @SuppressWarnings("unchecked") List<List<Comparable>> listList =
+            (List<List<Comparable>>) inputObject;
+        enumerators.add(Linq4j.enumerator(listList));
+        break;
+      case MAP:
+        @SuppressWarnings("unchecked") Map<Comparable, Comparable> map =
+            (Map<Comparable, Comparable>) inputObject;
+        Enumerator<Entry<Comparable, Comparable>> enumerator =
+            Linq4j.enumerator(map.entrySet());
+
+        Enumerator<List<Comparable>> transformed = Linq4j.transform(enumerator,
+          new Function1<Entry<Comparable, Comparable>, List<Comparable>>() {
+            public List<Comparable> apply(Entry<Comparable, Comparable> entry) {
+              return FlatLists.<Comparable>of(entry.getKey(), entry.getValue());
+            }
+          });
+        enumerators.add(transformed);
+        break;
+      default:
+        break;
+      }
+      if (fieldCount < 0) {
+        ++totalFieldCount;
       } else {
         totalFieldCount += fieldCount;
-        @SuppressWarnings("unchecked")
-        List<List<E>> list = (List<List<E>>) lists[i];
-        enumerators.add(Linq4j.enumerator(list));
       }
     }
     if (withOrdinality) {
@@ -1592,6 +1620,12 @@ public class SqlFunctions {
       return FlatLists.ofComparable(list);
     }
   }
+
+  /** Type of argument passed into {@link #flatProduct}. */
+  public enum FlatProductInputType {
+    SCALAR, LIST, MAP
+  }
+
 }
 
 // End SqlFunctions.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
index b3d0a1b..7d32fe6 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
@@ -16,15 +16,18 @@
  */
 package org.apache.calcite.sql;
 
+
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.type.ArraySqlType;
+import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.MultisetSqlType;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Util;
 
+
 /**
  * The <code>UNNEST</code> operator.
  */
@@ -36,6 +39,10 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
 
   public static final String ORDINALITY_COLUMN_NAME = "ORDINALITY";
 
+  public static final String MAP_KEY_COLUMN_NAME = "KEY";
+
+  public static final String MAP_VALUE_COLUMN_NAME = "VALUE";
+
   //~ Constructors -----------------------------------------------------------
 
   public SqlUnnestOperator(boolean withOrdinality) {
@@ -47,7 +54,7 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
         null,
         null,
         OperandTypes.repeat(SqlOperandCountRanges.from(1),
-            OperandTypes.SCALAR_OR_RECORD_COLLECTION));
+            OperandTypes.SCALAR_OR_RECORD_COLLECTION_OR_MAP));
     this.withOrdinality = withOrdinality;
   }
 
@@ -61,12 +68,18 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
       if (type.isStruct()) {
         type = type.getFieldList().get(0).getType();
       }
-      assert type instanceof ArraySqlType || type instanceof MultisetSqlType;
-      if (type.getComponentType().isStruct()) {
-        builder.addAll(type.getComponentType().getFieldList());
+      assert type instanceof ArraySqlType || type instanceof MultisetSqlType
+          || type instanceof MapSqlType;
+      if (type instanceof MapSqlType) {
+        builder.add(MAP_KEY_COLUMN_NAME, type.getKeyType());
+        builder.add(MAP_VALUE_COLUMN_NAME, type.getValueType());
       } else {
-        builder.add(SqlUtil.deriveAliasFromOrdinal(operand),
-            type.getComponentType());
+        if (type.getComponentType().isStruct()) {
+          builder.addAll(type.getComponentType().getFieldList());
+        } else {
+          builder.add(SqlUtil.deriveAliasFromOrdinal(operand),
+              type.getComponentType());
+        }
       }
     }
     if (withOrdinality) {
@@ -86,6 +99,7 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
   public boolean argumentMustBeScalar(int ordinal) {
     return false;
   }
+
 }
 
 // End SqlUnnestOperator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index 1d43ba0..f101ee9 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -529,6 +529,9 @@ public abstract class OperandTypes {
   public static final SqlSingleOperandTypeChecker SCALAR_OR_RECORD_COLLECTION =
       OperandTypes.or(COLLECTION, RECORD_COLLECTION);
 
+  public static final SqlSingleOperandTypeChecker SCALAR_OR_RECORD_COLLECTION_OR_MAP =
+      OperandTypes.or(COLLECTION_OR_MAP, RECORD_COLLECTION);
+
   public static final SqlOperandTypeChecker MULTISET_MULTISET =
       new MultisetOperandTypeChecker();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/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 53f7880..6a17843 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -43,9 +43,24 @@ import org.apache.calcite.linq4j.function.Predicate2;
 import org.apache.calcite.linq4j.tree.FunctionExpression;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.Collation;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnOrigin;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnUniqueness;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.CumulativeCost;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.DistinctRowCount;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount;
 import org.apache.calcite.rel.metadata.BuiltInMetadata.Memory;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.NonCumulativeCost;
 import org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.PercentageOriginalRows;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.PopulationSize;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.Predicates;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.RowCount;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.Selectivity;
 import org.apache.calcite.rel.metadata.BuiltInMetadata.Size;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.UniqueKeys;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.runtime.ArrayBindable;
@@ -56,6 +71,7 @@ import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.runtime.ResultSetEnumerable;
 import org.apache.calcite.runtime.SortedMultiMap;
 import org.apache.calcite.runtime.SqlFunctions;
+import org.apache.calcite.runtime.SqlFunctions.FlatProductInputType;
 import org.apache.calcite.runtime.Utilities;
 import org.apache.calcite.schema.FilterableTable;
 import org.apache.calcite.schema.ModifiableTable;
@@ -85,23 +101,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TimeZone;
-import javax.sql.DataSource;
 
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.Collation;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnOrigin;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnUniqueness;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.CumulativeCost;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.DistinctRowCount;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.NonCumulativeCost;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.PercentageOriginalRows;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.PopulationSize;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.Predicates;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.RowCount;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.Selectivity;
-import static org.apache.calcite.rel.metadata.BuiltInMetadata.UniqueKeys;
+import javax.sql.DataSource;
 
 /**
  * Built-in methods.
@@ -177,7 +178,8 @@ public enum BuiltInMethod {
   FUNCTION1_APPLY(Function1.class, "apply", Object.class),
   ARRAYS_AS_LIST(Arrays.class, "asList", Object[].class),
   ARRAY(SqlFunctions.class, "array", Object[].class),
-  FLAT_PRODUCT(SqlFunctions.class, "flatProduct", int[].class, boolean.class),
+  FLAT_PRODUCT(SqlFunctions.class, "flatProduct", int[].class, boolean.class,
+      FlatProductInputType[].class),
   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),

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 06a6854..bab74d6 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -52,6 +52,7 @@ import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.runtime.SqlFunctions;
 import org.apache.calcite.schema.ModifiableTable;
@@ -2286,6 +2287,50 @@ public class JdbcTest {
             "name=Sales; EI=150; D=10; N=Sebastian; S=7000.0; C=null; I=2; O=5");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1250">[CALCITE-1250]
+   * UNNEST applied to MAP data type</a>. */
+  @Test public void testUnnestItemsInMap() throws SQLException {
+    Connection connection = DriverManager.getConnection("jdbc:calcite:");
+    final String sql = "select * from unnest(MAP['a', 1, 'b', 2]) as um(k, v)";
+    ResultSet resultSet = connection.createStatement().executeQuery(sql);
+    final String expected = "K=a; V=1\n"
+        + "K=b; V=2\n";
+    assertThat(CalciteAssert.toString(resultSet), is(expected));
+    connection.close();
+  }
+
+  @Test public void testUnnestItemsInMapWithOrdinality() throws SQLException {
+    Connection connection = DriverManager.getConnection("jdbc:calcite:");
+    final String sql = "select *\n"
+        + "from unnest(MAP['a', 1, 'b', 2]) with ordinality as um(k, v, i)";
+    ResultSet resultSet = connection.createStatement().executeQuery(sql);
+    final String expected = "K=a; V=1; I=1\n"
+        + "K=b; V=2; I=2\n";
+    assertThat(CalciteAssert.toString(resultSet), is(expected));
+    connection.close();
+  }
+
+  @Test public void testUnnestItemsInMapWithNoAliasAndAdditionalArgument()
+      throws SQLException {
+    Connection connection = DriverManager.getConnection("jdbc:calcite:");
+    final String sql =
+        "select * from unnest(MAP['a', 1, 'b', 2], array[5, 6, 7])";
+    ResultSet resultSet = connection.createStatement().executeQuery(sql);
+
+    List<String> map = FlatLists.of("KEY=a; VALUE=1", "KEY=b; VALUE=2");
+    List<String> array = FlatLists.of(" EXPR$1=5", " EXPR$1=6", " EXPR$1=7");
+
+    final StringBuilder b = new StringBuilder();
+    for (List<String> row : Linq4j.product(FlatLists.of(map, array))) {
+      b.append(row.get(0)).append(";").append(row.get(1)).append("\n");
+    }
+    final String expected = b.toString();
+
+    assertThat(CalciteAssert.toString(resultSet), is(expected));
+    connection.close();
+  }
+
   private CalciteAssert.AssertQuery withFoodMartQuery(int id)
       throws IOException {
     final FoodmartTest.FoodMartQuerySet set =

http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 4a29961..c05dab3 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -6700,6 +6700,11 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         "Column 'ORDINALITY' not found in any table");
   }
 
+  @Test public void unnestMapMustNameColumnsKeyAndValueWhenNotAliased() {
+    checkResultType("select * from unnest(map[1, 12, 2, 22])",
+        "RecordType(INTEGER NOT NULL KEY, INTEGER NOT NULL VALUE) NOT NULL");
+  }
+
   @Test public void testCorrelationJoin() {
     check("select *,"
         + "         multiset(select * from emp where deptno=dept.deptno) "


[09/14] calcite git commit: [CALCITE-1253] Elasticsearch adapter (Subhobrata Dey)

Posted by el...@apache.org.
[CALCITE-1253] Elasticsearch adapter (Subhobrata Dey)

Close apache/calcite#236


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

Branch: refs/heads/branch-avatica-1.8
Commit: f3caf13b9f1cd92f95dcf27716466bf2133e1ed7
Parents: b76affc
Author: Subhobrata Dey <sb...@gmail.com>
Authored: Sat May 21 15:33:32 2016 -0400
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu May 26 14:57:06 2016 -0700

----------------------------------------------------------------------
 elasticsearch/pom.xml                           | 142 ++++++++++
 .../elasticsearch/ElasticsearchEnumerator.java  | 151 ++++++++++
 .../elasticsearch/ElasticsearchFilter.java      | 284 +++++++++++++++++++
 .../elasticsearch/ElasticsearchMethod.java      |  50 ++++
 .../elasticsearch/ElasticsearchProject.java     |  95 +++++++
 .../adapter/elasticsearch/ElasticsearchRel.java |  58 ++++
 .../elasticsearch/ElasticsearchRules.java       | 240 ++++++++++++++++
 .../elasticsearch/ElasticsearchSchema.java      | 125 ++++++++
 .../ElasticsearchSchemaFactory.java             |  63 ++++
 .../elasticsearch/ElasticsearchSort.java        |  93 ++++++
 .../elasticsearch/ElasticsearchTable.java       | 150 ++++++++++
 .../elasticsearch/ElasticsearchTableScan.java   |  88 ++++++
 .../ElasticsearchToEnumerableConverter.java     | 124 ++++++++
 .../ElasticsearchToEnumerableConverterRule.java |  42 +++
 .../adapter/elasticsearch/package-info.java     |  26 ++
 .../calcite/test/ElasticsearchAdapterIT.java    | 270 ++++++++++++++++++
 .../resources/elasticsearch-zips-model.json     |  50 ++++
 .../src/test/resources/log4j.properties         |  24 ++
 pom.xml                                         |   2 +
 site/_docs/adapter.md                           |   1 +
 site/_docs/elasticsearch_adapter.md             | 136 +++++++++
 sqlline                                         |   2 +-
 sqlline.bat                                     |   2 +-
 23 files changed, 2216 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml
new file mode 100644
index 0000000..fc6df83
--- /dev/null
+++ b/elasticsearch/pom.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.calcite</groupId>
+        <artifactId>calcite</artifactId>
+        <version>1.8.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>calcite-elasticsearch</artifactId>
+    <packaging>jar</packaging>
+    <version>1.8.0-SNAPSHOT</version>
+    <name>Calcite Elasticsearch</name>
+    <description>Elasticsearch adapter for Calcite</description>
+
+    <properties>
+        <top.dir>${project.basedir}/..</top.dir>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.calcite.avatica</groupId>
+            <artifactId>avatica</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.calcite</groupId>
+            <artifactId>calcite-core</artifactId>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.calcite</groupId>
+            <artifactId>calcite-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.calcite</groupId>
+            <artifactId>calcite-linq4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>${elasticsearch-java-driver.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.carrotsearch</groupId>
+            <artifactId>hppc</artifactId>
+            <version>0.7.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>${maven-dependency-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <id>analyze</id>
+                        <goals>
+                            <goal>analyze-only</goal>
+                        </goals>
+                        <configuration>
+                            <failOnWarning>true</failOnWarning>
+                            <!-- ignore "unused but declared" warnings -->
+                            <ignoredUnusedDeclaredDependencies>
+                                <ignoredUnusedDeclaredDependency>org.apache.calcite.avatica:avatica</ignoredUnusedDeclaredDependency>
+                                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
+                                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+                            </ignoredUnusedDeclaredDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+            </plugin>
+            <!-- Parent module has the same plugin and does the work of
+                generating -sources.jar for each project. But without the
+                plugin declared here, IDEs don't know the sources are
+                available. -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                            <goal>test-jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerator.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerator.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerator.java
new file mode 100644
index 0000000..e7478f5
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchEnumerator.java
@@ -0,0 +1,151 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.tree.Primitive;
+
+import org.elasticsearch.search.SearchHit;
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Enumerator that reads from an Elasticsearch type.
+ */
+public class ElasticsearchEnumerator implements Enumerator<Object> {
+  private final Iterator<SearchHit> cursor;
+  private final Function1<SearchHit, Object> getter;
+  private Object current;
+
+  /**
+   * Creates an ElasticsearchEnumerator.
+   *
+   * @param cursor Iterator over Elasticsearch {@link SearchHit} objects
+   * @param getter Converts an object into a list of fields
+   */
+  public ElasticsearchEnumerator(Iterator<SearchHit> cursor, Function1<SearchHit, Object> getter) {
+    this.cursor = cursor;
+    this.getter = getter;
+  }
+
+  public Object current() {
+    return current;
+  }
+
+  public boolean moveNext() {
+    if (cursor.hasNext()) {
+      SearchHit map = cursor.next();
+      current = getter.apply(map);
+      return true;
+    } else {
+      current = null;
+      return false;
+    }
+  }
+
+  public void reset() {
+    throw new UnsupportedOperationException();
+  }
+
+  public void close() {
+    // nothing to do
+  }
+
+  private static Function1<SearchHit, Map> mapGetter() {
+    return new Function1<SearchHit, Map>() {
+      public Map apply(SearchHit searchHitFields) {
+        return (Map) searchHitFields.fields();
+      }
+    };
+  }
+
+  private static Function1<SearchHit, Object> singletonGetter(final String fieldName,
+      final Class fieldClass) {
+    return new Function1<SearchHit, Object>() {
+      public Object apply(SearchHit searchHitFields) {
+        if (searchHitFields.fields().isEmpty()) {
+          return convert(searchHitFields.getSource(), fieldClass);
+        } else {
+          return convert(searchHitFields.getFields(), fieldClass);
+        }
+      }
+    };
+  }
+
+  /**
+   * Function that extracts a given set of fields from {@link SearchHit}
+   * objects.
+   *
+   * @param fields List of fields to project
+   */
+  private static Function1<SearchHit, Object[]> listGetter(
+      final List<Map.Entry<String, Class>> fields) {
+    return new Function1<SearchHit, Object[]>() {
+      public Object[] apply(SearchHit searchHitFields) {
+        Object[] objects = new Object[fields.size()];
+        for (int i = 0; i < fields.size(); i++) {
+          final Map.Entry<String, Class> field = fields.get(i);
+          final String name = field.getKey();
+          if (searchHitFields.fields().isEmpty()) {
+            objects[i] = convert(searchHitFields.getSource().get(name), field.getValue());
+          } else {
+            objects[i] = convert(searchHitFields.field(name).getValue(), field.getValue());
+          }
+        }
+        return objects;
+      }
+    };
+  }
+
+  static Function1<SearchHit, Object> getter(List<Map.Entry<String, Class>> fields) {
+    //noinspection unchecked
+    return fields == null
+      ? (Function1) mapGetter()
+      : fields.size() == 1
+      ? singletonGetter(fields.get(0).getKey(), fields.get(0).getValue())
+      : (Function1) listGetter(fields);
+  }
+
+  private static Object convert(Object o, Class clazz) {
+    if (o == null) {
+      return null;
+    }
+    Primitive primitive = Primitive.of(clazz);
+    if (primitive != null) {
+      clazz = primitive.boxClass;
+    } else {
+      primitive = Primitive.ofBox(clazz);
+    }
+    if (clazz.isInstance(o)) {
+      return o;
+    }
+    if (o instanceof Date && primitive != null) {
+      o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
+    }
+    if (o instanceof Number && primitive != null) {
+      return primitive.number((Number) o);
+    }
+    return o;
+  }
+}
+
+// End ElasticsearchEnumerator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
new file mode 100644
index 0000000..f11a7b5
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchFilter.java
@@ -0,0 +1,284 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.JsonBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Implementation of a {@link org.apache.calcite.rel.core.Filter}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchFilter extends Filter implements ElasticsearchRel {
+  public ElasticsearchFilter(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
+      RexNode condition) {
+    super(cluster, traitSet, child, condition);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == child.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public Filter copy(RelTraitSet relTraitSet, RelNode input, RexNode condition) {
+    return new ElasticsearchFilter(getCluster(), relTraitSet, input, condition);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+    Translator translator = new Translator(ElasticsearchRules
+      .elasticsearchFieldNames(getRowType()));
+    String match = translator.translateMatch(condition);
+    implementor.add(match);
+  }
+
+  /**
+   * Translates {@link RexNode} expressions into Elasticsearch expression strings.
+   */
+  static class Translator {
+    final JsonBuilder builder = new JsonBuilder();
+    final Multimap<String, Pair<String, RexLiteral>> multimap =
+      HashMultimap.create();
+    final Map<String, RexLiteral> eqMap = new LinkedHashMap<>();
+    private final List<String> fieldNames;
+
+    Translator(List<String> fieldNames) {
+      this.fieldNames = fieldNames;
+    }
+
+    private String translateMatch(RexNode condition) {
+      // filter node
+      final Map<String, Object> filterMap = new LinkedHashMap<>();
+      filterMap.put("filter", translateOr(condition));
+
+      // constant_score node
+      final Map<String, Object> map = builder.map();
+      map.put("constant_score", filterMap);
+
+      return "\"query\" : " + builder.toJsonString(map).replaceAll("\\s+", "").toLowerCase();
+    }
+
+    private Object translateOr(RexNode condition) {
+      final List<Object> list = new ArrayList<>();
+
+      final List<RexNode> orNodes = RelOptUtil.disjunctions(condition);
+      for (RexNode node : orNodes) {
+        List<Map<String, Object>> andNodes = translateAnd(node);
+
+        if (andNodes.size() > 0) {
+          Map<String, Object> andClause = new HashMap<>();
+          andClause.put("must", andNodes);
+
+          // boolean filters
+          LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
+          filterEvaluator.put("bool", andClause);
+          list.add(filterEvaluator);
+        } else {
+          list.add(andNodes.get(0));
+        }
+      }
+
+      if (orNodes.size() > 1) {
+        Map<String, Object> map = builder.map();
+        map.put("should", list);
+
+        // boolean filters
+        LinkedHashMap<String, Object> filterEvaluator = new LinkedHashMap<>();
+        filterEvaluator.put("bool", map);
+        return filterEvaluator;
+      } else {
+        return list.get(0);
+      }
+    }
+
+    private void addPredicate(Map<String, Object> map, String op, Object v) {
+      if (map.containsKey(op) && stronger(op, map.get(op), v)) {
+        return;
+      }
+      map.put(op, v);
+    }
+
+    /**
+     * Translates a condition that may be an AND of other conditions. Gathers
+     * together conditions that apply to the same field.
+     */
+    private List<Map<String, Object>> translateAnd(RexNode node0) {
+      eqMap.clear();
+      multimap.clear();
+      for (RexNode node : RelOptUtil.conjunctions(node0)) {
+        translateMatch2(node);
+      }
+      List<Map<String, Object>> filters = new ArrayList<>();
+      for (Map.Entry<String, RexLiteral> entry : eqMap.entrySet()) {
+        multimap.removeAll(entry.getKey());
+
+        Map<String, Object> filter = new HashMap<>();
+        filter.put(entry.getKey(), literalValue(entry.getValue()));
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("term", filter);
+        filters.add(map);
+      }
+      for (Map.Entry<String, Collection<Pair<String, RexLiteral>>> entry
+        : multimap.asMap().entrySet()) {
+        Map<String, Object> map2 = builder.map();
+
+        Map<String, Object> map = new HashMap<>();
+        for (Pair<String, RexLiteral> s : entry.getValue()) {
+          if (!s.left.equals("not")) {
+            addPredicate(map2, s.left, literalValue(s.right));
+
+            Map<String, Object> filter = new HashMap<>();
+            filter.put(entry.getKey(), map2);
+
+            map.put("range", filter);
+          } else {
+            map2.put(entry.getKey(), literalValue(s.right));
+
+            Map<String, Object> termMap = new HashMap<>();
+            termMap.put("term", map2);
+
+            map.put("not", termMap);
+          }
+        }
+        filters.add(map);
+      }
+      return filters;
+    }
+
+    private boolean stronger(String key, Object v0, Object v1) {
+      if (key.equals("lt") || key.equals("lte")) {
+        if (v0 instanceof Number && v1 instanceof Number) {
+          return ((Number) v0).doubleValue() < ((Number) v1).doubleValue();
+        }
+        if (v0 instanceof String && v1 instanceof String) {
+          return v0.toString().compareTo(v1.toString()) < 0;
+        }
+      }
+      if (key.equals("gt") || key.equals("gte")) {
+        return stronger("lt", v1, v0);
+      }
+      return false;
+    }
+
+    private static Object literalValue(RexLiteral literal) {
+      return literal.getValue2();
+    }
+
+    private Void translateMatch2(RexNode node) {
+      switch (node.getKind()) {
+      case EQUALS:
+        return translateBinary(null, null, (RexCall) node);
+      case LESS_THAN:
+        return translateBinary("lt", "gt", (RexCall) node);
+      case LESS_THAN_OR_EQUAL:
+        return translateBinary("lte", "gte", (RexCall) node);
+      case NOT_EQUALS:
+        return translateBinary("not", "not", (RexCall) node);
+      case GREATER_THAN:
+        return translateBinary("gt", "lt", (RexCall) node);
+      case GREATER_THAN_OR_EQUAL:
+        return translateBinary("gte", "lte", (RexCall) node);
+      default:
+        throw new AssertionError("cannot translate " + node);
+      }
+    }
+
+    /**
+     * Translates a call to a binary operator, reversing arguments if
+     * necessary.
+     */
+    private Void translateBinary(String op, String rop, RexCall call) {
+      final RexNode left = call.operands.get(0);
+      final RexNode right = call.operands.get(1);
+      boolean b = translateBinary2(op, left, right);
+      if (b) {
+        return null;
+      }
+      b = translateBinary2(rop, right, left);
+      if (b) {
+        return null;
+      }
+      throw new AssertionError("cannot translate op " + op + " call " + call);
+    }
+
+    /**
+     * Translates a call to a binary operator. Returns whether successful.
+     */
+    private boolean translateBinary2(String op, RexNode left, RexNode right) {
+      switch (right.getKind()) {
+      case LITERAL:
+        break;
+      default:
+        return false;
+      }
+      final RexLiteral rightLiteral = (RexLiteral) right;
+      switch (left.getKind()) {
+      case INPUT_REF:
+        final RexInputRef left1 = (RexInputRef) left;
+        String name = fieldNames.get(left1.getIndex());
+        translateOp2(op, name, rightLiteral);
+        return true;
+      case CAST:
+        return translateBinary2(op, ((RexCall) left).operands.get(0), right);
+      case OTHER_FUNCTION:
+        String itemName = ElasticsearchRules.isItem((RexCall) left);
+        if (itemName != null) {
+          translateOp2(op, itemName, rightLiteral);
+          return true;
+        }
+        // fall through
+      default:
+        return false;
+      }
+    }
+
+    private void translateOp2(String op, String name, RexLiteral right) {
+      if (op == null) {
+        eqMap.put(name, right);
+      } else {
+        multimap.put(name, Pair.of(op, right));
+      }
+    }
+  }
+}
+
+// End ElasticsearchFilter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
new file mode 100644
index 0000000..a0b3af6
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchMethod.java
@@ -0,0 +1,50 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.linq4j.tree.Types;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Builtin methods in the Elasticsearch adapter.
+ */
+enum ElasticsearchMethod {
+  ELASTICSEARCH_QUERYABLE_FIND(ElasticsearchTable.ElasticsearchQueryable.class, "find",
+      List.class, List.class);
+
+  public final Method method;
+
+  public static final ImmutableMap<Method, ElasticsearchMethod> MAP;
+
+  static {
+    final ImmutableMap.Builder<Method, ElasticsearchMethod> builder = ImmutableMap.builder();
+    for (ElasticsearchMethod value: ElasticsearchMethod.values()) {
+      builder.put(value.method, value);
+    }
+    MAP = builder.build();
+  }
+
+  ElasticsearchMethod(Class clazz, String methodName, Class... argumentTypes) {
+    this.method = Types.lookupMethod(clazz, methodName, argumentTypes);
+  }
+}
+
+// End ElasticsearchMethod.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
new file mode 100644
index 0000000..c2c09a5
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchProject.java
@@ -0,0 +1,95 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Project}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchProject extends Project implements ElasticsearchRel {
+  public ElasticsearchProject(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
+      List<? extends RexNode> projects, RelDataType rowType) {
+    super(cluster, traitSet, input, projects, rowType);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == input.getConvention();
+  }
+
+  @Override public Project copy(RelTraitSet relTraitSet, RelNode input, List<RexNode> projects,
+      RelDataType relDataType) {
+    return new ElasticsearchProject(getCluster(), traitSet, input, projects, relDataType);
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+
+    final ElasticsearchRules.RexToElasticsearchTranslator translator =
+      new ElasticsearchRules.RexToElasticsearchTranslator(
+        (JavaTypeFactory) getCluster().getTypeFactory(),
+        ElasticsearchRules.elasticsearchFieldNames(getInput().getRowType()));
+
+    final List<String> findItems = new ArrayList<>();
+    final List<String> scriptFieldItems = new ArrayList<>();
+    for (Pair<RexNode, String> pair: getNamedProjects()) {
+      final String name = pair.right;
+      final String expr = pair.left.accept(translator);
+
+      if (expr.equals("\"" + name + "\"")) {
+        findItems.add(ElasticsearchRules.quote(name));
+      } else if (expr.matches("\"literal\":.+")) {
+        scriptFieldItems.add(ElasticsearchRules.quote(name) + ":{\"script\": "
+          + expr.split(":")[1] + "}");
+      } else {
+        scriptFieldItems.add(ElasticsearchRules.quote(name) + ":{\"script\":\"_source."
+          + expr.replaceAll("\"", "") + "\"}");
+      }
+    }
+    final String findString = Util.toString(findItems, "", ", ", "");
+    final String scriptFieldString = "\"script_fields\": {"
+        + Util.toString(scriptFieldItems, "", ", ", "") + "}";
+    final String fieldString = "\"fields\" : [" + findString + "]"
+        + ", " + scriptFieldString;
+
+    for (String opfield : implementor.list) {
+      if (opfield.startsWith("\"fields\"")) {
+        implementor.list.remove(opfield);
+      }
+    }
+    implementor.add(fieldString);
+  }
+}
+
+// End ElasticsearchProject.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
new file mode 100644
index 0000000..e24cb0d
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRel.java
@@ -0,0 +1,58 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Relational expression that uses Elasticsearch calling convention.
+ */
+public interface ElasticsearchRel extends RelNode {
+  void implement(Implementor implementor);
+
+  /**
+   * Calling convention for relational operations that occur in Elasticsearch.
+   */
+  Convention CONVENTION = new Convention.Impl("ELASTICSEARCH", ElasticsearchRel.class);
+
+  /**
+   * Callback for the implementation process that converts a tree of
+   * {@link ElasticsearchRel} nodes into an Elasticsearch query.
+   */
+  class Implementor {
+    final List<String> list = new ArrayList<>();
+
+    RelOptTable table;
+    ElasticsearchTable elasticsearchTable;
+
+    public void add(String findOp) {
+      list.add(findOp);
+    }
+
+    public void visitChild(int ordinal, RelNode input) {
+      assert ordinal == 0;
+      ((ElasticsearchRel) input).implement(this);
+    }
+  }
+}
+
+// End ElasticsearchRel.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
new file mode 100644
index 0000000..2e68156
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
@@ -0,0 +1,240 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.RexImpTable;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelTrait;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.util.trace.CalciteTrace;
+
+import org.slf4j.Logger;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Rules and relational operators for
+ * {@link ElasticsearchRel#CONVENTION ELASTICSEARCH}
+ * calling convention.
+ */
+class ElasticsearchRules {
+  protected static final Logger LOGGER = CalciteTrace.getPlannerTracer();
+
+  static final RelOptRule[] RULES = {
+    ElasticsearchSortRule.INSTANCE,
+    ElasticsearchFilterRule.INSTANCE,
+    ElasticsearchProjectRule.INSTANCE
+  };
+
+  private ElasticsearchRules() {}
+
+  /**
+   * Returns 'string' if it is a call to item['string'], null otherwise.
+   */
+  static String isItem(RexCall call) {
+    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
+      return null;
+    }
+    final RexNode op0 = call.getOperands().get(0);
+    final RexNode op1 = call.getOperands().get(1);
+
+    if (op0 instanceof RexInputRef
+      && ((RexInputRef) op0).getIndex() == 0
+      && op1 instanceof RexLiteral
+      && ((RexLiteral) op1).getValue2() instanceof String) {
+      return (String) ((RexLiteral) op1).getValue2();
+    }
+    return null;
+  }
+
+  static List<String> elasticsearchFieldNames(final RelDataType rowType) {
+    return SqlValidatorUtil.uniquify(
+      new AbstractList<String>() {
+        @Override public String get(int index) {
+          final String name = rowType.getFieldList().get(index).getName();
+          return name.startsWith("$") ? "_" + name.substring(2) : name;
+        }
+
+        @Override public int size() {
+          return rowType.getFieldCount();
+        }
+      });
+  }
+
+  static String quote(String s) {
+    return "\"" + s + "\"";
+  }
+
+  /**
+   * Translator from {@link RexNode} to strings in Elasticsearch's expression
+   * language.
+   */
+  static class RexToElasticsearchTranslator extends RexVisitorImpl<String> {
+    private final JavaTypeFactory typeFactory;
+    private final List<String> inFields;
+
+    RexToElasticsearchTranslator(JavaTypeFactory typeFactory, List<String> inFields) {
+      super(true);
+      this.typeFactory = typeFactory;
+      this.inFields = inFields;
+    }
+
+    @Override public String visitLiteral(RexLiteral literal) {
+      if (literal.getValue() == null) {
+        return "null";
+      }
+      return "\"literal\":\""
+        + RexToLixTranslator.translateLiteral(literal, literal.getType(),
+          typeFactory, RexImpTable.NullAs.NOT_POSSIBLE)
+        + "\"";
+    }
+
+    @Override public String visitInputRef(RexInputRef inputRef) {
+      return quote(inFields.get(inputRef.getIndex()));
+    }
+
+    @Override public String visitCall(RexCall call) {
+      final String name = isItem(call);
+      if (name != null) {
+        return "\"" + name + "\"";
+      }
+
+      final List<String> strings = visitList(call.operands);
+      if (call.getKind() == SqlKind.CAST) {
+        return strings.get(0).startsWith("$") ? strings.get(0).substring(1) : strings.get(0);
+      }
+      if (call.getOperator() == SqlStdOperatorTable.ITEM) {
+        final RexNode op1 = call.getOperands().get(1);
+        if (op1 instanceof RexLiteral && op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
+          return stripQuotes(strings.get(0)) + "[" + ((RexLiteral) op1).getValue2() + "]";
+        }
+      }
+      throw new IllegalArgumentException("Translation of " + call.toString()
+        + "is not supported by ElasticsearchProject");
+    }
+
+    private String stripQuotes(String s) {
+      return s.startsWith("'") && s.endsWith("'") ? s.substring(1, s.length() - 1) : s;
+    }
+
+    List<String> visitList(List<RexNode> list) {
+      final List<String> strings = new ArrayList<>();
+      for (RexNode node: list) {
+        strings.add(node.accept(this));
+      }
+      return strings;
+    }
+  }
+
+  /**
+   * Base class for planner rules that convert a relational expression to
+   * Elasticsearch calling convention.
+   */
+  abstract static class ElasticsearchConverterRule extends ConverterRule {
+    final Convention out;
+
+    ElasticsearchConverterRule(Class<? extends RelNode> clazz, RelTrait in, Convention out,
+        String description) {
+      super(clazz, in, out, description);
+      this.out = out;
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.core.Sort} to an
+   * {@link ElasticsearchSort}.
+   */
+  private static class ElasticsearchSortRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchSortRule INSTANCE = new ElasticsearchSortRule();
+
+    private ElasticsearchSortRule() {
+      super(Sort.class, Convention.NONE, ElasticsearchRel.CONVENTION, "ElasticsearchSortRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final Sort sort = (Sort) relNode;
+      final RelTraitSet traitSet = sort.getTraitSet().replace(out).replace(sort.getCollation());
+      return new ElasticsearchSort(relNode.getCluster(), traitSet,
+        convert(sort.getInput(), traitSet.replace(RelCollations.EMPTY)), sort.getCollation(),
+        sort.offset, sort.fetch);
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to an
+   * {@link ElasticsearchFilter}.
+   */
+  private static class ElasticsearchFilterRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchFilterRule INSTANCE = new ElasticsearchFilterRule();
+
+    private ElasticsearchFilterRule() {
+      super(LogicalFilter.class, Convention.NONE, ElasticsearchRel.CONVENTION,
+        "ElasticsearchFilterRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final LogicalFilter filter = (LogicalFilter) relNode;
+      final RelTraitSet traitSet = filter.getTraitSet().replace(out);
+      return new ElasticsearchFilter(relNode.getCluster(), traitSet,
+        convert(filter.getInput(), out),
+        filter.getCondition());
+    }
+  }
+
+  /**
+   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
+   * to an {@link ElasticsearchProject}.
+   */
+  private static class ElasticsearchProjectRule extends ElasticsearchConverterRule {
+    private static final ElasticsearchProjectRule INSTANCE = new ElasticsearchProjectRule();
+
+    private ElasticsearchProjectRule() {
+      super(LogicalProject.class, Convention.NONE, ElasticsearchRel.CONVENTION,
+        "ElasticsearchProjectRule");
+    }
+
+    @Override public RelNode convert(RelNode relNode) {
+      final LogicalProject project = (LogicalProject) relNode;
+      final RelTraitSet traitSet = project.getTraitSet().replace(out);
+      return new ElasticsearchProject(project.getCluster(), traitSet,
+        convert(project.getInput(), out), project.getProjects(), project.getRowType());
+    }
+  }
+}
+
+// End ElasticsearchRules.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
new file mode 100644
index 0000000..e59e0a4
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchema.java
@@ -0,0 +1,125 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.impl.AbstractSchema;
+
+import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
+import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
+import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.client.transport.TransportClient;
+import org.elasticsearch.cluster.metadata.MappingMetaData;
+import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.common.collect.ImmutableOpenMap;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.common.transport.TransportAddress;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Schema mapped onto an index of ELASTICSEARCH types.
+ *
+ * <p>Each table in the schema is an ELASTICSEARCH type in that index.
+ */
+public class ElasticsearchSchema extends AbstractSchema {
+  final String index;
+
+  private transient Client client;
+
+  /**
+   * Creates an Elasticsearch schema.
+   *
+   * @param coordinates Map of Elasticsearch node locations (host, port)
+   * @param userConfig Map of user-specified configurations
+   * @param indexName Elasticsearch database name, e.g. "usa".
+   */
+  ElasticsearchSchema(Map<String, Integer> coordinates,
+      Map<String, String> userConfig, String indexName) {
+    super();
+
+    final List<InetSocketAddress> transportAddresses = new ArrayList<>();
+    for (Map.Entry<String, Integer> coordinate: coordinates.entrySet()) {
+      transportAddresses.add(new InetSocketAddress(coordinate.getKey(), coordinate.getValue()));
+    }
+
+    open(transportAddresses, userConfig);
+
+    if (client != null) {
+      final String[] indices = client.admin().indices()
+          .getIndex(new GetIndexRequest().indices(indexName))
+          .actionGet().getIndices();
+      if (indices.length == 1) {
+        index = indices[0];
+      } else {
+        index = null;
+      }
+    } else {
+      index = null;
+    }
+  }
+
+  @Override protected Map<String, Table> getTableMap() {
+    final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
+
+    try {
+      GetMappingsResponse response = client.admin().indices().getMappings(
+        new GetMappingsRequest().indices(index)).get();
+      ImmutableOpenMap<String, MappingMetaData> mapping = response.getMappings().get(index);
+      for (ObjectObjectCursor<String, MappingMetaData> c: mapping) {
+        builder.put(c.key, new ElasticsearchTable(client, index, c.key));
+      }
+    } catch (Exception e) {
+      throw Throwables.propagate(e);
+    }
+    return builder.build();
+  }
+
+  private void open(List<InetSocketAddress> transportAddresses, Map<String, String> userConfig) {
+    final List<TransportAddress> transportNodes = new ArrayList<>(transportAddresses.size());
+    for (InetSocketAddress address : transportAddresses) {
+      transportNodes.add(new InetSocketTransportAddress(address));
+    }
+
+    Settings settings = Settings.settingsBuilder().put(userConfig).build();
+
+    final TransportClient transportClient = TransportClient.builder().settings(settings).build();
+    for (TransportAddress transport : transportNodes) {
+      transportClient.addTransportAddress(transport);
+    }
+
+    final List<DiscoveryNode> nodes = ImmutableList.copyOf(transportClient.connectedNodes());
+    if (nodes.isEmpty()) {
+      throw new RuntimeException("Cannot connect to any elasticsearch nodes");
+    }
+
+    client = transportClient;
+  }
+}
+
+// End ElasticsearchSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
new file mode 100644
index 0000000..41ffc10
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSchemaFactory.java
@@ -0,0 +1,63 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaFactory;
+import org.apache.calcite.schema.SchemaPlus;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Factory that creates a {@link ElasticsearchSchema}.
+ *
+ * <p>Allows a custom schema to be included in a model.json file.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class ElasticsearchSchemaFactory implements SchemaFactory {
+
+  public ElasticsearchSchemaFactory() {
+  }
+
+  @Override public Schema create(SchemaPlus parentSchema, String name,
+      Map<String, Object> operand) {
+    final Map map = (Map) operand;
+
+    final ObjectMapper mapper = new ObjectMapper();
+    mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+
+    try {
+      final Map<String, Integer> coordinates =
+          mapper.readValue((String) map.get("coordinates"),
+              new TypeReference<Map<String, Integer>>() { });
+      final Map<String, String> userConfig =
+          mapper.readValue((String) map.get("userConfig"),
+              new TypeReference<Map<String, String>>() { });
+      final String index = (String) map.get("index");
+      return new ElasticsearchSchema(coordinates, userConfig, index);
+    } catch (IOException e) {
+      throw new RuntimeException("Cannot parse values from json", e);
+    }
+  }
+}
+
+// End ElasticsearchSchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
new file mode 100644
index 0000000..5f5dfe8
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchSort.java
@@ -0,0 +1,93 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Sort}
+ * relational expression in Elasticsearch.
+ */
+public class ElasticsearchSort extends Sort implements ElasticsearchRel {
+  public ElasticsearchSort(RelOptCluster cluster, RelTraitSet traitSet, RelNode child,
+      RelCollation collation, RexNode offset, RexNode fetch) {
+    super(cluster, traitSet, child, collation, offset, fetch);
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+    assert getConvention() == child.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.05);
+  }
+
+  @Override public Sort copy(RelTraitSet traitSet, RelNode relNode, RelCollation relCollation,
+      RexNode offset, RexNode fetch) {
+    return new ElasticsearchSort(getCluster(), traitSet, relNode, collation, offset, fetch);
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.visitChild(0, getInput());
+    if (!collation.getFieldCollations().isEmpty()) {
+      final List<String> keys = new ArrayList<>();
+      final List<RelDataTypeField> fields = getRowType().getFieldList();
+
+      for (RelFieldCollation fieldCollation: collation.getFieldCollations()) {
+        final String name = fields.get(fieldCollation.getFieldIndex()).getName();
+        keys.add(ElasticsearchRules.quote(name) + ": " + direction(fieldCollation));
+      }
+
+      implementor.add("\"sort\": [ " + Util.toString(keys, "{", "}, {", "}") + "]");
+    }
+
+    if (offset != null) {
+      implementor.add("\"from\": " + ((RexLiteral) offset).getValue());
+    }
+
+    if (fetch != null) {
+      implementor.add("\"size\": " + ((RexLiteral) fetch).getValue());
+    }
+  }
+
+  private String direction(RelFieldCollation fieldCollation) {
+    switch (fieldCollation.getDirection()) {
+    case DESCENDING:
+    case STRICTLY_DESCENDING:
+      return "\"desc\"";
+    case ASCENDING:
+    case STRICTLY_ASCENDING:
+    default:
+      return "\"asc\"";
+    }
+  }
+}
+
+// End ElasticsearchSort.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
new file mode 100644
index 0000000..f3dbca5
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTable.java
@@ -0,0 +1,150 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.java.AbstractQueryableTable;
+import org.apache.calcite.linq4j.AbstractEnumerable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.linq4j.Queryable;
+import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import org.apache.calcite.util.Util;
+
+import org.elasticsearch.client.Client;
+import org.elasticsearch.search.SearchHit;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Table based on an Elasticsearch type.
+ */
+public class ElasticsearchTable extends AbstractQueryableTable implements TranslatableTable {
+  private final Client client;
+  private final String indexName;
+  private final String typeName;
+
+  /**
+   * Creates an ElasticsearchTable.
+   */
+  public ElasticsearchTable(Client client, String indexName,
+      String typeName) {
+    super(Object[].class);
+    this.client = client;
+    this.indexName = indexName;
+    this.typeName = typeName;
+  }
+
+  @Override public String toString() {
+    return "ElasticsearchTable{" + typeName + "}";
+  }
+
+  public RelDataType getRowType(RelDataTypeFactory relDataTypeFactory) {
+    final RelDataType mapType = relDataTypeFactory.createMapType(
+        relDataTypeFactory.createSqlType(SqlTypeName.VARCHAR),
+        relDataTypeFactory.createTypeWithNullability(
+            relDataTypeFactory.createSqlType(SqlTypeName.ANY),
+            true));
+    return relDataTypeFactory.builder().add("_MAP", mapType).build();
+  }
+
+  public <T> Queryable<T> asQueryable(QueryProvider queryProvider, SchemaPlus schema,
+      String tableName) {
+    return new ElasticsearchQueryable<>(queryProvider, schema, this, tableName);
+  }
+
+  public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable relOptTable) {
+    final RelOptCluster cluster = context.getCluster();
+    return new ElasticsearchTableScan(cluster, cluster.traitSetOf(ElasticsearchRel.CONVENTION),
+        relOptTable, this, null);
+  }
+
+  /** Executes a "find" operation on the underlying type.
+   *
+   * <p>For example,
+   * <code>client.prepareSearch(index).setTypes(type)
+   * .setSource("{\"fields\" : [\"state\"]}")</code></p>
+   *
+   * @param index Elasticsearch index
+   * @param ops List of operations represented as Json strings.
+   * @param fields List of fields to project; or null to return map
+   * @return Enumerator of results
+   */
+  private Enumerable<Object> find(String index, List<String> ops,
+      List<Map.Entry<String, Class>> fields) {
+    final String dbName = index;
+
+    final String queryString = "{" + Util.toString(ops, "", ", ", "") + "}";
+
+    final Function1<SearchHit, Object> getter = ElasticsearchEnumerator.getter(fields);
+
+    return new AbstractEnumerable<Object>() {
+      public Enumerator<Object> enumerator() {
+        final Iterator<SearchHit> cursor = client.prepareSearch(dbName).setTypes(typeName)
+            .setSource(queryString).execute().actionGet().getHits().iterator();
+        return new ElasticsearchEnumerator(cursor, getter);
+      }
+    };
+  }
+
+  /**
+   * Implementation of {@link org.apache.calcite.linq4j.Queryable} based on
+   * a {@link org.apache.calcite.adapter.elasticsearch.ElasticsearchTable}.
+   */
+  public static class ElasticsearchQueryable<T> extends AbstractTableQueryable<T> {
+    public ElasticsearchQueryable(QueryProvider queryProvider, SchemaPlus schema,
+        ElasticsearchTable table, String tableName) {
+      super(queryProvider, schema, table, tableName);
+    }
+
+    public Enumerator<T> enumerator() {
+      return null;
+    }
+
+    private String getIndex() {
+      return schema.unwrap(ElasticsearchSchema.class).index;
+    }
+
+    private ElasticsearchTable getTable() {
+      return (ElasticsearchTable) table;
+    }
+
+    /** Called via code-generation.
+     *
+     * @see org.apache.calcite.adapter.elasticsearch.ElasticsearchMethod#ELASTICSEARCH_QUERYABLE_FIND
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public Enumerable<Object> find(List<String> ops,
+        List<Map.Entry<String, Class>> fields) {
+      return getTable().find(getIndex(), ops, fields);
+    }
+  }
+}
+
+// End ElasticsearchTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
new file mode 100644
index 0000000..636a629
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchTableScan.java
@@ -0,0 +1,88 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+
+import java.util.List;
+
+/**
+ * Relational expression representing a scan of an Elasticsearch type.
+ *
+ * <p> Additional operations might be applied,
+ * using the "find" method.</p>
+ */
+public class ElasticsearchTableScan extends TableScan implements ElasticsearchRel {
+  private final ElasticsearchTable elasticsearchTable;
+  private final RelDataType projectRowType;
+
+  /**
+   * Creates an ElasticsearchTableScan.
+   *
+   * @param cluster Cluster
+   * @param traitSet Trait set
+   * @param table Table
+   * @param elasticsearchTable Elasticsearch table
+   * @param projectRowType Fields and types to project; null to project raw row
+   */
+  protected ElasticsearchTableScan(RelOptCluster cluster, RelTraitSet traitSet, RelOptTable table,
+      ElasticsearchTable elasticsearchTable, RelDataType projectRowType) {
+    super(cluster, traitSet, table);
+    this.elasticsearchTable = elasticsearchTable;
+    this.projectRowType = projectRowType;
+
+    assert elasticsearchTable != null;
+    assert getConvention() == ElasticsearchRel.CONVENTION;
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    assert inputs.isEmpty();
+    return this;
+  }
+
+  @Override public RelDataType deriveRowType() {
+    return projectRowType != null ? projectRowType : super.deriveRowType();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    final float f = projectRowType == null ? 1f : (float) projectRowType.getFieldCount() / 100f;
+    return super.computeSelfCost(planner, mq).multiplyBy(.1 * f);
+  }
+
+  @Override public void register(RelOptPlanner planner) {
+    planner.addRule(ElasticsearchToEnumerableConverterRule.INSTANCE);
+    for (RelOptRule rule: ElasticsearchRules.RULES) {
+      planner.addRule(rule);
+    }
+  }
+
+  @Override public void implement(Implementor implementor) {
+    implementor.elasticsearchTable = elasticsearchTable;
+    implementor.table = table;
+  }
+}
+
+// End ElasticsearchTableScan.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
new file mode 100644
index 0000000..adb88f7
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.EnumerableRel;
+import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
+import org.apache.calcite.adapter.enumerable.JavaRowFormat;
+import org.apache.calcite.adapter.enumerable.PhysType;
+import org.apache.calcite.adapter.enumerable.PhysTypeImpl;
+
+import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.MethodCallExpression;
+import org.apache.calcite.plan.ConventionTraitDef;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterImpl;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
+import java.util.AbstractList;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Relational expression representing a scan of a table in an Elasticsearch data source.
+ */
+public class ElasticsearchToEnumerableConverter extends ConverterImpl implements EnumerableRel {
+  protected ElasticsearchToEnumerableConverter(RelOptCluster cluster, RelTraitSet traits,
+      RelNode input) {
+    super(cluster, ConventionTraitDef.INSTANCE, traits, input);
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    return new ElasticsearchToEnumerableConverter(getCluster(), traitSet, sole(inputs));
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(.1);
+  }
+
+  @Override public Result implement(EnumerableRelImplementor implementor, Prefer prefer) {
+    final BlockBuilder list = new BlockBuilder();
+    final ElasticsearchRel.Implementor elasticsearchImplementor =
+        new ElasticsearchRel.Implementor();
+    elasticsearchImplementor.visitChild(0, getInput());
+    final RelDataType rowType = getRowType();
+    final PhysType physType = PhysTypeImpl.of(implementor.getTypeFactory(), rowType,
+        prefer.prefer(JavaRowFormat.ARRAY));
+    final Expression fields = list.append("fields",
+        constantArrayList(
+            Pair.zip(ElasticsearchRules.elasticsearchFieldNames(rowType),
+                new AbstractList<Class>() {
+                  @Override public Class get(int index) {
+                    return physType.fieldClass(index);
+                  }
+
+                  @Override public int size() {
+                    return rowType.getFieldCount();
+                  }
+                }),
+            Pair.class));
+    final Expression table = list.append("table",
+        elasticsearchImplementor.table
+            .getExpression(ElasticsearchTable.ElasticsearchQueryable.class));
+    List<String> opList = elasticsearchImplementor.list;
+    final Expression ops = list.append("ops", constantArrayList(opList, String.class));
+    Expression enumerable = list.append("enumerable",
+        Expressions.call(table, ElasticsearchMethod.ELASTICSEARCH_QUERYABLE_FIND.method, ops,
+            fields));
+    if (CalcitePrepareImpl.DEBUG) {
+      System.out.println("Elasticsearch: " + opList);
+    }
+    Hook.QUERY_PLAN.run(opList);
+    list.add(Expressions.return_(null, enumerable));
+    return implementor.result(physType, list.toBlock());
+  }
+
+  /** E.g. {@code constantArrayList("x", "y")} returns
+   * "Arrays.asList('x', 'y')". */
+  private static <T> MethodCallExpression constantArrayList(List<T> values, Class clazz) {
+    return Expressions.call(BuiltInMethod.ARRAYS_AS_LIST.method,
+        Expressions.newArrayInit(clazz, constantList(values)));
+  }
+
+  /** E.g. {@code constantList("x", "y")} returns
+   * {@code {ConstantExpression("x"), ConstantExpression("y")}}. */
+  private static <T> List<Expression> constantList(List<T> values) {
+    return Lists.transform(values,
+        new Function<T, Expression>() {
+          @Nullable
+          @Override public Expression apply(@Nullable T t) {
+            return Expressions.constant(t);
+          }
+        });
+  }
+}
+
+// End ElasticsearchToEnumerableConverter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
new file mode 100644
index 0000000..1047757
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchToEnumerableConverterRule.java
@@ -0,0 +1,42 @@
+/*
+ * 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.adapter.elasticsearch;
+
+import org.apache.calcite.adapter.enumerable.EnumerableConvention;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+
+/**
+ * Rule to convert a relational expression from
+ * {@link ElasticsearchRel#CONVENTION} to {@link EnumerableConvention}.
+ */
+public class ElasticsearchToEnumerableConverterRule extends ConverterRule {
+  public static final ConverterRule INSTANCE = new ElasticsearchToEnumerableConverterRule();
+
+  private ElasticsearchToEnumerableConverterRule() {
+    super(RelNode.class, ElasticsearchRel.CONVENTION, EnumerableConvention.INSTANCE,
+        "ElasticsearchToEnumerableConverterRule");
+  }
+
+  @Override public RelNode convert(RelNode relNode) {
+    RelTraitSet newTraitSet = relNode.getTraitSet().replace(getOutConvention());
+    return new ElasticsearchToEnumerableConverter(relNode.getCluster(), newTraitSet, relNode);
+  }
+}
+
+// End ElasticsearchToEnumerableConverterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
new file mode 100644
index 0000000..dad800a
--- /dev/null
+++ b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Query provider based on an Elasticsearch DB.
+ */
+@PackageMarker
+package org.apache.calcite.adapter.elasticsearch;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java


[14/14] calcite git commit: [CALCITE-1267] Trivial pointer in README.md to release notes on the website

Posted by el...@apache.org.
[CALCITE-1267] Trivial pointer in README.md to release notes on the website


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

Branch: refs/heads/branch-avatica-1.8
Commit: ea25b7327e734f93acae85dc60f6188f852bed12
Parents: 08c966b
Author: Josh Elser <el...@apache.org>
Authored: Tue May 31 19:22:00 2016 -0400
Committer: Josh Elser <el...@apache.org>
Committed: Tue May 31 19:22:34 2016 -0400

----------------------------------------------------------------------
 avatica/README.md | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/ea25b732/avatica/README.md
----------------------------------------------------------------------
diff --git a/avatica/README.md b/avatica/README.md
index 1d2cf28..9bbf29b 100644
--- a/avatica/README.md
+++ b/avatica/README.md
@@ -25,3 +25,6 @@ Apache Calcite's Avatica is a framework for building database drivers.
 Avatica is a sub-project of [Apache Calcite](https://calcite.apache.org).
 
 For more details, see the [home page](https://calcite.apache.org/avatica).
+
+Release notes for all published versions are available on the [history
+page](https://calcite.apache.org/avatica/docs/history.html).


[08/14] calcite git commit: [CALCITE-1253] Elasticsearch adapter (Subhobrata Dey)

Posted by el...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchAdapterIT.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchAdapterIT.java b/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchAdapterIT.java
new file mode 100644
index 0000000..d99351e
--- /dev/null
+++ b/elasticsearch/src/test/java/org/apache/calcite/test/ElasticsearchAdapterIT.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Test;
+
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.elasticsearch} package.
+ *
+ * <p>Before calling this test, you need to populate Elasticsearch, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-test-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * This will create a virtual machine with Elasticsearch and the "zips" test
+ * dataset.
+ */
+public class ElasticsearchAdapterIT {
+  /**
+   * Whether to run Elasticsearch tests. Enabled by default, however test is only
+   * included if "it" profile is activated ({@code -Pit}). To disable,
+   * specify {@code -Dcalcite.test.elasticsearch=false} on the Java command line.
+   */
+  private static final boolean ENABLED = Util.getBooleanProperty("calcite.test.elasticsearch",
+      true);
+
+  /** Connection factory based on the "zips-es" model. */
+  private static final ImmutableMap<String, String> ZIPS = ImmutableMap.of("model",
+      ElasticsearchAdapterIT.class.getResource("/elasticsearch-zips-model.json").getPath());
+
+  /** Whether to run this test. */
+  private boolean enabled() {
+    return ENABLED;
+  }
+
+  /** Returns a function that checks that a particular Elasticsearch pipeline is
+   * generated to implement a query. */
+  private static Function<List, Void> elasticsearchChecker(final String... strings) {
+    return new Function<List, Void>() {
+      @Nullable
+      @Override public Void apply(@Nullable List actual) {
+        Object[] actualArray = actual == null || actual.isEmpty() ? null
+            : ((List) actual.get(0)).toArray();
+        CalciteAssert.assertArrayEqual("expected Elasticsearch query not found", strings,
+            actualArray);
+        return null;
+      }
+    };
+  }
+
+  @Test public void testSort() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], dir0=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips order by \"state\"")
+        .returnsCount(10)
+        .explainContains(explain);
+  }
+
+  @Test public void testSortLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "order by \"state\", \"id\" offset 2 rows fetch next 3 rows only";
+    CalciteAssert.that()
+        .with(ZIPS)
+        .query(sql)
+        .returnsUnordered("state=AK; id=99503",
+            "state=AK; id=99504",
+            "state=AK; id=99505")
+        .queryContains(
+            elasticsearchChecker(
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]",
+                "\"from\": 2",
+                "\"size\": 3"));
+  }
+
+  @Test public void testOffsetLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "offset 2 fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"from\": 2",
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testLimit() {
+    final String sql = "select \"state\", \"id\" from zips\n"
+        + "fetch next 3 rows only";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(
+                "\"size\": 3",
+                "\"fields\" : [\"state\", \"id\"], \"script_fields\": {}"));
+  }
+
+  @Test public void testFilterSort() {
+    final String sql = "select * from zips\n"
+        + "where \"city\" = 'SPRINGFIELD' and \"id\" >= '70000'\n"
+        + "order by \"state\", \"id\"";
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchSort(sort0=[$4], sort1=[$5], dir0=[ASC], dir1=[ASC])\n"
+        + "    ElasticsearchProject(city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], longitude=[CAST(ITEM(ITEM($0, 'loc'), 0)):FLOAT], latitude=[CAST(ITEM(ITEM($0, 'loc'), 1)):FLOAT], pop=[CAST(ITEM($0, 'pop')):INTEGER], state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], id=[CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "      ElasticsearchFilter(condition=[AND(=(CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'SPRINGFIELD'), >=(CAST(ITEM($0, 'id')):VARCHAR(5) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", '70000'))])\n"
+        + "        ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .returnsOrdered(
+            "city=SPRINGFIELD; longitude=-92.54567; latitude=35.274879; pop=752; state=AR; id=72157",
+            "city=SPRINGFIELD; longitude=-102.617322; latitude=37.406727; pop=1992; state=CO; id=81073",
+            "city=SPRINGFIELD; longitude=-90.577479; latitude=30.415738; pop=5597; state=LA; id=70462",
+            "city=SPRINGFIELD; longitude=-123.015259; latitude=44.06106; pop=32384; state=OR; id=97477",
+            "city=SPRINGFIELD; longitude=-122.917108; latitude=44.056056; pop=27521; state=OR; id=97478")
+        .queryContains(
+            elasticsearchChecker("\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                    + "{\"must\":[{\"term\":{\"city\":\"springfield\"}},{\"range\":{\"id\":{\"gte\":\"70000\"}}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}",
+                "\"sort\": [ {\"state\": \"asc\"}, {\"id\": \"asc\"}]"))
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterSortDesc() {
+    final String sql = "select * from zips\n"
+        + "where \"pop\" BETWEEN 20000 AND 20100\n"
+        + "order by \"state\" desc, \"pop\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(4)
+        .returnsOrdered(
+            "city=SHERIDAN; longitude=-106.964795; latitude=44.78486; pop=20025; state=WY; id=82801",
+            "city=MOUNTLAKE TERRAC; longitude=-122.304036; latitude=47.793061; pop=20059; state=WA; id=98043",
+            "city=FALMOUTH; longitude=-77.404537; latitude=38.314557; pop=20039; state=VA; id=22405",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104");
+  }
+
+  @Test public void testFilterRedundant() {
+    final String sql = "select * from zips\n"
+        + "where \"state\" > 'CA' and \"state\" < 'AZ' and \"state\" = 'OK'";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .runs()
+        .queryContains(
+            elasticsearchChecker(""
+                + "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":"
+                + "{\"must\":[{\"term\":{\"state\":\"ok\"}}]}}}}",
+                "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"));
+  }
+
+  @Test public void testInPlan() {
+    final String[] searches = {
+      "\"query\" : {\"constant_score\":{\"filter\":{\"bool\":{\"should\":"
+          + "[{\"bool\":{\"must\":[{\"term\":{\"pop\":20012}}]}},{\"bool\":{\"must\":[{\"term\":"
+          + "{\"pop\":15590}}]}}]}}}}",
+      "\"fields\" : [\"city\", \"pop\", \"state\", \"id\"], \"script_fields\": {\"longitude\":{\"script\":\"_source.loc[0]\"}, \"latitude\":{\"script\":\"_source.loc[1]\"}}"
+    };
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select * from zips where \"pop\" in (20012, 15590)")
+        .returnsUnordered(
+            "city=COVINA; longitude=-117.884285; latitude=34.08596; pop=15590; state=CA; id=91723",
+            "city=ARLINGTON; longitude=-97.091987; latitude=32.654752; pop=15590; state=TX; id=76018",
+            "city=CROFTON; longitude=-76.680166; latitude=39.011163; pop=15590; state=MD; id=21114",
+            "city=FORT WORTH; longitude=-97.318409; latitude=32.725551; pop=20012; state=TX; id=76104",
+            "city=DINUBA; longitude=-119.39087; latitude=36.534931; pop=20012; state=CA; id=93618")
+        .queryContains(elasticsearchChecker(searches));
+  }
+
+  @Test public void testZips() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips")
+        .returnsCount(10);
+  }
+
+  @Test public void testProject() {
+    final String sql = "select \"state\", \"city\", 0 as \"zero\"\n"
+        + "from zips\n"
+        + "order by \"state\", \"city\"";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query(sql)
+        .limit(2)
+        .returnsUnordered("state=AK; city=ELMENDORF AFB; zero=0",
+            "state=AK; city=EIELSON AFB; zero=0")
+        .queryContains(
+            elasticsearchChecker("\"sort\": [ {\"state\": \"asc\"}, {\"city\": \"asc\"}]",
+                "\"fields\" : [\"state\", \"city\"], \"script_fields\": {\"zero\":{\"script\": \"0\"}}"));
+  }
+
+  @Test public void testFilter() {
+    final String explain = "PLAN=ElasticsearchToEnumerableConverter\n"
+        + "  ElasticsearchProject(state=[CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"], city=[CAST(ITEM($0, 'city')):VARCHAR(20) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+        + "    ElasticsearchFilter(condition=[=(CAST(ITEM($0, 'state')):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\", 'CA')])\n"
+        + "      ElasticsearchTableScan(table=[[elasticsearch_raw, zips]])";
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" = 'CA'")
+        .limit(2)
+        .returnsUnordered("state=CA; city=LOS ANGELES",
+            "state=CA; city=LOS ANGELES")
+        .explainContains(explain);
+  }
+
+  @Test public void testFilterReversed() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where 'WI' < \"state\"")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(ZIPS)
+        .query("select \"state\", \"city\" from zips where \"state\" > 'WI'")
+        .limit(2)
+        .returnsUnordered("state=WV; city=WELCH",
+            "state=WV; city=HANOVER");
+  }
+}
+
+// End ElasticsearchAdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/test/resources/elasticsearch-zips-model.json
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/resources/elasticsearch-zips-model.json b/elasticsearch/src/test/resources/elasticsearch-zips-model.json
new file mode 100644
index 0000000..dcbf2a4
--- /dev/null
+++ b/elasticsearch/src/test/resources/elasticsearch-zips-model.json
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "elasticsearch",
+  "schemas": [
+    {
+      "type": "custom",
+      "name": "elasticsearch_raw",
+      "factory": "org.apache.calcite.adapter.elasticsearch.ElasticsearchSchemaFactory",
+      "operand": {
+        "coordinates": "{'127.0.0.1': 9300}",
+        "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
+        "index": "usa"
+      }
+    },
+    {
+      "name": "elasticsearch",
+      "tables": [
+        {
+          "name": "ZIPS",
+          "type": "view",
+          "sql": [
+            "select cast(_MAP['city'] AS varchar(20)) AS \"city\",\n",
+            " cast(_MAP['loc'][0] AS float) AS \"longitude\",\n",
+            " cast(_MAP['loc'][1] AS float) AS \"latitude\",\n",
+            " cast(_MAP['pop'] AS integer) AS \"pop\",\n",
+            " cast(_MAP['state'] AS varchar(2)) AS \"state\",\n",
+            " cast(_MAP['id'] AS varchar(5)) AS \"id\"\n",
+            "from \"elasticsearch_raw\".\"zips\""
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/elasticsearch/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/resources/log4j.properties b/elasticsearch/src/test/resources/log4j.properties
new file mode 100644
index 0000000..834e2db
--- /dev/null
+++ b/elasticsearch/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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.
+
+# Root logger is configured at INFO and is sent to A1
+log4j.rootLogger=INFO, A1
+
+# A1 goes to the console
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# Set the pattern for each log message
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index b12e430..063f16d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@ limitations under the License.
     <commons-lang3.version>3.2</commons-lang3.version>
     <commons-logging.version>1.1.3</commons-logging.version>
     <eigenbase-properties.version>1.1.5</eigenbase-properties.version>
+    <elasticsearch-java-driver.version>2.3.2</elasticsearch-java-driver.version>
     <findbugs.version>1.3.9</findbugs.version>
     <fmpp-maven-plugin.version>1.0</fmpp-maven-plugin.version>
     <foodmart-data-hsqldb.version>0.3</foodmart-data-hsqldb.version>
@@ -131,6 +132,7 @@ limitations under the License.
     <module>cassandra</module>
     <module>core</module>
     <module>druid</module>
+    <module>elasticsearch</module>
     <module>example</module>
     <module>linq4j</module>
     <module>mongodb</module>

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index b003e81..01a6995 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -30,6 +30,7 @@ presenting the data as tables within a schema.
 * [Cassandra adapter](cassandra_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/cassandra/package-summary.html">calcite-cassandra</a>)
 * CSV adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/csv/package-summary.html">example/csv</a>)
 * [Druid adapter](druid_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/druid/package-summary.html">calcite-druid</a>)
+* [Elasticsearch adapter](elasticsearch_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/elasticsearch/package-summary.html">calcite-elasticsearch</a>)
 * JDBC adapter (part of <a href="{{ site.apiRoot }}/org/apache/calcite/adapter/jdbc/package-summary.html">calcite-core</a>)
 * MongoDB adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/mongodb/package-summary.html">calcite-mongodb</a>)
 * Spark adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/spark/package-summary.html">calcite-spark</a>)

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/site/_docs/elasticsearch_adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/elasticsearch_adapter.md b/site/_docs/elasticsearch_adapter.md
new file mode 100644
index 0000000..d87d9e3
--- /dev/null
+++ b/site/_docs/elasticsearch_adapter.md
@@ -0,0 +1,136 @@
+---
+layout: docs
+title: Elasticsearch adapter
+permalink: /docs/elasticsearch_adapter.html
+---
+<!--
+{% comment %}
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+-->
+
+For instructions on downloading and building Calcite, start with the
+[tutorial]({{ site.baseurl }}/docs/tutorial.html).
+
+Once you've managed to compile the project, you can return here to
+start querying Elasticsearch with Calcite. First, we need a
+[model definition]({{ site.baseurl }}/docs/model.html).
+The model gives Calcite the necessary parameters to create an instance
+of the Elasticsearch adapter. The models can contain
+definitions of
+[materializations]({{ site.baseurl }}/docs/model.html#materialization).
+The name of the tables defined in the model definition corresponds to
+[types](https://www.elastic.co/blog/what-is-an-elasticsearch-index) in
+Elasticsearch. The schema/database is represented by the `index` parameter
+in the model definition.
+
+A basic example of a model file is given below:
+
+{% highlight json %}
+{
+  "version": "1.0",
+  "defaultSchema": "elasticsearch",
+  "schemas": [
+    {
+      "type": "custom",
+      "name": "elasticsearch",
+      "factory": "org.apache.calcite.adapter.elasticsearch.ElasticsearchSchemaFactory",
+      "operand": {
+        "coordinates": "{'127.0.0.1': 9300}",
+        "userConfig": "{'bulk.flush.max.actions': 10, 'bulk.flush.max.size.mb': 1}",
+        "index": "usa"
+      }
+    }
+  ]
+}
+{% endhighlight %}
+
+Assuming this file is stored as `model.json`, you can connect to
+Elasticsearch via [`sqlline`](https://github.com/julianhyde/sqlline) as
+follows:
+
+{% highlight bash %}
+$ ./sqlline
+sqlline> !connect jdbc:calcite:model=model.json admin admin
+{% endhighlight %}
+
+`sqlline` will now accept SQL queries which access your Elasticsearch types.
+The purpose of this adapter is to compile the query into the most efficient
+Elasticsearch SEARCH JSON possible by exploiting filtering and sorting directly
+in Elasticsearch where possible.
+
+For example, in the example dataset there is an Elasticsearch type
+named `zips` under index named `usa`.
+
+We can issue a simple query to fetch the names of all the states
+stored in the type `zips`. By default, Elasticsearch returns only 10 rows:
+
+{% highlight sql %}
+sqlline> SELECT * from "zips";
+{% endhighlight %}
+
+{% highlight json %}
+_MAP={pop=13367, loc=[-72.505565, 42.067203], city=EAST LONGMEADOW, id=01028, state=MA}
+_MAP={pop=1652, loc=[-72.908793, 42.070234], city=TOLLAND, id=01034, state=MA}
+_MAP={pop=3184, loc=[-72.616735, 42.38439], city=HATFIELD, id=01038, state=MA}
+_MAP={pop=43704, loc=[-72.626193, 42.202007], city=HOLYOKE, id=01040, state=MA}
+_MAP={pop=2084, loc=[-72.873341, 42.265301], city=HUNTINGTON, id=01050, state=MA}
+_MAP={pop=1350, loc=[-72.703403, 42.354292], city=LEEDS, id=01053, state=MA}
+_MAP={pop=8194, loc=[-72.319634, 42.101017], city=MONSON, id=01057, state=MA}
+_MAP={pop=1732, loc=[-72.204592, 42.062734], city=WALES, id=01081, state=MA}
+_MAP={pop=9808, loc=[-72.258285, 42.261831], city=WARE, id=01082, state=MA}
+_MAP={pop=4441, loc=[-72.203639, 42.20734], city=WEST WARREN, id=01092, state=MA}
+{% endhighlight %}
+
+While executing this query, the Elasticsearch adapter is able to recognize
+that `city` can be filtered by Elasticsearch and `state` can be sorted by
+Elasticsearch in ascending order.
+
+The final source json given to Elasticsearch is below:
+
+{% highlight json %}
+{
+  "query": {
+    "constant_score": {
+      "filter": {
+        "bool": {
+          "must": [
+            {
+              "term": {
+                "city": "springfield"
+              }
+            }
+          ]
+        }
+      }
+    }
+  },
+  "fields": [
+    "city",
+    "state"
+  ],
+  "script_fields": {},
+  "sort": [
+    {
+      "state": "asc"
+    }
+  ]
+}
+{% endhighlight %}
+
+This is the initial version of the Calcite Elasticsearch adapter.
+Work is in progress to introduce new features like aggregations into
+it.

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/sqlline
----------------------------------------------------------------------
diff --git a/sqlline b/sqlline
index e1b13c7..9e111a2 100755
--- a/sqlline
+++ b/sqlline
@@ -37,7 +37,7 @@ if [ ! -f target/fullclasspath.txt ]; then
 fi
 
 CP=
-for module in core avatica cassandra druid mongodb spark splunk example/csv example/function; do
+for module in core avatica cassandra druid elasticsearch mongodb spark splunk example/csv example/function; do
   CP=${CP}${module}/target/classes:
   CP=${CP}${module}/target/test-classes:
 done

http://git-wip-us.apache.org/repos/asf/calcite/blob/f3caf13b/sqlline.bat
----------------------------------------------------------------------
diff --git a/sqlline.bat b/sqlline.bat
index b9a4875..50e9701 100644
--- a/sqlline.bat
+++ b/sqlline.bat
@@ -23,6 +23,6 @@
 :: Copy dependency jars on first call. (To force jar refresh, remove target\dependencies)
 if not exist target\dependencies (call mvn -B dependency:copy-dependencies -DoverWriteReleases=false -DoverWriteSnapshots=false -DoverWriteIfNewer=true -DoutputDirectory=target\dependencies)
 
-java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;avatica\target\dependencies\*;cassandra\target\dependencies\*;mongodb\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
+java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;avatica\target\dependencies\*;cassandra\target\dependencies\*;elasticsearch\target\dependencies\*;mongodb\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
 
 :: End sqlline.bat


[07/14] calcite git commit: [CALCITE-1245] Allow RelBuilder.scan to take qualified table name (Chris Baynes)

Posted by el...@apache.org.
[CALCITE-1245] Allow RelBuilder.scan to take qualified table name (Chris Baynes)

Close apache/calcite#237


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

Branch: refs/heads/branch-avatica-1.8
Commit: b76affccec5756d7d1f6cf76cf4c2344a28fcfeb
Parents: f191a38
Author: Chris Baynes <bi...@gmail.com>
Authored: Tue May 24 15:04:41 2016 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed May 25 09:41:08 2016 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/tools/PigRelBuilder.java |  8 +++-
 .../org/apache/calcite/tools/RelBuilder.java    | 26 +++++++++---
 .../org/apache/calcite/test/RelBuilderTest.java | 42 ++++++++++++++++++++
 3 files changed, 68 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/b76affcc/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
index be62155..644525a 100644
--- a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
@@ -49,8 +49,12 @@ public class PigRelBuilder extends RelBuilder {
         relBuilder.relOptSchema);
   }
 
-  @Override public PigRelBuilder scan(String tableName) {
-    return (PigRelBuilder) super.scan(tableName);
+  @Override public PigRelBuilder scan(String... tableNames) {
+    return (PigRelBuilder) super.scan(tableNames);
+  }
+
+  @Override public PigRelBuilder scan(Iterable<String> tableNames) {
+    return (PigRelBuilder) super.scan(tableNames);
   }
 
   /** Loads a data set.

http://git-wip-us.apache.org/repos/asf/calcite/blob/b76affcc/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 414f1ca..acc9363 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -64,6 +64,7 @@ import org.apache.calcite.util.mapping.Mapping;
 import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.base.Function;
+import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -663,23 +664,36 @@ public class RelBuilder {
   /** Creates a {@link org.apache.calcite.rel.core.TableScan} of the table
    * with a given name.
    *
-   * <p>Throws if the table does not exist within the current schema.
+   * <p>Throws if the table does not exist.
    *
    * <p>Returns this builder.
    *
-   * @param tableName Name of table
+   * @param tableNames Name of table (can optionally be qualified)
    */
-  public RelBuilder scan(String tableName) {
-    final RelOptTable relOptTable =
-        relOptSchema.getTableForMember(ImmutableList.of(tableName));
+  public RelBuilder scan(Iterable<String> tableNames) {
+    final List<String> names = ImmutableList.copyOf(tableNames);
+    final RelOptTable relOptTable = relOptSchema.getTableForMember(names);
     if (relOptTable == null) {
-      throw Static.RESOURCE.tableNotFound(tableName).ex();
+      throw Static.RESOURCE.tableNotFound(Joiner.on(".").join(names)).ex();
     }
     final RelNode scan = scanFactory.createScan(cluster, relOptTable);
     push(scan);
     return this;
   }
 
+  /** Creates a {@link org.apache.calcite.rel.core.TableScan} of the table
+   * with a given name.
+   *
+   * <p>Throws if the table does not exist.
+   *
+   * <p>Returns this builder.
+   *
+   * @param tableNames Name of table (can optionally be qualified)
+   */
+  public RelBuilder scan(String... tableNames) {
+    return scan(ImmutableList.copyOf(tableNames));
+  }
+
   /** Creates a {@link org.apache.calcite.rel.core.Filter} of an array of
    * predicates.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/b76affcc/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 8a164c5..e6ea995 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -121,6 +121,18 @@ public class RelBuilderTest {
         is("LogicalTableScan(table=[[scott, EMP]])\n"));
   }
 
+  @Test public void testScanQualifiedTable() {
+    // Equivalent SQL:
+    //   SELECT *
+    //   FROM "scott"."emp"
+    final RelNode root =
+        RelBuilder.create(config().build())
+            .scan("scott", "EMP")
+            .build();
+    assertThat(str(root),
+        is("LogicalTableScan(table=[[scott, EMP]])\n"));
+  }
+
   @Test public void testScanInvalidTable() {
     // Equivalent SQL:
     //   SELECT *
@@ -136,6 +148,36 @@ public class RelBuilderTest {
     }
   }
 
+  @Test public void testScanInvalidSchema() {
+    // Equivalent SQL:
+    //   SELECT *
+    //   FROM "zzz"."emp"
+    try {
+      final RelNode root =
+          RelBuilder.create(config().build())
+              .scan("ZZZ", "EMP") // the table exists, but the schema does not
+              .build();
+      fail("expected error, got " + root);
+    } catch (Exception e) {
+      assertThat(e.getMessage(), is("Table 'ZZZ.EMP' not found"));
+    }
+  }
+
+  @Test public void testScanInvalidQualifiedTable() {
+    // Equivalent SQL:
+    //   SELECT *
+    //   FROM "scott"."zzz"
+    try {
+      final RelNode root =
+          RelBuilder.create(config().build())
+              .scan("scott", "ZZZ") // the schema is valid, but the table does not exist
+              .build();
+      fail("expected error, got " + root);
+    } catch (Exception e) {
+      assertThat(e.getMessage(), is("Table 'scott.ZZZ' not found"));
+    }
+  }
+
   @Test public void testScanValidTableWrongCase() {
     // Equivalent SQL:
     //   SELECT *


[04/14] calcite git commit: Move code from Enumerables to EnumerableDefaults

Posted by el...@apache.org.
Move code from Enumerables to EnumerableDefaults


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

Branch: refs/heads/branch-avatica-1.8
Commit: b0a996b4105c238cd8455741b2fb4b0ba5256a49
Parents: b6f0e10
Author: Julian Hyde <jh...@apache.org>
Authored: Tue May 17 18:14:11 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun May 22 12:46:46 2016 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/runtime/Enumerables.java | 285 +-----------------
 .../org/apache/calcite/util/BuiltInMethod.java  |  15 +-
 .../apache/calcite/runtime/EnumerablesTest.java |  29 +-
 .../calcite/linq4j/EnumerableDefaults.java      | 290 +++++++++++++++++--
 4 files changed, 296 insertions(+), 323 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/b0a996b4/core/src/main/java/org/apache/calcite/runtime/Enumerables.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/Enumerables.java b/core/src/main/java/org/apache/calcite/runtime/Enumerables.java
index 22a85c7..0eba4f1 100644
--- a/core/src/main/java/org/apache/calcite/runtime/Enumerables.java
+++ b/core/src/main/java/org/apache/calcite/runtime/Enumerables.java
@@ -17,31 +17,17 @@
 package org.apache.calcite.runtime;
 
 import org.apache.calcite.interpreter.Row;
-import org.apache.calcite.linq4j.AbstractEnumerable;
 import org.apache.calcite.linq4j.Enumerable;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.Linq4j;
-import org.apache.calcite.linq4j.function.EqualityComparer;
 import org.apache.calcite.linq4j.function.Function1;
-import org.apache.calcite.linq4j.function.Function2;
-import org.apache.calcite.linq4j.function.Predicate1;
-import org.apache.calcite.linq4j.function.Predicate2;
-import org.apache.calcite.util.Bug;
 
 import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import java.util.List;
-import java.util.Set;
 
 /**
  * Utilities for processing {@link org.apache.calcite.linq4j.Enumerable}
  * collections.
  *
  * <p>This class is a place to put things not yet added to linq4j.
- * Methods are subject to removal without notice.</p>
+ * Methods are subject to removal without notice.
  */
 public class Enumerables {
   private static final Function1<?, ?> SLICE =
@@ -67,137 +53,6 @@ public class Enumerables {
     return enumerable.select((Function1<E[], E>) SLICE);
   }
 
-   /**
-   * Returns elements of {@code outer} for which there is a member of
-   * {@code inner} with a matching key.
-   */
-  public static <TSource, TInner, TKey> Enumerable<TSource> semiJoin(
-      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
-      final Function1<TSource, TKey> outerKeySelector,
-      final Function1<TInner, TKey> innerKeySelector) {
-    Bug.upgrade("move into linq4j");
-    return semiJoin(outer, inner, outerKeySelector, innerKeySelector, null);
-  }
-
-  /**
-   * Returns elements of {@code outer} for which there is a member of
-   * {@code inner} with a matching key. A specified
-   * {@code EqualityComparer<TSource>} is used to compare keys.
-   */
-  public static <TSource, TInner, TKey> Enumerable<TSource> semiJoin(
-      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
-      final Function1<TSource, TKey> outerKeySelector,
-      final Function1<TInner, TKey> innerKeySelector,
-      final EqualityComparer<TKey> comparer) {
-    return new AbstractEnumerable<TSource>() {
-      public Enumerator<TSource> enumerator() {
-        final Enumerable<TKey> innerLookup =
-            comparer == null
-                ? inner.select(innerKeySelector).distinct()
-                : inner.select(innerKeySelector).distinct(comparer);
-
-        return Enumerables.where(outer.enumerator(),
-            new Predicate1<TSource>() {
-              public boolean apply(TSource v0) {
-                final TKey key = outerKeySelector.apply(v0);
-                return innerLookup.contains(key);
-              }
-            });
-      }
-    };
-  }
-
-  /**
-   * Correlates the elements of two sequences based on a predicate.
-   */
-  public static <TSource, TInner, TResult> Enumerable<TResult> thetaJoin(
-      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
-      final Predicate2<TSource, TInner> predicate,
-      Function2<TSource, TInner, TResult> resultSelector,
-      final boolean generateNullsOnLeft,
-      final boolean generateNullsOnRight) {
-    // Building the result as a list is easy but hogs memory. We should iterate.
-    final List<TResult> result = Lists.newArrayList();
-    final Enumerator<TSource> lefts = outer.enumerator();
-    final List<TInner> rightList = inner.toList();
-    final Set<TInner> rightUnmatched;
-    if (generateNullsOnLeft) {
-      rightUnmatched = Sets.newIdentityHashSet();
-      rightUnmatched.addAll(rightList);
-    } else {
-      rightUnmatched = null;
-    }
-    while (lefts.moveNext()) {
-      int leftMatchCount = 0;
-      final TSource left = lefts.current();
-      final Enumerator<TInner> rights = Linq4j.iterableEnumerator(rightList);
-      while (rights.moveNext()) {
-        TInner right = rights.current();
-        if (predicate.apply(left, right)) {
-          ++leftMatchCount;
-          if (rightUnmatched != null) {
-            rightUnmatched.remove(right);
-          }
-          result.add(resultSelector.apply(left, right));
-        }
-      }
-      if (generateNullsOnRight && leftMatchCount == 0) {
-        result.add(resultSelector.apply(left, null));
-      }
-    }
-    if (rightUnmatched != null) {
-      final Enumerator<TInner> rights =
-          Linq4j.iterableEnumerator(rightUnmatched);
-      while (rights.moveNext()) {
-        TInner right = rights.current();
-        result.add(resultSelector.apply(null, right));
-      }
-    }
-    return Linq4j.asEnumerable(result);
-  }
-
-  /**
-   * Filters a sequence of values based on a
-   * predicate.
-   */
-  public static <TSource> Enumerable<TSource> where(
-      final Enumerable<TSource> source, final Predicate1<TSource> predicate) {
-    assert predicate != null;
-    return new AbstractEnumerable<TSource>() {
-      public Enumerator<TSource> enumerator() {
-        final Enumerator<TSource> enumerator = source.enumerator();
-        return Enumerables.where(enumerator, predicate);
-      }
-    };
-  }
-
-  private static <TSource> Enumerator<TSource> where(
-      final Enumerator<TSource> enumerator,
-      final Predicate1<TSource> predicate) {
-    return new Enumerator<TSource>() {
-      public TSource current() {
-        return enumerator.current();
-      }
-
-      public boolean moveNext() {
-        while (enumerator.moveNext()) {
-          if (predicate.apply(enumerator.current())) {
-            return true;
-          }
-        }
-        return false;
-      }
-
-      public void reset() {
-        enumerator.reset();
-      }
-
-      public void close() {
-        enumerator.close();
-      }
-    };
-  }
-
   /** Converts an {@link Enumerable} over object arrays into an
    * {@link Enumerable} over {@link Row} objects. */
   public static Enumerable<Row> toRow(final Enumerable<Object[]> enumerable) {
@@ -215,144 +70,6 @@ public class Enumerables {
     };
   }
 
-  /** Joins two inputs that are sorted on the key. */
-  public static <TSource, TInner, TKey extends Comparable<TKey>, TResult>
-  Enumerable<TResult> mergeJoin(final Enumerable<TSource> outer,
-      final Enumerable<TInner> inner,
-      final Function1<TSource, TKey> outerKeySelector,
-      final Function1<TInner, TKey> innerKeySelector,
-      final Function2<TSource, TInner, TResult> resultSelector,
-      boolean generateNullsOnLeft,
-      boolean generateNullsOnRight) {
-    assert !generateNullsOnLeft : "not implemented";
-    assert !generateNullsOnRight : "not implemented";
-    return new AbstractEnumerable<TResult>() {
-      public Enumerator<TResult> enumerator() {
-        return new Enumerator<TResult>() {
-          final Enumerator<TSource> leftEnumerator = outer.enumerator();
-          final Enumerator<TInner> rightEnumerator = inner.enumerator();
-          final List<TSource> lefts = Lists.newArrayList();
-          final List<TInner> rights = Lists.newArrayList();
-          boolean done;
-          Enumerator<List<Object>> cartesians;
-
-          {
-            start();
-          }
-
-          private void start() {
-            if (!leftEnumerator.moveNext()
-                || !rightEnumerator.moveNext()
-                || !advance()) {
-              done = true;
-              cartesians = Linq4j.emptyEnumerator();
-            }
-          }
-
-          /** Moves to the next key that is present in both sides. Populates
-           * lefts and rights with the rows. Restarts the cross-join
-           * enumerator. */
-          private boolean advance() {
-            TSource left = leftEnumerator.current();
-            TKey leftKey = outerKeySelector.apply(left);
-            TInner right = rightEnumerator.current();
-            TKey rightKey = innerKeySelector.apply(right);
-            for (;;) {
-              int c = leftKey.compareTo(rightKey);
-              if (c == 0) {
-                break;
-              }
-              if (c < 0) {
-                if (!leftEnumerator.moveNext()) {
-                  done = true;
-                  return false;
-                }
-                left = leftEnumerator.current();
-                leftKey = outerKeySelector.apply(left);
-              } else {
-                if (!rightEnumerator.moveNext()) {
-                  done = true;
-                  return false;
-                }
-                right = rightEnumerator.current();
-                rightKey = innerKeySelector.apply(right);
-              }
-            }
-            lefts.clear();
-            lefts.add(left);
-            for (;;) {
-              if (!leftEnumerator.moveNext()) {
-                done = true;
-                break;
-              }
-              left = leftEnumerator.current();
-              TKey leftKey2 = outerKeySelector.apply(left);
-              int c = leftKey.compareTo(leftKey2);
-              if (c != 0) {
-                assert c < 0 : "not sorted";
-                break;
-              }
-              lefts.add(left);
-            }
-            rights.clear();
-            rights.add(right);
-            for (;;) {
-              if (!rightEnumerator.moveNext()) {
-                done = true;
-                break;
-              }
-              right = rightEnumerator.current();
-              TKey rightKey2 = innerKeySelector.apply(right);
-              int c = rightKey.compareTo(rightKey2);
-              if (c != 0) {
-                assert c < 0 : "not sorted";
-                break;
-              }
-              rights.add(right);
-            }
-            cartesians = Linq4j.product(
-                ImmutableList.of(Linq4j.<Object>enumerator(lefts),
-                    Linq4j.<Object>enumerator(rights)));
-            return true;
-          }
-
-          public TResult current() {
-            final List<Object> list = cartesians.current();
-            @SuppressWarnings("unchecked") final TSource left =
-                (TSource) list.get(0);
-            @SuppressWarnings("unchecked") final TInner right =
-                (TInner) list.get(1);
-            return resultSelector.apply(left, right);
-          }
-
-          public boolean moveNext() {
-            for (;;) {
-              if (cartesians.moveNext()) {
-                return true;
-              }
-              if (done) {
-                return false;
-              }
-              if (!advance()) {
-                return false;
-              }
-            }
-          }
-
-          public void reset() {
-            done = false;
-            leftEnumerator.reset();
-            rightEnumerator.reset();
-            start();
-          }
-
-          public void close() {
-          }
-        };
-      }
-    };
-  }
-
 }
 
 // End Enumerables.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/b0a996b4/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 94356a4..53f7880 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -135,14 +135,15 @@ public enum BuiltInMethod {
       String.class, Function1.class),
   JOIN(ExtendedEnumerable.class, "join", Enumerable.class, Function1.class,
       Function1.class, Function2.class),
-  MERGE_JOIN(Enumerables.class, "mergeJoin", Enumerable.class, Enumerable.class,
-      Function1.class, Function1.class, Function2.class, boolean.class,
-      boolean.class),
+  MERGE_JOIN(EnumerableDefaults.class, "mergeJoin", Enumerable.class,
+      Enumerable.class, Function1.class, Function1.class, Function2.class,
+      boolean.class, boolean.class),
   SLICE0(Enumerables.class, "slice0", Enumerable.class),
-  SEMI_JOIN(Enumerables.class, "semiJoin", Enumerable.class, Enumerable.class,
-      Function1.class, Function1.class),
-  THETA_JOIN(Enumerables.class, "thetaJoin", Enumerable.class, Enumerable.class,
-      Predicate2.class, Function2.class, boolean.class, boolean.class),
+  SEMI_JOIN(EnumerableDefaults.class, "semiJoin", Enumerable.class,
+      Enumerable.class, Function1.class, Function1.class),
+  THETA_JOIN(EnumerableDefaults.class, "thetaJoin", Enumerable.class,
+      Enumerable.class, Predicate2.class, Function2.class, boolean.class,
+      boolean.class),
   CORRELATE_JOIN(ExtendedEnumerable.class, "correlateJoin",
       CorrelateJoinType.class, Function1.class, Function2.class),
   SELECT(ExtendedEnumerable.class, "select", Function1.class),

http://git-wip-us.apache.org/repos/asf/calcite/blob/b0a996b4/core/src/test/java/org/apache/calcite/runtime/EnumerablesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/runtime/EnumerablesTest.java b/core/src/test/java/org/apache/calcite/runtime/EnumerablesTest.java
index 33a2593..5e76578 100644
--- a/core/src/test/java/org/apache/calcite/runtime/EnumerablesTest.java
+++ b/core/src/test/java/org/apache/calcite/runtime/EnumerablesTest.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.runtime;
 
 import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.EnumerableDefaults;
 import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.Function2;
@@ -69,7 +70,7 @@ public class EnumerablesTest {
 
   @Test public void testSemiJoin() {
     assertThat(
-        Enumerables.semiJoin(EMPS, DEPTS,
+        EnumerableDefaults.semiJoin(EMPS, DEPTS,
             new Function1<Emp, Integer>() {
               public Integer apply(Emp a0) {
                 return a0.deptno;
@@ -86,7 +87,7 @@ public class EnumerablesTest {
 
   @Test public void testMergeJoin() {
     assertThat(
-        Enumerables.mergeJoin(
+        EnumerableDefaults.mergeJoin(
             Linq4j.asEnumerable(
                 Arrays.asList(
                     new Emp(10, "Fred"),
@@ -171,7 +172,7 @@ public class EnumerablesTest {
 
   private static <T extends Comparable<T>> Enumerable<T> intersect(
       List<T> list0, List<T> list1) {
-    return Enumerables.mergeJoin(
+    return EnumerableDefaults.mergeJoin(
         Linq4j.asEnumerable(list0),
         Linq4j.asEnumerable(list1),
         Functions.<T>identitySelector(),
@@ -185,31 +186,31 @@ public class EnumerablesTest {
 
   @Test public void testThetaJoin() {
     assertThat(
-        Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING,
-            false, false).toList().toString(),
+        EnumerableDefaults.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO,
+            EMP_DEPT_TO_STRING, false, false).toList().toString(),
         equalTo("[{Theodore, 20, 20, Sales}, {Sebastian, 20, 20, Sales}]"));
   }
 
   @Test public void testThetaLeftJoin() {
     assertThat(
-        Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING,
-            false, true).toList().toString(),
+        EnumerableDefaults.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO,
+            EMP_DEPT_TO_STRING, false, true).toList().toString(),
         equalTo("[{Fred, 10, null, null}, {Theodore, 20, 20, Sales}, "
             + "{Sebastian, 20, 20, Sales}, {Joe, 30, null, null}]"));
   }
 
   @Test public void testThetaRightJoin() {
     assertThat(
-        Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING,
-            true, false).toList().toString(),
+        EnumerableDefaults.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO,
+            EMP_DEPT_TO_STRING, true, false).toList().toString(),
         equalTo("[{Theodore, 20, 20, Sales}, {Sebastian, 20, 20, Sales}, "
             + "{null, null, 15, Marketing}]"));
   }
 
   @Test public void testThetaFullJoin() {
     assertThat(
-        Enumerables.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO, EMP_DEPT_TO_STRING,
-            true, true).toList().toString(),
+        EnumerableDefaults.thetaJoin(EMPS, DEPTS, EQUAL_DEPTNO,
+            EMP_DEPT_TO_STRING, true, true).toList().toString(),
         equalTo("[{Fred, 10, null, null}, {Theodore, 20, 20, Sales}, "
             + "{Sebastian, 20, 20, Sales}, {Joe, 30, null, null}, "
             + "{null, null, 15, Marketing}]"));
@@ -217,7 +218,7 @@ public class EnumerablesTest {
 
   @Test public void testThetaFullJoinLeftEmpty() {
     assertThat(
-        Enumerables.thetaJoin(EMPS.take(0), DEPTS, EQUAL_DEPTNO,
+        EnumerableDefaults.thetaJoin(EMPS.take(0), DEPTS, EQUAL_DEPTNO,
             EMP_DEPT_TO_STRING, true, true)
             .orderBy(Functions.<String>identitySelector()).toList().toString(),
         equalTo("[{null, null, 15, Marketing}, {null, null, 20, Sales}]"));
@@ -225,7 +226,7 @@ public class EnumerablesTest {
 
   @Test public void testThetaFullJoinRightEmpty() {
     assertThat(
-        Enumerables.thetaJoin(EMPS, DEPTS.take(0), EQUAL_DEPTNO,
+        EnumerableDefaults.thetaJoin(EMPS, DEPTS.take(0), EQUAL_DEPTNO,
             EMP_DEPT_TO_STRING, true, true).toList().toString(),
         equalTo("[{Fred, 10, null, null}, {Theodore, 20, null, null}, "
             + "{Sebastian, 20, null, null}, {Joe, 30, null, null}]"));
@@ -233,7 +234,7 @@ public class EnumerablesTest {
 
   @Test public void testThetaFullJoinBothEmpty() {
     assertThat(
-        Enumerables.thetaJoin(EMPS.take(0), DEPTS.take(0), EQUAL_DEPTNO,
+        EnumerableDefaults.thetaJoin(EMPS.take(0), DEPTS.take(0), EQUAL_DEPTNO,
             EMP_DEPT_TO_STRING, true, true).toList().toString(),
         equalTo("[]"));
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/b0a996b4/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
----------------------------------------------------------------------
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
index 7024a86..dc3ef03 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
@@ -34,6 +34,10 @@ import org.apache.calcite.linq4j.function.NullableLongFunction1;
 import org.apache.calcite.linq4j.function.Predicate1;
 import org.apache.calcite.linq4j.function.Predicate2;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
 import java.math.BigDecimal;
 import java.util.AbstractList;
 import java.util.AbstractMap;
@@ -1292,6 +1296,114 @@ public abstract class EnumerableDefaults {
   }
 
   /**
+   * Returns elements of {@code outer} for which there is a member of
+   * {@code inner} with a matching key.
+   */
+  public static <TSource, TInner, TKey> Enumerable<TSource> semiJoin(
+      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
+      final Function1<TSource, TKey> outerKeySelector,
+      final Function1<TInner, TKey> innerKeySelector) {
+    return semiJoin(outer, inner, outerKeySelector, innerKeySelector, null);
+  }
+
+  /**
+   * Returns elements of {@code outer} for which there is a member of
+   * {@code inner} with a matching key. A specified
+   * {@code EqualityComparer<TSource>} is used to compare keys.
+   */
+  public static <TSource, TInner, TKey> Enumerable<TSource> semiJoin(
+      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
+      final Function1<TSource, TKey> outerKeySelector,
+      final Function1<TInner, TKey> innerKeySelector,
+      final EqualityComparer<TKey> comparer) {
+    return new AbstractEnumerable<TSource>() {
+      public Enumerator<TSource> enumerator() {
+        final Enumerable<TKey> innerLookup =
+            comparer == null
+                ? inner.select(innerKeySelector).distinct()
+                : inner.select(innerKeySelector).distinct(comparer);
+
+        return EnumerableDefaults.where(outer.enumerator(),
+            new Predicate1<TSource>() {
+              public boolean apply(TSource v0) {
+                final TKey key = outerKeySelector.apply(v0);
+                return innerLookup.contains(key);
+              }
+            });
+      }
+    };
+  }
+
+  /**
+   * Correlates the elements of two sequences based on a predicate.
+   */
+  public static <TSource, TInner, TResult> Enumerable<TResult> thetaJoin(
+      final Enumerable<TSource> outer, final Enumerable<TInner> inner,
+      final Predicate2<TSource, TInner> predicate,
+      Function2<TSource, TInner, TResult> resultSelector,
+      final boolean generateNullsOnLeft,
+      final boolean generateNullsOnRight) {
+    // Building the result as a list is easy but hogs memory. We should iterate.
+    final List<TResult> result = Lists.newArrayList();
+    final Enumerator<TSource> lefts = outer.enumerator();
+    final List<TInner> rightList = inner.toList();
+    final Set<TInner> rightUnmatched;
+    if (generateNullsOnLeft) {
+      rightUnmatched = Sets.newIdentityHashSet();
+      rightUnmatched.addAll(rightList);
+    } else {
+      rightUnmatched = null;
+    }
+    while (lefts.moveNext()) {
+      int leftMatchCount = 0;
+      final TSource left = lefts.current();
+      final Enumerator<TInner> rights = Linq4j.iterableEnumerator(rightList);
+      while (rights.moveNext()) {
+        TInner right = rights.current();
+        if (predicate.apply(left, right)) {
+          ++leftMatchCount;
+          if (rightUnmatched != null) {
+            rightUnmatched.remove(right);
+          }
+          result.add(resultSelector.apply(left, right));
+        }
+      }
+      if (generateNullsOnRight && leftMatchCount == 0) {
+        result.add(resultSelector.apply(left, null));
+      }
+    }
+    if (rightUnmatched != null) {
+      final Enumerator<TInner> rights =
+          Linq4j.iterableEnumerator(rightUnmatched);
+      while (rights.moveNext()) {
+        TInner right = rights.current();
+        result.add(resultSelector.apply(null, right));
+      }
+    }
+    return Linq4j.asEnumerable(result);
+  }
+
+  /** Joins two inputs that are sorted on the key. */
+  public static <TSource, TInner, TKey extends Comparable<TKey>, TResult>
+  Enumerable<TResult> mergeJoin(final Enumerable<TSource> outer,
+      final Enumerable<TInner> inner,
+      final Function1<TSource, TKey> outerKeySelector,
+      final Function1<TInner, TKey> innerKeySelector,
+      final Function2<TSource, TInner, TResult> resultSelector,
+      boolean generateNullsOnLeft,
+      boolean generateNullsOnRight) {
+    assert !generateNullsOnLeft : "not implemented";
+    assert !generateNullsOnRight : "not implemented";
+    return new AbstractEnumerable<TResult>() {
+      public Enumerator<TResult> enumerator() {
+        return new MergeJoinEnumerator<>(outer.enumerator(),
+            inner.enumerator(), outerKeySelector, innerKeySelector,
+            resultSelector);
+      }
+    };
+  }
+
+  /**
    * Returns the last element of a sequence that
    * satisfies a specified condition.
    */
@@ -2675,28 +2787,34 @@ public abstract class EnumerableDefaults {
     return new AbstractEnumerable<TSource>() {
       public Enumerator<TSource> enumerator() {
         final Enumerator<TSource> enumerator = source.enumerator();
-        return new Enumerator<TSource>() {
-          public TSource current() {
-            return enumerator.current();
-          }
+        return EnumerableDefaults.where(enumerator, predicate);
+      }
+    };
+  }
 
-          public boolean moveNext() {
-            while (enumerator.moveNext()) {
-              if (predicate.apply(enumerator.current())) {
-                return true;
-              }
-            }
-            return false;
-          }
+  private static <TSource> Enumerator<TSource> where(
+      final Enumerator<TSource> enumerator,
+      final Predicate1<TSource> predicate) {
+    return new Enumerator<TSource>() {
+      public TSource current() {
+        return enumerator.current();
+      }
 
-          public void reset() {
-            enumerator.reset();
+      public boolean moveNext() {
+        while (enumerator.moveNext()) {
+          if (predicate.apply(enumerator.current())) {
+            return true;
           }
+        }
+        return false;
+      }
 
-          public void close() {
-            enumerator.close();
-          }
-        };
+      public void reset() {
+        enumerator.reset();
+      }
+
+      public void close() {
+        enumerator.close();
       }
     };
   }
@@ -3075,6 +3193,142 @@ public abstract class EnumerableDefaults {
       };
     }
   }
+
+  /** Enumerator that performs a merge join on its sorted inputs. */
+  private static class MergeJoinEnumerator<TResult, TSource, TInner, TKey extends Comparable<TKey>>
+      implements Enumerator<TResult> {
+    final List<TSource> lefts = new ArrayList<>();
+    final List<TInner> rights = new ArrayList<>();
+    private final Enumerator<TSource> leftEnumerator;
+    private final Enumerator<TInner> rightEnumerator;
+    private final Function1<TSource, TKey> outerKeySelector;
+    private final Function1<TInner, TKey> innerKeySelector;
+    private final Function2<TSource, TInner, TResult> resultSelector;
+    boolean done;
+    Enumerator<List<Object>> cartesians;
+
+    MergeJoinEnumerator(Enumerator<TSource> leftEnumerator,
+        Enumerator<TInner> rightEnumerator,
+        Function1<TSource, TKey> outerKeySelector,
+        Function1<TInner, TKey> innerKeySelector,
+        Function2<TSource, TInner, TResult> resultSelector) {
+      this.leftEnumerator = leftEnumerator;
+      this.rightEnumerator = rightEnumerator;
+      this.outerKeySelector = outerKeySelector;
+      this.innerKeySelector = innerKeySelector;
+      this.resultSelector = resultSelector;
+      start();
+    }
+
+    private void start() {
+      if (!leftEnumerator.moveNext()
+          || !rightEnumerator.moveNext()
+          || !advance()) {
+        done = true;
+        cartesians = Linq4j.emptyEnumerator();
+      }
+    }
+
+    /** Moves to the next key that is present in both sides. Populates
+     * lefts and rights with the rows. Restarts the cross-join
+     * enumerator. */
+    private boolean advance() {
+      TSource left = leftEnumerator.current();
+      TKey leftKey = outerKeySelector.apply(left);
+      TInner right = rightEnumerator.current();
+      TKey rightKey = innerKeySelector.apply(right);
+      for (;;) {
+        int c = leftKey.compareTo(rightKey);
+        if (c == 0) {
+          break;
+        }
+        if (c < 0) {
+          if (!leftEnumerator.moveNext()) {
+            done = true;
+            return false;
+          }
+          left = leftEnumerator.current();
+          leftKey = outerKeySelector.apply(left);
+        } else {
+          if (!rightEnumerator.moveNext()) {
+            done = true;
+            return false;
+          }
+          right = rightEnumerator.current();
+          rightKey = innerKeySelector.apply(right);
+        }
+      }
+      lefts.clear();
+      lefts.add(left);
+      for (;;) {
+        if (!leftEnumerator.moveNext()) {
+          done = true;
+          break;
+        }
+        left = leftEnumerator.current();
+        TKey leftKey2 = outerKeySelector.apply(left);
+        int c = leftKey.compareTo(leftKey2);
+        if (c != 0) {
+          assert c < 0 : "not sorted";
+          break;
+        }
+        lefts.add(left);
+      }
+      rights.clear();
+      rights.add(right);
+      for (;;) {
+        if (!rightEnumerator.moveNext()) {
+          done = true;
+          break;
+        }
+        right = rightEnumerator.current();
+        TKey rightKey2 = innerKeySelector.apply(right);
+        int c = rightKey.compareTo(rightKey2);
+        if (c != 0) {
+          assert c < 0 : "not sorted";
+          break;
+        }
+        rights.add(right);
+      }
+      cartesians = Linq4j.product(
+          ImmutableList.of(Linq4j.<Object>enumerator(lefts),
+              Linq4j.<Object>enumerator(rights)));
+      return true;
+    }
+
+    public TResult current() {
+      final List<Object> list = cartesians.current();
+      @SuppressWarnings("unchecked") final TSource left =
+          (TSource) list.get(0);
+      @SuppressWarnings("unchecked") final TInner right =
+          (TInner) list.get(1);
+      return resultSelector.apply(left, right);
+    }
+
+    public boolean moveNext() {
+      for (;;) {
+        if (cartesians.moveNext()) {
+          return true;
+        }
+        if (done) {
+          return false;
+        }
+        if (!advance()) {
+          return false;
+        }
+      }
+    }
+
+    public void reset() {
+      done = false;
+      leftEnumerator.reset();
+      rightEnumerator.reset();
+      start();
+    }
+
+    public void close() {
+    }
+  }
 }
 
 // End EnumerableDefaults.java


[06/14] calcite git commit: Following [CALCITE-1241], create a table of reserved keywords from various versions of the SQL standard

Posted by el...@apache.org.
Following [CALCITE-1241], create a table of reserved keywords from various versions of the SQL standard


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

Branch: refs/heads/branch-avatica-1.8
Commit: 5a697eedf1a6d32fe4b6e504dc0454429949d423
Parents: 20ba434
Author: Julian Hyde <jh...@apache.org>
Authored: Fri May 20 17:48:32 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun May 22 12:46:46 2016 -0700

----------------------------------------------------------------------
 .../calcite/sql/parser/SqlParserTest.java       | 779 +++++++++++--------
 1 file changed, 466 insertions(+), 313 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/5a697eed/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index f8bb2b3..a66f12a 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -31,6 +31,7 @@ import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
 
 import org.junit.Ignore;
 import org.junit.Test;
@@ -42,11 +43,12 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
@@ -63,317 +65,437 @@ public class SqlParserTest {
   //~ Static fields/initializers ---------------------------------------------
 
   /**
-   * List of reserved keywords in parser. If a new <b>reserved</b> keyword is added to the
-   * parser, it must be included in this list. If the keyword is not intended to be a
-   * reserved keyword, add it to the non-reserved keyword list in the parser.
+   * List of reserved keywords.
+   *
+   * <p>Each keyword is followed by tokens indicating whether it is reserved in
+   * the SQL:92, SQL:99, SQL:2003 standards and in Calcite.
+   *
+   * <p>The standard keywords are derived from
+   * <a href="http://developer.mimer.com/validator/sql-reserved-words.tml">Mimer</a>.
+   *
+   * <p>If a new <b>reserved</b> keyword is added to the parser, include it in
+   * this list, flagged "c". If the keyword is not intended to be a reserved
+   * keyword, add it to the non-reserved keyword list in the parser.
    */
   private static final List<String> RESERVED_KEYWORDS = ImmutableList.of(
-      "ABS",
-      "ALL",
-      "ALLOCATE",
-      "ALLOW",
-      "ALTER",
-      "AND",
-      "ANY",
-      "ARE",
-      "ARRAY",
-      "AS",
-      "ASENSITIVE",
-      "ASYMMETRIC",
-      "AT",
-      "ATOMIC",
-      "AUTHORIZATION",
-      "AVG",
-      "BEGIN",
-      "BETWEEN",
-      "BIGINT",
-      "BINARY",
-      "BIT",
-      "BLOB",
-      "BOOLEAN",
-      "BOTH",
-      "BY",
-      "CALL",
-      "CALLED",
-      "CARDINALITY",
-      "CASCADED",
-      "CASE",
-      "CAST",
-      "CEIL",
-      "CEILING",
-      "CHAR",
-      "CHARACTER",
-      "CHARACTER_LENGTH",
-      "CHAR_LENGTH",
-      "CHECK",
-      "CLOB",
-      "CLOSE",
-      "COALESCE",
-      "COLLATE",
-      "COLLECT",
-      "COLUMN",
-      "COMMIT",
-      "CONDITION",
-      "CONNECT",
-      "CONSTRAINT",
-      "CONVERT",
-      "CORR",
-      "CORRESPONDING",
-      "COUNT",
-      "COVAR_POP",
-      "COVAR_SAMP",
-      "CREATE",
-      "CROSS",
-      "CUBE",
-      "CUME_DIST",
-      "CURRENT",
-      "CURRENT_CATALOG",
-      "CURRENT_DATE",
-      "CURRENT_DEFAULT_TRANSFORM_GROUP",
-      "CURRENT_PATH",
-      "CURRENT_ROLE",
-      "CURRENT_SCHEMA",
-      "CURRENT_TIME",
-      "CURRENT_TIMESTAMP",
-      "CURRENT_TRANSFORM_GROUP_FOR_TYPE",
-      "CURRENT_USER",
-      "CURSOR",
-      "CYCLE",
-      "DATE",
-      "DAY",
-      "DEALLOCATE",
-      "DEC",
-      "DECIMAL",
-      "DECLARE",
-      "DEFAULT",
-      "DELETE",
-      "DENSE_RANK",
-      "DEREF",
-      "DESCRIBE",
-      "DETERMINISTIC",
-      "DISALLOW",
-      "DISCONNECT",
-      "DISTINCT",
-      "DOUBLE",
-      "DROP",
-      "DYNAMIC",
-      "EACH",
-      "ELEMENT",
-      "ELSE",
-      "END",
-      "END-EXEC",
-      "ESCAPE",
-      "EVERY",
-      "EXCEPT",
-      "EXEC",
-      "EXECUTE",
-      "EXISTS",
-      "EXP",
-      "EXPLAIN",
-      "EXTEND",
-      "EXTERNAL",
-      "EXTRACT",
-      "FALSE",
-      "FETCH",
-      "FILTER",
-      "FIRST_VALUE",
-      "FLOAT",
-      "FLOOR",
-      "FOR",
-      "FOREIGN",
-      "FREE",
-      "FROM",
-      "FULL",
-      "FUNCTION",
-      "FUSION",
-      "GET",
-      "GLOBAL",
-      "GRANT",
-      "GROUP",
-      "GROUPING",
-      "HAVING",
-      "HOLD",
-      "HOUR",
-      "IDENTITY",
-      "IMPORT",
-      "IN",
-      "INDICATOR",
-      "INNER",
-      "INOUT",
-      "INSENSITIVE",
-      "INSERT",
-      "INT",
-      "INTEGER",
-      "INTERSECT",
-      "INTERSECTION",
-      "INTERVAL",
-      "INTO",
-      "IS",
-      "JOIN",
-      "LANGUAGE",
-      "LARGE",
-      "LAST_VALUE",
-      "LATERAL",
-      "LEADING",
-      "LEFT",
-      "LIKE",
-      "LIMIT",
-      "LN",
-      "LOCAL",
-      "LOCALTIME",
-      "LOCALTIMESTAMP",
-      "LOWER",
-      "MATCH",
-      "MAX",
-      "MEMBER",
-      "MERGE",
-      "METHOD",
-      "MIN",
-      "MINUTE",
-      "MOD",
-      "MODIFIES",
-      "MODULE",
-      "MONTH",
-      "MULTISET",
-      "NATIONAL",
-      "NATURAL",
-      "NCHAR",
-      "NCLOB",
-      "NEW",
-      "NEXT",
-      "NO",
-      "NONE",
-      "NORMALIZE",
-      "NOT",
-      "NULL",
-      "NULLIF",
-      "NUMERIC",
-      "OCTET_LENGTH",
-      "OF",
-      "OFFSET",
-      "OLD",
-      "ON",
-      "ONLY",
-      "OPEN",
-      "OR",
-      "ORDER",
-      "OUT",
-      "OUTER",
-      "OVER",
-      "OVERLAPS",
-      "OVERLAY",
-      "PARAMETER",
-      "PARTITION",
-      "PERCENTILE_CONT",
-      "PERCENTILE_DISC",
-      "PERCENT_RANK",
-      "POSITION",
-      "POWER",
-      "PRECISION",
-      "PREPARE",
-      "PRIMARY",
-      "PROCEDURE",
-      "RANGE",
-      "RANK",
-      "READS",
-      "REAL",
-      "RECURSIVE",
-      "REF",
-      "REFERENCES",
-      "REFERENCING",
-      "REGR_AVGX",
-      "REGR_AVGY",
-      "REGR_COUNT",
-      "REGR_INTERCEPT",
-      "REGR_R2",
-      "REGR_SLOPE",
-      "REGR_SXX",
-      "REGR_SXY",
-      "REGR_SYY",
-      "RELEASE",
-      "RESET",
-      "RESULT",
-      "RETURN",
-      "RETURNS",
-      "REVOKE",
-      "RIGHT",
-      "ROLLBACK",
-      "ROLLUP",
-      "ROW",
-      "ROWS",
-      "ROW_NUMBER",
-      "SAVEPOINT",
-      "SCOPE",
-      "SCROLL",
-      "SEARCH",
-      "SECOND",
-      "SELECT",
-      "SENSITIVE",
-      "SESSION_USER",
-      "SET",
-      "SIMILAR",
-      "SMALLINT",
-      "SOME",
-      "SPECIFIC",
-      "SPECIFICTYPE",
-      "SQL",
-      "SQLEXCEPTION",
-      "SQLSTATE",
-      "SQLWARNING",
-      "SQRT",
-      "START",
-      "STATIC",
-      "STDDEV_POP",
-      "STDDEV_SAMP",
-      "STREAM",
-      "SUBMULTISET",
-      "SUBSTRING",
-      "SUM",
-      "SYMMETRIC",
-      "SYSTEM",
-      "SYSTEM_USER",
-      "TABLE",
-      "TABLESAMPLE",
-      "THEN",
-      "TIME",
-      "TIMESTAMP",
-      "TIMEZONE_HOUR",
-      "TIMEZONE_MINUTE",
-      "TINYINT",
-      "TO",
-      "TRAILING",
-      "TRANSLATE",
-      "TRANSLATION",
-      "TREAT",
-      "TRIGGER",
-      "TRIM",
-      "TRUE",
-      "UESCAPE",
-      "UNION",
-      "UNIQUE",
-      "UNKNOWN",
-      "UNNEST",
-      "UPDATE",
-      "UPPER",
-      "UPSERT",
-      "USER",
-      "USING",
-      "VALUE",
-      "VALUES",
-      "VARBINARY",
-      "VARCHAR",
-      "VARYING",
-      "VAR_POP",
-      "VAR_SAMP",
-      "WHEN",
-      "WHENEVER",
-      "WHERE",
-      "WIDTH_BUCKET",
-      "WINDOW",
-      "WITH",
-      "WITHIN",
-      "WITHOUT",
-      "YEAR");
+      "ABS",                                                    "2011", "c",
+      "ABSOLUTE",                           "92", "99",
+      "ACTION",                             "92", "99",
+      "ADD",                                "92", "99", "2003",
+      "AFTER",                                    "99",
+      "ALL",                                "92", "99", "2003", "2011", "c",
+      "ALLOCATE",                           "92", "99", "2003", "2011", "c",
+      "ALLOW",                                                          "c",
+      "ALTER",                              "92", "99", "2003", "2011", "c",
+      "AND",                                "92", "99", "2003", "2011", "c",
+      "ANY",                                "92", "99", "2003", "2011", "c",
+      "ARE",                                "92", "99", "2003", "2011", "c",
+      "ARRAY",                                    "99", "2003", "2011", "c",
+      "ARRAY_AGG",                                              "2011",
+      "AS",                                 "92", "99", "2003", "2011", "c",
+      "ASC",                                "92", "99",
+      "ASENSITIVE",                               "99", "2003", "2011", "c",
+      "ASSERTION",                          "92", "99",
+      "ASYMMETRIC",                               "99", "2003", "2011", "c",
+      "AT",                                 "92", "99", "2003", "2011", "c",
+      "ATOMIC",                                   "99", "2003", "2011", "c",
+      "AUTHORIZATION",                      "92", "99", "2003", "2011", "c",
+      "AVG",                                "92",               "2011", "c",
+      "BEFORE",                                   "99",
+      "BEGIN",                              "92", "99", "2003", "2011", "c",
+      "BETWEEN",                            "92", "99", "2003", "2011", "c",
+      "BIGINT",                                         "2003", "2011", "c",
+      "BINARY",                                   "99", "2003", "2011", "c",
+      "BIT",                                "92", "99",                 "c",
+      "BIT_LENGTH",                         "92",
+      "BLOB",                                     "99", "2003", "2011", "c",
+      "BOOLEAN",                                  "99", "2003", "2011", "c",
+      "BOTH",                               "92", "99", "2003", "2011", "c",
+      "BREADTH",                                  "99",
+      "BY",                                 "92", "99", "2003", "2011", "c",
+      "CALL",                               "92", "99", "2003", "2011", "c",
+      "CALLED",                                         "2003", "2011", "c",
+      "CARDINALITY",                                            "2011", "c",
+      "CASCADE",                            "92", "99",
+      "CASCADED",                           "92", "99", "2003", "2011", "c",
+      "CASE",                               "92", "99", "2003", "2011", "c",
+      "CAST",                               "92", "99", "2003", "2011", "c",
+      "CATALOG",                            "92", "99",
+      "CEIL",                                                   "2011", "c",
+      "CEILING",                                                "2011", "c",
+      "CHAR",                               "92", "99", "2003", "2011", "c",
+      "CHARACTER",                          "92", "99", "2003", "2011", "c",
+      "CHARACTER_LENGTH",                   "92",               "2011", "c",
+      "CHAR_LENGTH",                        "92",               "2011", "c",
+      "CHECK",                              "92", "99", "2003", "2011", "c",
+      "CLOB",                                     "99", "2003", "2011", "c",
+      "CLOSE",                              "92", "99", "2003", "2011", "c",
+      "COALESCE",                           "92",               "2011", "c",
+      "COLLATE",                            "92", "99", "2003", "2011", "c",
+      "COLLATION",                          "92", "99",
+      "COLLECT",                                                "2011", "c",
+      "COLUMN",                             "92", "99", "2003", "2011", "c",
+      "COMMIT",                             "92", "99", "2003", "2011", "c",
+      "CONDITION",                          "92", "99", "2003", "2011", "c",
+      "CONNECT",                            "92", "99", "2003", "2011", "c",
+      "CONNECTION",                         "92", "99",
+      "CONSTRAINT",                         "92", "99", "2003", "2011", "c",
+      "CONSTRAINTS",                        "92", "99",
+      "CONSTRUCTOR",                              "99",
+      "CONTAINS",                           "92",
+      "CONTINUE",                           "92", "99", "2003",
+      "CONVERT",                            "92",               "2011", "c",
+      "CORR",                                                   "2011", "c",
+      "CORRESPONDING",                      "92", "99", "2003", "2011", "c",
+      "COUNT",                              "92",               "2011", "c",
+      "COVAR_POP",                                              "2011", "c",
+      "COVAR_SAMP",                                             "2011", "c",
+      "CREATE",                             "92", "99", "2003", "2011", "c",
+      "CROSS",                              "92", "99", "2003", "2011", "c",
+      "CUBE",                                     "99", "2003", "2011", "c",
+      "CUME_DIST",                                              "2011", "c",
+      "CURRENT",                            "92", "99", "2003", "2011", "c",
+      "CURRENT_CATALOG",                                        "2011", "c",
+      "CURRENT_DATE",                       "92", "99", "2003", "2011", "c",
+      "CURRENT_DEFAULT_TRANSFORM_GROUP",          "99", "2003", "2011", "c",
+      "CURRENT_PATH",                       "92", "99", "2003", "2011", "c",
+      "CURRENT_ROLE",                             "99", "2003", "2011", "c",
+      "CURRENT_SCHEMA",                                         "2011", "c",
+      "CURRENT_TIME",                       "92", "99", "2003", "2011", "c",
+      "CURRENT_TIMESTAMP",                  "92", "99", "2003", "2011", "c",
+      "CURRENT_TRANSFORM_GROUP_FOR_TYPE",         "99", "2003", "2011", "c",
+      "CURRENT_USER",                       "92", "99", "2003", "2011", "c",
+      "CURSOR",                             "92", "99", "2003", "2011", "c",
+      "CYCLE",                                    "99", "2003", "2011", "c",
+      "DATA",                                     "99",
+      "DATE",                               "92", "99", "2003", "2011", "c",
+      "DAY",                                "92", "99", "2003", "2011", "c",
+      "DAYS",                                                   "2011",
+      "DEALLOCATE",                         "92", "99", "2003", "2011", "c",
+      "DEC",                                "92", "99", "2003", "2011", "c",
+      "DECIMAL",                            "92", "99", "2003", "2011", "c",
+      "DECLARE",                            "92", "99", "2003", "2011", "c",
+      "DEFAULT",                            "92", "99", "2003", "2011", "c",
+      "DEFERRABLE",                         "92", "99",
+      "DEFERRED",                           "92", "99",
+      "DELETE",                             "92", "99", "2003", "2011", "c",
+      "DENSE_RANK",                                             "2011", "c",
+      "DEPTH",                                    "99",
+      "DEREF",                                    "99", "2003", "2011", "c",
+      "DESC",                               "92", "99",
+      "DESCRIBE",                           "92", "99", "2003", "2011", "c",
+      "DESCRIPTOR",                         "92", "99",
+      "DETERMINISTIC",                      "92", "99", "2003", "2011", "c",
+      "DIAGNOSTICS",                        "92", "99",
+      "DISALLOW",                                                       "c",
+      "DISCONNECT",                         "92", "99", "2003", "2011", "c",
+      "DISTINCT",                           "92", "99", "2003", "2011", "c",
+      "DO",                                 "92", "99", "2003",
+      "DOMAIN",                             "92", "99",
+      "DOUBLE",                             "92", "99", "2003", "2011", "c",
+      "DROP",                               "92", "99", "2003", "2011", "c",
+      "DYNAMIC",                                  "99", "2003", "2011", "c",
+      "EACH",                                     "99", "2003", "2011", "c",
+      "ELEMENT",                                        "2003", "2011", "c",
+      "ELSE",                               "92", "99", "2003", "2011", "c",
+      "ELSEIF",                             "92", "99", "2003",
+      "END",                                "92", "99", "2003", "2011", "c",
+      "END-EXEC",                                               "2011", "c",
+      "EQUALS",                                   "99",
+      "ESCAPE",                             "92", "99", "2003", "2011", "c",
+      "EVERY",                                                  "2011", "c",
+      "EXCEPT",                             "92", "99", "2003", "2011", "c",
+      "EXCEPTION",                          "92", "99",
+      "EXEC",                               "92", "99", "2003", "2011", "c",
+      "EXECUTE",                            "92", "99", "2003", "2011", "c",
+      "EXISTS",                             "92", "99", "2003", "2011", "c",
+      "EXIT",                               "92", "99", "2003",
+      "EXP",                                                    "2011", "c",
+      "EXPLAIN",                                                        "c",
+      "EXTEND",                                                         "c",
+      "EXTERNAL",                           "92", "99", "2003", "2011", "c",
+      "EXTRACT",                            "92",               "2011", "c",
+      "FALSE",                              "92", "99", "2003", "2011", "c",
+      "FETCH",                              "92", "99", "2003", "2011", "c",
+      "FILTER",                                   "99", "2003", "2011", "c",
+      "FIRST",                              "92", "99",
+      "FIRST_VALUE",                                            "2011", "c",
+      "FLOAT",                              "92", "99", "2003", "2011", "c",
+      "FLOOR",                                                  "2011", "c",
+      "FOR",                                "92", "99", "2003", "2011", "c",
+      "FOREIGN",                            "92", "99", "2003", "2011", "c",
+      "FOREVER",                                                "2011",
+      "FOUND",                              "92", "99",
+      "FREE",                                     "99", "2003", "2011", "c",
+      "FROM",                               "92", "99", "2003", "2011", "c",
+      "FULL",                               "92", "99", "2003", "2011", "c",
+      "FUNCTION",                           "92", "99", "2003", "2011", "c",
+      "FUSION",                                                 "2011", "c",
+      "GENERAL",                                  "99",
+      "GET",                                "92", "99", "2003", "2011", "c",
+      "GLOBAL",                             "92", "99", "2003", "2011", "c",
+      "GO",                                 "92", "99",
+      "GOTO",                               "92", "99",
+      "GRANT",                              "92", "99", "2003", "2011", "c",
+      "GROUP",                              "92", "99", "2003", "2011", "c",
+      "GROUPING",                                 "99", "2003", "2011", "c",
+      "HANDLER",                            "92", "99", "2003",
+      "HAVING",                             "92", "99", "2003", "2011", "c",
+      "HOLD",                                     "99", "2003", "2011", "c",
+      "HOUR",                               "92", "99", "2003", "2011", "c",
+      "HOURS",                                                  "2011",
+      "IDENTITY",                           "92", "99", "2003", "2011", "c",
+      "IF",                                 "92", "99", "2003",
+      "IMMEDIATE",                          "92", "99", "2003",
+      "IMPORT",                                                         "c",
+      "IN",                                 "92", "99", "2003", "2011", "c",
+      "INDICATOR",                          "92", "99", "2003", "2011", "c",
+      "INITIALLY",                          "92", "99",
+      "INNER",                              "92", "99", "2003", "2011", "c",
+      "INOUT",                              "92", "99", "2003", "2011", "c",
+      "INPUT",                              "92", "99", "2003",
+      "INSENSITIVE",                        "92", "99", "2003", "2011", "c",
+      "INSERT",                             "92", "99", "2003", "2011", "c",
+      "INT",                                "92", "99", "2003", "2011", "c",
+      "INTEGER",                            "92", "99", "2003", "2011", "c",
+      "INTERSECT",                          "92", "99", "2003", "2011", "c",
+      "INTERSECTION",                                           "2011", "c",
+      "INTERVAL",                           "92", "99", "2003", "2011", "c",
+      "INTO",                               "92", "99", "2003", "2011", "c",
+      "IS",                                 "92", "99", "2003", "2011", "c",
+      "ISOLATION",                          "92", "99",
+      "ITERATE",                                  "99", "2003",
+      "JOIN",                               "92", "99", "2003", "2011", "c",
+      "KEEP",                                                   "2011",
+      "KEY",                                "92", "99",
+      "LAG",                                                    "2011",
+      "LANGUAGE",                           "92", "99", "2003", "2011", "c",
+      "LARGE",                                    "99", "2003", "2011", "c",
+      "LAST",                               "92", "99",
+      "LAST_VALUE",                                             "2011", "c",
+      "LATERAL",                                  "99", "2003", "2011", "c",
+      "LEAD",                                                   "2011",
+      "LEADING",                            "92", "99", "2003", "2011", "c",
+      "LEAVE",                              "92", "99", "2003",
+      "LEFT",                               "92", "99", "2003", "2011", "c",
+      "LEVEL",                              "92", "99",
+      "LIKE",                               "92", "99", "2003", "2011", "c",
+      "LIKE_REGEX",                                             "2011",
+      "LIMIT",                                                          "c",
+      "LN",                                                     "2011", "c",
+      "LOCAL",                              "92", "99", "2003", "2011", "c",
+      "LOCALTIME",                                "99", "2003", "2011", "c",
+      "LOCALTIMESTAMP",                           "99", "2003", "2011", "c",
+      "LOCATOR",                                  "99",
+      "LOOP",                               "92", "99", "2003",
+      "LOWER",                              "92",               "2011", "c",
+      "MAP",                                      "99",
+      "MATCH",                              "92", "99", "2003", "2011", "c",
+      "MAX",                                "92",               "2011", "c",
+      "MAX_CARDINALITY",                                        "2011",
+      "MEMBER",                                         "2003", "2011", "c",
+      "MERGE",                                          "2003", "2011", "c",
+      "METHOD",                                   "99", "2003", "2011", "c",
+      "MIN",                                "92",               "2011", "c",
+      "MINUTE",                             "92", "99", "2003", "2011", "c",
+      "MINUTES",                                                "2011",
+      "MOD",                                                    "2011", "c",
+      "MODIFIES",                                 "99", "2003", "2011", "c",
+      "MODULE",                             "92", "99", "2003", "2011", "c",
+      "MONTH",                              "92", "99", "2003", "2011", "c",
+      "MULTISET",                                       "2003", "2011", "c",
+      "NAMES",                              "92", "99",
+      "NATIONAL",                           "92", "99", "2003", "2011", "c",
+      "NATURAL",                            "92", "99", "2003", "2011", "c",
+      "NCHAR",                              "92", "99", "2003", "2011", "c",
+      "NCLOB",                                    "99", "2003", "2011", "c",
+      "NEW",                                      "99", "2003", "2011", "c",
+      "NEXT",                               "92", "99",                 "c",
+      "NO",                                 "92", "99", "2003", "2011", "c",
+      "NONE",                                     "99", "2003", "2011", "c",
+      "NORMALIZE",                                              "2011", "c",
+      "NOT",                                "92", "99", "2003", "2011", "c",
+      "NTH_VALUE",                                              "2011",
+      "NTILE",                                                  "2011",
+      "NULL",                               "92", "99", "2003", "2011", "c",
+      "NULLIF",                             "92",               "2011", "c",
+      "NUMERIC",                            "92", "99", "2003", "2011", "c",
+      "OBJECT",                                   "99",
+      "OCCURRENCES_REGEX",                                      "2011",
+      "OCTET_LENGTH",                       "92",               "2011", "c",
+      "OF",                                 "92", "99", "2003", "2011", "c",
+      "OFFSET",                                                 "2011", "c",
+      "OLD",                                      "99", "2003", "2011", "c",
+      "ON",                                 "92", "99", "2003", "2011", "c",
+      "ONLY",                               "92", "99", "2003", "2011", "c",
+      "OPEN",                               "92", "99", "2003", "2011", "c",
+      "OPTION",                             "92", "99",
+      "OR",                                 "92", "99", "2003", "2011", "c",
+      "ORDER",                              "92", "99", "2003", "2011", "c",
+      "ORDINALITY",                               "99",
+      "OUT",                                "92", "99", "2003", "2011", "c",
+      "OUTER",                              "92", "99", "2003", "2011", "c",
+      "OUTPUT",                             "92", "99", "2003",
+      "OVER",                                     "99", "2003", "2011", "c",
+      "OVERLAPS",                           "92", "99", "2003", "2011", "c",
+      "OVERLAY",                                                "2011", "c",
+      "PAD",                                "92", "99",
+      "PARAMETER",                          "92", "99", "2003", "2011", "c",
+      "PARTIAL",                            "92", "99",
+      "PARTITION",                                "99", "2003", "2011", "c",
+      "PATH",                               "92", "99",
+      "PERCENTILE_CONT",                                        "2011", "c",
+      "PERCENTILE_DISC",                                        "2011", "c",
+      "PERCENT_RANK",                                           "2011", "c",
+      "POSITION",                           "92",               "2011", "c",
+      "POSITION_REGEX",                                         "2011",
+      "POWER",                                                  "2011", "c",
+      "PRECISION",                          "92", "99", "2003", "2011", "c",
+      "PREPARE",                            "92", "99", "2003", "2011", "c",
+      "PRESERVE",                           "92", "99",
+      "PRIMARY",                            "92", "99", "2003", "2011", "c",
+      "PRIOR",                              "92", "99",
+      "PRIVILEGES",                         "92", "99",
+      "PROCEDURE",                          "92", "99", "2003", "2011", "c",
+      "PUBLIC",                             "92", "99",
+      "RANGE",                                    "99", "2003", "2011", "c",
+      "RANK",                                                   "2011", "c",
+      "READ",                               "92", "99",
+      "READS",                                    "99", "2003", "2011", "c",
+      "REAL",                               "92", "99", "2003", "2011", "c",
+      "RECURSIVE",                                "99", "2003", "2011", "c",
+      "REF",                                      "99", "2003", "2011", "c",
+      "REFERENCES",                         "92", "99", "2003", "2011", "c",
+      "REFERENCING",                              "99", "2003", "2011", "c",
+      "REGR_AVGX",                                              "2011", "c",
+      "REGR_AVGY",                                              "2011", "c",
+      "REGR_COUNT",                                             "2011", "c",
+      "REGR_INTERCEPT",                                         "2011", "c",
+      "REGR_R2",                                                "2011", "c",
+      "REGR_SLOPE",                                             "2011", "c",
+      "REGR_SXX",                                               "2011", "c",
+      "REGR_SXY",                                               "2011", "c",
+      "REGR_SYY",                                               "2011", "c",
+      "RELATIVE",                           "92", "99",
+      "RELEASE",                                  "99", "2003", "2011", "c",
+      "REPEAT",                             "92", "99", "2003",
+      "RESET",                                                          "c",
+      "RESIGNAL",                           "92", "99", "2003",
+      "RESTRICT",                           "92", "99",
+      "RESULT",                                   "99", "2003", "2011", "c",
+      "RETURN",                             "92", "99", "2003", "2011", "c",
+      "RETURNS",                            "92", "99", "2003", "2011", "c",
+      "REVOKE",                             "92", "99", "2003", "2011", "c",
+      "RIGHT",                              "92", "99", "2003", "2011", "c",
+      "ROLE",                                     "99",
+      "ROLLBACK",                           "92", "99", "2003", "2011", "c",
+      "ROLLUP",                                   "99", "2003", "2011", "c",
+      "ROUTINE",                            "92", "99",
+      "ROW",                                      "99", "2003", "2011", "c",
+      "ROWS",                               "92", "99", "2003", "2011", "c",
+      "ROW_NUMBER",                                             "2011", "c",
+      "SAVEPOINT",                                "99", "2003", "2011", "c",
+      "SCHEMA",                             "92", "99",
+      "SCOPE",                                    "99", "2003", "2011", "c",
+      "SCROLL",                             "92", "99", "2003", "2011", "c",
+      "SEARCH",                                   "99", "2003", "2011", "c",
+      "SECOND",                             "92", "99", "2003", "2011", "c",
+      "SECONDS",                                                "2011",
+      "SECTION",                            "92", "99",
+      "SELECT",                             "92", "99", "2003", "2011", "c",
+      "SENSITIVE",                                "99", "2003", "2011", "c",
+      "SESSION",                            "92", "99",
+      "SESSION_USER",                       "92", "99", "2003", "2011", "c",
+      "SET",                                "92", "99", "2003", "2011", "c",
+      "SETS",                                     "99",
+      "SIGNAL",                             "92", "99", "2003",
+      "SIMILAR",                                  "99", "2003", "2011", "c",
+      "SIZE",                               "92", "99",
+      "SMALLINT",                           "92", "99", "2003", "2011", "c",
+      "SOME",                               "92", "99", "2003", "2011", "c",
+      "SPACE",                              "92", "99",
+      "SPECIFIC",                           "92", "99", "2003", "2011", "c",
+      "SPECIFICTYPE",                             "99", "2003", "2011", "c",
+      "SQL",                                "92", "99", "2003", "2011", "c",
+      "SQLCODE",                            "92",
+      "SQLERROR",                           "92",
+      "SQLEXCEPTION",                       "92", "99", "2003", "2011", "c",
+      "SQLSTATE",                           "92", "99", "2003", "2011", "c",
+      "SQLWARNING",                         "92", "99", "2003", "2011", "c",
+      "SQRT",                                                   "2011", "c",
+      "START",                                    "99", "2003", "2011", "c",
+      "STATE",                                    "99",
+      "STATIC",                                   "99", "2003", "2011", "c",
+      "STDDEV_POP",                                             "2011", "c",
+      "STDDEV_SAMP",                                            "2011", "c",
+      "STREAM",                                                         "c",
+      "SUBMULTISET",                                    "2003", "2011", "c",
+      "SUBSTRING",                          "92",               "2011", "c",
+      "SUBSTRING_REGEX",                                        "2011",
+      "SUM",                                "92",               "2011", "c",
+      "SYMMETRIC",                                "99", "2003", "2011", "c",
+      "SYSTEM",                                   "99", "2003", "2011", "c",
+      "SYSTEM_USER",                        "92", "99", "2003", "2011", "c",
+      "TABLE",                              "92", "99", "2003", "2011", "c",
+      "TABLESAMPLE",                                    "2003", "2011", "c",
+      "TEMPORARY",                          "92", "99",
+      "THEN",                               "92", "99", "2003", "2011", "c",
+      "TIME",                               "92", "99", "2003", "2011", "c",
+      "TIMESTAMP",                          "92", "99", "2003", "2011", "c",
+      "TIMEZONE_HOUR",                      "92", "99", "2003", "2011", "c",
+      "TIMEZONE_MINUTE",                    "92", "99", "2003", "2011", "c",
+      "TINYINT",                                                        "c",
+      "TO",                                 "92", "99", "2003", "2011", "c",
+      "TRAILING",                           "92", "99", "2003", "2011", "c",
+      "TRANSACTION",                        "92", "99",
+      "TRANSLATE",                          "92",               "2011", "c",
+      "TRANSLATE_REGEX",                                        "2011",
+      "TRANSLATION",                        "92", "99", "2003", "2011", "c",
+      "TREAT",                                    "99", "2003", "2011", "c",
+      "TRIGGER",                                  "99", "2003", "2011", "c",
+      "TRIM",                               "92",               "2011", "c",
+      "TRIM_ARRAY",                                             "2011",
+      "TRUE",                               "92", "99", "2003", "2011", "c",
+      "TRUNCATE",                                               "2011",
+      "UESCAPE",                                                "2011", "c",
+      "UNDER",                                    "99",
+      "UNDO",                               "92", "99", "2003",
+      "UNION",                              "92", "99", "2003", "2011", "c",
+      "UNIQUE",                             "92", "99", "2003", "2011", "c",
+      "UNKNOWN",                            "92", "99", "2003", "2011", "c",
+      "UNNEST",                                   "99", "2003", "2011", "c",
+      "UNTIL",                              "92", "99", "2003",
+      "UPDATE",                             "92", "99", "2003", "2011", "c",
+      "UPPER",                              "92",               "2011", "c",
+      "UPSERT",                                                         "c",
+      "USAGE",                              "92", "99",
+      "USER",                               "92", "99", "2003", "2011", "c",
+      "USING",                              "92", "99", "2003", "2011", "c",
+      "VALUE",                              "92", "99", "2003", "2011", "c",
+      "VALUES",                             "92", "99", "2003", "2011", "c",
+      "VARBINARY",                                              "2011", "c",
+      "VARCHAR",                            "92", "99", "2003", "2011", "c",
+      "VARYING",                            "92", "99", "2003", "2011", "c",
+      "VAR_POP",                                                "2011", "c",
+      "VAR_SAMP",                                               "2011", "c",
+      "VERSION",                                                "2011",
+      "VERSIONING",                                             "2011",
+      "VERSIONS",                                               "2011",
+      "VIEW",                               "92", "99",
+      "WHEN",                               "92", "99", "2003", "2011", "c",
+      "WHENEVER",                           "92", "99", "2003", "2011", "c",
+      "WHERE",                              "92", "99", "2003", "2011", "c",
+      "WHILE",                              "92", "99", "2003",
+      "WIDTH_BUCKET",                                           "2011", "c",
+      "WINDOW",                                   "99", "2003", "2011", "c",
+      "WITH",                               "92", "99", "2003", "2011", "c",
+      "WITHIN",                                   "99", "2003", "2011", "c",
+      "WITHOUT",                                  "99", "2003", "2011", "c",
+      "WORK",                               "92", "99",
+      "WRITE",                              "92", "99",
+      "YEAR",                               "92", "99", "2003", "2011", "c",
+      "YEARS",                                                  "2011",
+      "ZONE",                               "92", "99");
 
   private static final String ANY = "(?s).*";
 
@@ -458,8 +580,32 @@ public class SqlParserTest {
     getTester().checkExpFails(sql, expectedMsgPattern);
   }
 
-  protected List<String> getReservedKeywords() {
-    return RESERVED_KEYWORDS;
+  protected SortedSet<String> getReservedKeywords() {
+    return keywords("c");
+  }
+
+  private static SortedSet<String> keywords(String dialect) {
+    final ImmutableSortedSet.Builder<String> builder =
+        ImmutableSortedSet.naturalOrder();
+    String r = null;
+    for (String w : RESERVED_KEYWORDS) {
+      switch (w) {
+      case "92":
+      case "99":
+      case "2003":
+      case "2011":
+      case "c":
+        assert r != null;
+        if (dialect == null || dialect.equals(w)) {
+          builder.add(r);
+        }
+        break;
+      default:
+        assert r == null || r.compareTo(w) < 0 : "table should be sorted: " + w;
+        r = w;
+      }
+    }
+    return builder.build();
   }
 
   /**
@@ -6182,11 +6328,18 @@ public class SqlParserTest {
   @Test public void testNoUnintendedNewReservedKeywords() {
     final SqlAbstractParserImpl.Metadata metadata = getParserMetadata();
 
-    final List<String> reservedKeywords = new ArrayList<>();
+    final SortedSet<String> reservedKeywords = new TreeSet<>();
+    final SortedSet<String> keywords92 = keywords("92");
     for (String s : metadata.getTokens()) {
       if (metadata.isKeyword(s) && metadata.isReservedWord(s)) {
         reservedKeywords.add(s);
       }
+      if (false) {
+        // Cannot enable this test yet, because the parser's list of SQL:92
+        // reserved words is not consistent with keywords("92").
+        assertThat(s, metadata.isSql92ReservedWord(s),
+            is(keywords92.contains(s)));
+      }
     }
 
     assertThat("At least one new reserved keyword is added in parser. "


[03/14] calcite git commit: [CALCITE-1241] Add a freemarker variable for adding non reserved keyword list to Parser.jj template (Venki Korukanti)

Posted by el...@apache.org.
[CALCITE-1241] Add a freemarker variable for adding non reserved keyword list to Parser.jj template (Venki Korukanti)

Add a test to prevent unintentionally adding reserved keywords.

Close apache/calcite#233


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

Branch: refs/heads/branch-avatica-1.8
Commit: 20ba43461564c983557e9d9fd1b3e04d75ef7b18
Parents: 6c633cf
Author: vkorukanti <ve...@dremio.com>
Authored: Wed Mar 9 11:00:26 2016 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun May 22 12:46:46 2016 -0700

----------------------------------------------------------------------
 core/src/main/codegen/config.fmpp               |   7 +-
 core/src/main/codegen/templates/Parser.jj       |   3 +
 .../calcite/sql/parser/SqlParserTest.java       | 343 +++++++++++++++++++
 3 files changed, 352 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/20ba4346/core/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/config.fmpp b/core/src/main/codegen/config.fmpp
index 6f88718..87ed18f 100644
--- a/core/src/main/codegen/config.fmpp
+++ b/core/src/main/codegen/config.fmpp
@@ -43,10 +43,15 @@ data: {
     imports: [
     ]
 
-    # List of new keywords. Example: "DATABASES", "TABLES".
+    # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is not a reserved
+    # keyword add it to 'nonReservedKeywords' section.
     keywords: [
     ]
 
+    # List of keywords from "keywords" section that are not reserved.
+    nonReservedKeywords: [
+    ]
+
     # List of methods for parsing custom SQL statements.
     # Return type of method implementation should be 'SqlNode'.
     # Example: SqlShowDatabases(), SqlShowTables().

http://git-wip-us.apache.org/repos/asf/calcite/blob/20ba4346/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index bad7622..cfd9f22 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -5619,6 +5619,9 @@ String CommonNonReservedKeyWord() :
         | <WRITE>
         | <XML>
         | <ZONE>
+      <#list parser.nonReservedKeywords as keyword>
+        | <${keyword}>
+      </#list>
     )
     {
         return unquotedIdentifier();

http://git-wip-us.apache.org/repos/asf/calcite/blob/20ba4346/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index 9fe216a..f8bb2b3 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -42,12 +42,14 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
@@ -60,6 +62,319 @@ import static org.junit.Assert.assertTrue;
 public class SqlParserTest {
   //~ Static fields/initializers ---------------------------------------------
 
+  /**
+   * List of reserved keywords in parser. If a new <b>reserved</b> keyword is added to the
+   * parser, it must be included in this list. If the keyword is not intended to be a
+   * reserved keyword, add it to the non-reserved keyword list in the parser.
+   */
+  private static final List<String> RESERVED_KEYWORDS = ImmutableList.of(
+      "ABS",
+      "ALL",
+      "ALLOCATE",
+      "ALLOW",
+      "ALTER",
+      "AND",
+      "ANY",
+      "ARE",
+      "ARRAY",
+      "AS",
+      "ASENSITIVE",
+      "ASYMMETRIC",
+      "AT",
+      "ATOMIC",
+      "AUTHORIZATION",
+      "AVG",
+      "BEGIN",
+      "BETWEEN",
+      "BIGINT",
+      "BINARY",
+      "BIT",
+      "BLOB",
+      "BOOLEAN",
+      "BOTH",
+      "BY",
+      "CALL",
+      "CALLED",
+      "CARDINALITY",
+      "CASCADED",
+      "CASE",
+      "CAST",
+      "CEIL",
+      "CEILING",
+      "CHAR",
+      "CHARACTER",
+      "CHARACTER_LENGTH",
+      "CHAR_LENGTH",
+      "CHECK",
+      "CLOB",
+      "CLOSE",
+      "COALESCE",
+      "COLLATE",
+      "COLLECT",
+      "COLUMN",
+      "COMMIT",
+      "CONDITION",
+      "CONNECT",
+      "CONSTRAINT",
+      "CONVERT",
+      "CORR",
+      "CORRESPONDING",
+      "COUNT",
+      "COVAR_POP",
+      "COVAR_SAMP",
+      "CREATE",
+      "CROSS",
+      "CUBE",
+      "CUME_DIST",
+      "CURRENT",
+      "CURRENT_CATALOG",
+      "CURRENT_DATE",
+      "CURRENT_DEFAULT_TRANSFORM_GROUP",
+      "CURRENT_PATH",
+      "CURRENT_ROLE",
+      "CURRENT_SCHEMA",
+      "CURRENT_TIME",
+      "CURRENT_TIMESTAMP",
+      "CURRENT_TRANSFORM_GROUP_FOR_TYPE",
+      "CURRENT_USER",
+      "CURSOR",
+      "CYCLE",
+      "DATE",
+      "DAY",
+      "DEALLOCATE",
+      "DEC",
+      "DECIMAL",
+      "DECLARE",
+      "DEFAULT",
+      "DELETE",
+      "DENSE_RANK",
+      "DEREF",
+      "DESCRIBE",
+      "DETERMINISTIC",
+      "DISALLOW",
+      "DISCONNECT",
+      "DISTINCT",
+      "DOUBLE",
+      "DROP",
+      "DYNAMIC",
+      "EACH",
+      "ELEMENT",
+      "ELSE",
+      "END",
+      "END-EXEC",
+      "ESCAPE",
+      "EVERY",
+      "EXCEPT",
+      "EXEC",
+      "EXECUTE",
+      "EXISTS",
+      "EXP",
+      "EXPLAIN",
+      "EXTEND",
+      "EXTERNAL",
+      "EXTRACT",
+      "FALSE",
+      "FETCH",
+      "FILTER",
+      "FIRST_VALUE",
+      "FLOAT",
+      "FLOOR",
+      "FOR",
+      "FOREIGN",
+      "FREE",
+      "FROM",
+      "FULL",
+      "FUNCTION",
+      "FUSION",
+      "GET",
+      "GLOBAL",
+      "GRANT",
+      "GROUP",
+      "GROUPING",
+      "HAVING",
+      "HOLD",
+      "HOUR",
+      "IDENTITY",
+      "IMPORT",
+      "IN",
+      "INDICATOR",
+      "INNER",
+      "INOUT",
+      "INSENSITIVE",
+      "INSERT",
+      "INT",
+      "INTEGER",
+      "INTERSECT",
+      "INTERSECTION",
+      "INTERVAL",
+      "INTO",
+      "IS",
+      "JOIN",
+      "LANGUAGE",
+      "LARGE",
+      "LAST_VALUE",
+      "LATERAL",
+      "LEADING",
+      "LEFT",
+      "LIKE",
+      "LIMIT",
+      "LN",
+      "LOCAL",
+      "LOCALTIME",
+      "LOCALTIMESTAMP",
+      "LOWER",
+      "MATCH",
+      "MAX",
+      "MEMBER",
+      "MERGE",
+      "METHOD",
+      "MIN",
+      "MINUTE",
+      "MOD",
+      "MODIFIES",
+      "MODULE",
+      "MONTH",
+      "MULTISET",
+      "NATIONAL",
+      "NATURAL",
+      "NCHAR",
+      "NCLOB",
+      "NEW",
+      "NEXT",
+      "NO",
+      "NONE",
+      "NORMALIZE",
+      "NOT",
+      "NULL",
+      "NULLIF",
+      "NUMERIC",
+      "OCTET_LENGTH",
+      "OF",
+      "OFFSET",
+      "OLD",
+      "ON",
+      "ONLY",
+      "OPEN",
+      "OR",
+      "ORDER",
+      "OUT",
+      "OUTER",
+      "OVER",
+      "OVERLAPS",
+      "OVERLAY",
+      "PARAMETER",
+      "PARTITION",
+      "PERCENTILE_CONT",
+      "PERCENTILE_DISC",
+      "PERCENT_RANK",
+      "POSITION",
+      "POWER",
+      "PRECISION",
+      "PREPARE",
+      "PRIMARY",
+      "PROCEDURE",
+      "RANGE",
+      "RANK",
+      "READS",
+      "REAL",
+      "RECURSIVE",
+      "REF",
+      "REFERENCES",
+      "REFERENCING",
+      "REGR_AVGX",
+      "REGR_AVGY",
+      "REGR_COUNT",
+      "REGR_INTERCEPT",
+      "REGR_R2",
+      "REGR_SLOPE",
+      "REGR_SXX",
+      "REGR_SXY",
+      "REGR_SYY",
+      "RELEASE",
+      "RESET",
+      "RESULT",
+      "RETURN",
+      "RETURNS",
+      "REVOKE",
+      "RIGHT",
+      "ROLLBACK",
+      "ROLLUP",
+      "ROW",
+      "ROWS",
+      "ROW_NUMBER",
+      "SAVEPOINT",
+      "SCOPE",
+      "SCROLL",
+      "SEARCH",
+      "SECOND",
+      "SELECT",
+      "SENSITIVE",
+      "SESSION_USER",
+      "SET",
+      "SIMILAR",
+      "SMALLINT",
+      "SOME",
+      "SPECIFIC",
+      "SPECIFICTYPE",
+      "SQL",
+      "SQLEXCEPTION",
+      "SQLSTATE",
+      "SQLWARNING",
+      "SQRT",
+      "START",
+      "STATIC",
+      "STDDEV_POP",
+      "STDDEV_SAMP",
+      "STREAM",
+      "SUBMULTISET",
+      "SUBSTRING",
+      "SUM",
+      "SYMMETRIC",
+      "SYSTEM",
+      "SYSTEM_USER",
+      "TABLE",
+      "TABLESAMPLE",
+      "THEN",
+      "TIME",
+      "TIMESTAMP",
+      "TIMEZONE_HOUR",
+      "TIMEZONE_MINUTE",
+      "TINYINT",
+      "TO",
+      "TRAILING",
+      "TRANSLATE",
+      "TRANSLATION",
+      "TREAT",
+      "TRIGGER",
+      "TRIM",
+      "TRUE",
+      "UESCAPE",
+      "UNION",
+      "UNIQUE",
+      "UNKNOWN",
+      "UNNEST",
+      "UPDATE",
+      "UPPER",
+      "UPSERT",
+      "USER",
+      "USING",
+      "VALUE",
+      "VALUES",
+      "VARBINARY",
+      "VARCHAR",
+      "VARYING",
+      "VAR_POP",
+      "VAR_SAMP",
+      "WHEN",
+      "WHENEVER",
+      "WHERE",
+      "WIDTH_BUCKET",
+      "WINDOW",
+      "WITH",
+      "WITHIN",
+      "WITHOUT",
+      "YEAR");
+
   private static final String ANY = "(?s).*";
 
   private static final ThreadLocal<boolean[]> LINUXIFY =
@@ -143,6 +458,10 @@ public class SqlParserTest {
     getTester().checkExpFails(sql, expectedMsgPattern);
   }
 
+  protected List<String> getReservedKeywords() {
+    return RESERVED_KEYWORDS;
+  }
+
   /**
    * Tests that when there is an error, non-reserved keywords such as "A",
    * "ABSOLUTE" (which naturally arise whenever a production uses
@@ -5851,6 +6170,30 @@ public class SqlParserTest {
     assertTrue(!jdbcKeywords.contains(",SELECT,"));
   }
 
+  /**
+   * Tests that reserved keywords are not added to the parser unintentionally.
+   * (Most keywords are non-reserved. The set of reserved words generally
+   * only changes with a new version of the SQL standard.)
+   *
+   * <p>If the new keyword added is intended to be a reserved keyword, update
+   * the {@link #RESERVED_KEYWORDS} list. If not, add the keyword to the
+   * non-reserved keyword list in the parser.
+   */
+  @Test public void testNoUnintendedNewReservedKeywords() {
+    final SqlAbstractParserImpl.Metadata metadata = getParserMetadata();
+
+    final List<String> reservedKeywords = new ArrayList<>();
+    for (String s : metadata.getTokens()) {
+      if (metadata.isKeyword(s) && metadata.isReservedWord(s)) {
+        reservedKeywords.add(s);
+      }
+    }
+
+    assertThat("At least one new reserved keyword is added in parser. "
+        + "Make sure to check the new keywords are intended to be a reserved keywords.",
+        reservedKeywords, is(getReservedKeywords()));
+  }
+
   /** Generates a copy of {@code reference.md} with the current set of key
    * words. Fails if the copy is different from the original. */
   @Test public void testGenerateKeyWords() throws IOException {


[12/14] calcite git commit: [CALCITE-1266] RelBuilder.field gets offsets wrong

Posted by el...@apache.org.
[CALCITE-1266] RelBuilder.field gets offsets wrong


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

Branch: refs/heads/branch-avatica-1.8
Commit: 48e434263e526dcfa0c367e523cdc83b5989ab16
Parents: d757201
Author: Julian Hyde <jh...@apache.org>
Authored: Tue May 31 11:37:31 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue May 31 11:37:31 2016 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/tools/RelBuilder.java    | 20 +++++++++++++++++++-
 .../apache/calcite/test/PigRelBuilderTest.java  |  2 +-
 .../org/apache/calcite/test/RelBuilderTest.java |  6 +++---
 .../org/apache/calcite/test/RelOptRulesTest.xml |  2 +-
 4 files changed, 24 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/48e43426/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index acc9363..d912ba2 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -250,6 +250,20 @@ public class RelBuilder {
     return peek(inputCount - 1 - inputOrdinal);
   }
 
+  /** Returns the number of fields in all inputs before (to the left of)
+   * the given input.
+   *
+   * @param inputCount Number of inputs
+   * @param inputOrdinal Input ordinal
+   */
+  private int inputOffset(int inputCount, int inputOrdinal) {
+    int offset = 0;
+    for (int i = 0; i < inputOrdinal; i++) {
+      offset += peek(inputCount, i).getRowType().getFieldCount();
+    }
+    return offset;
+  }
+
   // Methods that return scalar expressions
 
   /** Creates a literal (constant expression). */
@@ -327,7 +341,11 @@ public class RelBuilder {
       throw new IllegalArgumentException("field ordinal [" + fieldOrdinal
           + "] out of range; input fields are: " + rowType.getFieldNames());
     }
-    return cluster.getRexBuilder().makeInputRef(input, fieldOrdinal);
+    final RelDataType fieldType =
+        rowType.getFieldList().get(fieldOrdinal).getType();
+    final int offset = inputOffset(inputCount, inputOrdinal);
+    return cluster.getRexBuilder()
+        .makeInputRef(fieldType, offset + fieldOrdinal);
   }
 
   /** Creates a reference to a field of the current record which originated

http://git-wip-us.apache.org/repos/asf/calcite/blob/48e43426/core/src/test/java/org/apache/calcite/test/PigRelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/PigRelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/PigRelBuilderTest.java
index d1b9f33..d877097 100644
--- a/core/src/test/java/org/apache/calcite/test/PigRelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/PigRelBuilderTest.java
@@ -117,7 +117,7 @@ public class PigRelBuilderTest {
             builder.groupKey("DEPTNO").alias("e"),
             builder.groupKey("DEPTNO").alias("d"))
         .build();
-    final String plan = "LogicalJoin(condition=[=($0, $0)], joinType=[inner])\n"
+    final String plan = "LogicalJoin(condition=[=($0, $2)], joinType=[inner])\n"
         + "  LogicalAggregate(group=[{0}], EMP=[COLLECT($8)])\n"
         + "    LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], $f8=[ROW($0, $1, $2, $3, $4, $5, $6, $7)])\n      LogicalTableScan(table=[[scott, EMP]])\n  LogicalAggregate(group=[{0}], DEPT=[COLLECT($3)])\n"
         + "    LogicalProject(DEPTNO=[$0], DNAME=[$1], LOC=[$2], $f3=[ROW($0, $1, $2)])\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/48e43426/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index e6ea995..bb9f7b4 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -815,7 +815,7 @@ public class RelBuilderTest {
                     builder.field(2, 1, "DEPTNO")))
             .build();
     final String expected = ""
-        + "LogicalJoin(condition=[=($7, $0)], joinType=[inner])\n"
+        + "LogicalJoin(condition=[=($7, $8)], joinType=[inner])\n"
         + "  LogicalFilter(condition=[IS NULL($6)])\n"
         + "    LogicalTableScan(table=[[scott, EMP]])\n"
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
@@ -834,7 +834,7 @@ public class RelBuilderTest {
             .join(JoinRelType.INNER, "DEPTNO")
             .build();
     final String expected = ""
-        + "LogicalJoin(condition=[=($7, $0)], joinType=[inner])\n"
+        + "LogicalJoin(condition=[=($7, $8)], joinType=[inner])\n"
         + "  LogicalFilter(condition=[IS NULL($6)])\n"
         + "    LogicalTableScan(table=[[scott, EMP]])\n"
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
@@ -864,7 +864,7 @@ public class RelBuilderTest {
             .build();
     // Note that "dept.deptno IS NOT NULL" has been simplified away.
     final String expected = ""
-        + "LogicalJoin(condition=[AND(=($7, $0), =($0, 123))], joinType=[left])\n"
+        + "LogicalJoin(condition=[AND(=($7, $8), =($0, 123))], joinType=[left])\n"
         + "  LogicalTableScan(table=[[scott, EMP]])\n"
         + "  LogicalTableScan(table=[[scott, DEPT]])\n";
     assertThat(str(root), is(expected));

http://git-wip-us.apache.org/repos/asf/calcite/blob/48e43426/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 20c5c0f..506f2dd 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -4666,7 +4666,7 @@ LogicalProject(DEPTNO=[$7])
 LogicalProject(EMPNO=[$0])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10])
     LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], DEPTNO0=[$9], NAME=[$10], $f0=[$11])
-      LogicalJoin(condition=[<($2, $3)], joinType=[left])
+      LogicalJoin(condition=[<($11, $12)], joinType=[left])
         LogicalTableScan(table=[[CATALOG, SALES, EMP]])
         LogicalJoin(condition=[true], joinType=[left])
           LogicalJoin(condition=[true], joinType=[left])


[05/14] calcite git commit: [CALCITE-1246] Cleanup duplicate variables in JoinPushThroughJoinRule (Yi Xinglu)

Posted by el...@apache.org.
[CALCITE-1246] Cleanup duplicate variables in JoinPushThroughJoinRule (Yi Xinglu)

Also, fix typo in comment.

Close apache/calcite#234


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

Branch: refs/heads/branch-avatica-1.8
Commit: b6f0e1021b8d8e0dc08e6da49391a76cdb6cfb14
Parents: 5a697ee
Author: yixinglu <yx...@gmail.com>
Authored: Thu May 19 21:07:18 2016 +0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun May 22 12:46:46 2016 -0700

----------------------------------------------------------------------
 .../calcite/rel/rules/JoinPushThroughJoinRule.java   | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/b6f0e102/core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java
index 402557d..5894130 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinPushThroughJoinRule.java
@@ -169,11 +169,7 @@ public class JoinPushThroughJoinRule extends RelOptRule {
     final List<RexNode> newBottomList = new ArrayList<>();
     new RexPermuteInputsShuttle(bottomMapping, relA, relC)
         .visitList(nonIntersecting, newBottomList);
-    final Mappings.TargetMapping bottomBottomMapping =
-        Mappings.createShiftMapping(
-            aCount + bCount,
-            0, 0, aCount);
-    new RexPermuteInputsShuttle(bottomBottomMapping, relA, relC)
+    new RexPermuteInputsShuttle(bottomMapping, relA, relC)
         .visitList(bottomNonIntersecting, newBottomList);
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     RexNode newBottomCondition =
@@ -259,7 +255,7 @@ public class JoinPushThroughJoinRule extends RelOptRule {
     }
 
     // Split the condition of bottomJoin into a conjunction. Each of the
-    // parts that use columns from B will need to be pulled up.
+    // parts that use columns from A will need to be pulled up.
     final List<RexNode> bottomIntersecting = new ArrayList<>();
     final List<RexNode> bottomNonIntersecting = new ArrayList<>();
     split(
@@ -276,12 +272,7 @@ public class JoinPushThroughJoinRule extends RelOptRule {
     final List<RexNode> newBottomList = new ArrayList<>();
     new RexPermuteInputsShuttle(bottomMapping, relC, relB)
         .visitList(nonIntersecting, newBottomList);
-    final Mappings.TargetMapping bottomBottomMapping =
-        Mappings.createShiftMapping(
-            aCount + bCount + cCount,
-            0, aCount + bCount, cCount,
-            cCount, aCount, bCount);
-    new RexPermuteInputsShuttle(bottomBottomMapping, relC, relB)
+    new RexPermuteInputsShuttle(bottomMapping, relC, relB)
         .visitList(bottomNonIntersecting, newBottomList);
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     RexNode newBottomCondition =


[10/14] calcite git commit: [CALCITE-1264] Litmus argument interpolation (Chris Baynes)

Posted by el...@apache.org.
[CALCITE-1264] Litmus argument interpolation (Chris Baynes)

Close apache/calcite#241


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

Branch: refs/heads/branch-avatica-1.8
Commit: 2f937c1ba354726a340968a51224131a71017979
Parents: f3caf13
Author: Chris Baynes <bi...@gmail.com>
Authored: Mon May 30 18:15:42 2016 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon May 30 23:15:43 2016 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/util/Litmus.java    |  5 +++-
 .../java/org/apache/calcite/util/UtilTest.java  | 26 ++++++++++++++++++++
 .../calcite/adapter/druid/DruidQuery.java       |  4 +--
 3 files changed, 32 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/2f937c1b/core/src/main/java/org/apache/calcite/util/Litmus.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Litmus.java b/core/src/main/java/org/apache/calcite/util/Litmus.java
index 0f0be55..dbdd0e3 100644
--- a/core/src/main/java/org/apache/calcite/util/Litmus.java
+++ b/core/src/main/java/org/apache/calcite/util/Litmus.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.util;
 
+import org.slf4j.helpers.MessageFormatter;
+
 /**
  * Callback to be called when a test for validity succeeds or fails.
  */
@@ -24,7 +26,8 @@ public interface Litmus {
    * an {@link java.lang.AssertionError} on failure. */
   Litmus THROW = new Litmus() {
     public boolean fail(String message, Object... args) {
-      final String s = message == null ? null : String.format(message, args);
+      final String s = message == null
+          ? null : MessageFormatter.arrayFormat(message, args).getMessage();
       throw new AssertionError(s);
     }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/2f937c1b/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 9afa6b6..e9af460 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -1791,6 +1791,32 @@ public class UtilTest {
     }
     assertThat(local2.get(), is("x"));
   }
+
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-1264">[CALCITE-1264]
+   * Litmus argument interpolation</a>. */
+  @Test public void testLitmus() {
+    boolean b = checkLitmus(2, Litmus.THROW);
+    assertThat(b, is(true));
+    b = checkLitmus(2, Litmus.IGNORE);
+    assertThat(b, is(true));
+    try {
+      b = checkLitmus(-1, Litmus.THROW);
+      fail("expected fail, got " + b);
+    } catch (AssertionError e) {
+      assertThat(e.getMessage(), is("-1 is less than 0"));
+    }
+    b = checkLitmus(-1, Litmus.IGNORE);
+    assertThat(b, is(false));
+  }
+
+  private boolean checkLitmus(int i, Litmus litmus) {
+    if (i < 0) {
+      return litmus.fail("{} is less than {}", i, 0);
+    } else {
+      return litmus.succeed();
+    }
+  }
 }
 
 // End UtilTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/2f937c1b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
index ee86dd3..bc51e88 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java
@@ -127,7 +127,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
     }
     final String signature = signature();
     if (!isValidSignature(signature)) {
-      return litmus.fail("invalid signature [%s]", signature);
+      return litmus.fail("invalid signature [{}]", signature);
     }
     if (rels.isEmpty()) {
       return litmus.fail("must have at least one rel");
@@ -161,7 +161,7 @@ public class DruidQuery extends AbstractRelNode implements BindableRel {
         if (r instanceof Filter) {
           final Filter filter = (Filter) r;
           if (!isValidFilter(filter.getCondition())) {
-            return litmus.fail("invalid filter [%s]", filter.getCondition());
+            return litmus.fail("invalid filter [{}]", filter.getCondition());
           }
         }
       }


[13/14] calcite git commit: [CALCITE-1254] Implement Statement#executeLargeBatch()

Posted by el...@apache.org.
[CALCITE-1254] Implement Statement#executeLargeBatch()

Further work on executeLargeBatch with the help of jhyde:
* Remove usage of deprecated prepareAndExecute
* saturated cast long to int
* do not call deprecated methods internally
* make DatabaseMetaData.supportsBatchUpdates return true
* JdbcMeta calls executeLargeBatch if underlying DB supports it
* clarify javadoc of Meta.closeStatement


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

Branch: refs/heads/branch-avatica-1.8
Commit: 08c966b9828f534ba3d8d2ccf052de7d887c70ef
Parents: 48e4342
Author: Josh Elser <el...@apache.org>
Authored: Sat May 28 10:26:10 2016 -0700
Committer: Josh Elser <el...@apache.org>
Committed: Tue May 31 18:44:41 2016 -0400

----------------------------------------------------------------------
 .../calcite/avatica/AvaticaConnection.java      | 13 ++--
 .../avatica/AvaticaDatabaseMetaData.java        |  2 +-
 .../avatica/AvaticaPreparedStatement.java       |  7 ++-
 .../calcite/avatica/AvaticaStatement.java       | 20 +++---
 .../apache/calcite/avatica/AvaticaUtils.java    | 61 +++++++++++++++++++
 .../java/org/apache/calcite/avatica/Meta.java   | 35 +++++++----
 .../apache/calcite/avatica/proto/Responses.java | 64 ++++++++++----------
 .../calcite/avatica/remote/LocalService.java    |  6 +-
 .../calcite/avatica/remote/RemoteMeta.java      |  9 ++-
 .../apache/calcite/avatica/remote/Service.java  | 11 ++--
 avatica/core/src/main/protobuf/responses.proto  |  2 +-
 .../calcite/avatica/AvaticaStatementTest.java   | 51 ++++++++++++++++
 .../remote/ProtobufSerializationTest.java       | 10 ++-
 .../remote/ProtobufTranslationImplTest.java     |  2 +-
 .../calcite/avatica/test/AvaticaUtilsTest.java  | 10 +++
 .../apache/calcite/avatica/jdbc/JdbcMeta.java   | 11 ++--
 16 files changed, 233 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
index 69a60ec..f0524e7 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -523,15 +523,12 @@ public abstract class AvaticaConnection implements Connection {
    * @param pstmt The prepared statement.
    * @return An array of update counts containing one element for each command in the batch.
    */
-  protected int[] executeBatchUpdateInternal(AvaticaPreparedStatement pstmt) throws SQLException {
+  protected long[] executeBatchUpdateInternal(AvaticaPreparedStatement pstmt) throws SQLException {
     try {
       // Get the handle from the statement
       Meta.StatementHandle handle = pstmt.handle;
       // Execute it against meta
-      final Meta.ExecuteBatchResult executeBatchResult =
-          meta.executeBatch(handle, pstmt.getParameterValueBatch());
-      // Send back just the update counts
-      return executeBatchResult.updateCounts;
+      return meta.executeBatch(handle, pstmt.getParameterValueBatch()).updateCounts;
     } catch (Exception e) {
       throw helper.createException(e.getMessage(), e);
     }
@@ -610,7 +607,11 @@ public abstract class AvaticaConnection implements Connection {
             }
           }
         };
-    return meta.prepareAndExecute(statement.handle, sql, maxRowCount, callback);
+    // The old semantics were that maxRowCount was also treated as the maximum number of
+    // elements in the first Frame of results. A value of -1 would also preserve this, but an
+    // explicit (positive) number is easier to follow, IMO.
+    return meta.prepareAndExecute(statement.handle, sql, maxRowCount,
+        AvaticaUtils.toSaturatedInt(maxRowCount), callback);
   }
 
   protected ExecuteBatchResult prepareAndUpdateBatch(final AvaticaStatement statement,

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaDatabaseMetaData.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaDatabaseMetaData.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaDatabaseMetaData.java
index 0345150..7182968 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaDatabaseMetaData.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaDatabaseMetaData.java
@@ -1100,7 +1100,7 @@ public class AvaticaDatabaseMetaData implements AvaticaSpecificDatabaseMetaData
   }
 
   public boolean supportsBatchUpdates() throws SQLException {
-    return false;
+    return true;
   }
 
   public ResultSet getUDTs(

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
index e83a785..5e25a03 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
@@ -244,10 +244,13 @@ public abstract class AvaticaPreparedStatement
   }
 
   @Override public int[] executeBatch() throws SQLException {
+    return AvaticaUtils.toSaturatedInts(executeLargeBatch());
+  }
+
+  public long[] executeLargeBatch() throws SQLException {
     // Overriding the implementation in AvaticaStatement.
     try {
-      final int[] updateCounts = getConnection().executeBatchUpdateInternal(this);
-      return updateCounts;
+      return getConnection().executeBatchUpdateInternal(this);
     } finally {
       // If we failed to send this batch, that's a problem for the user to handle, not us.
       // Make sure we always clear the statements we collected to submit in one RPC.

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
index 82e4443..73c16cd 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
@@ -155,13 +155,12 @@ public abstract class AvaticaStatement
   /**
    * Executes a collection of updates in a single batch RPC.
    *
-   * @return an array of integers mapping to the update count per SQL command.
+   * @return an array of long mapping to the update count per SQL command.
    */
-  protected int[] executeBatchInternal() throws SQLException {
+  protected long[] executeBatchInternal() throws SQLException {
     for (int i = 0; i < connection.maxRetriesPerExecute; i++) {
       try {
-        Meta.ExecuteBatchResult result = connection.prepareAndUpdateBatch(this, batchedSql);
-        return result.updateCounts;
+        return connection.prepareAndUpdateBatch(this, batchedSql).updateCounts;
       } catch (NoSuchStatementException e) {
         resetStatement();
       }
@@ -219,7 +218,7 @@ public abstract class AvaticaStatement
   }
 
   public final int executeUpdate(String sql) throws SQLException {
-    return (int) executeLargeUpdate(sql);
+    return AvaticaUtils.toSaturatedInt(executeLargeUpdate(sql));
   }
 
   public long executeLargeUpdate(String sql) throws SQLException {
@@ -266,7 +265,7 @@ public abstract class AvaticaStatement
   }
 
   public final int getMaxRows() {
-    return (int) getLargeMaxRows();
+    return AvaticaUtils.toSaturatedInt(getLargeMaxRows());
   }
 
   public long getLargeMaxRows() {
@@ -346,7 +345,7 @@ public abstract class AvaticaStatement
   }
 
   public int getUpdateCount() throws SQLException {
-    return (int) updateCount;
+    return AvaticaUtils.toSaturatedInt(updateCount);
   }
 
   public long getLargeUpdateCount() throws SQLException {
@@ -390,12 +389,16 @@ public abstract class AvaticaStatement
   }
 
   public int[] executeBatch() throws SQLException {
+    return AvaticaUtils.toSaturatedInts(executeLargeBatch());
+  }
+
+  public long[] executeLargeBatch() throws SQLException {
     try {
       return executeBatchInternal();
     } finally {
       // If we failed to send this batch, that's a problem for the user to handle, not us.
       // Make sure we always clear the statements we collected to submit in one RPC.
-      this.batchedSql.clear();
+      clearBatch();
     }
   }
 
@@ -551,6 +554,7 @@ public abstract class AvaticaStatement
     }
     return parameterValues;
   }
+
 }
 
 // End AvaticaStatement.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
index a999f19..845dde5 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaUtils.java
@@ -25,6 +25,7 @@ import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
+import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.AbstractList;
@@ -44,6 +45,8 @@ public class AvaticaUtils {
       method(long.class, Statement.class, "getLargeMaxRows");
   private static final MethodHandle GET_LARGE_UPDATE_COUNT =
       method(void.class, Statement.class, "getLargeUpdateCount");
+  private static final MethodHandle EXECUTE_LARGE_BATCH =
+      method(long[].class, Statement.class, "executeLargeBatch");
 
   private static final Set<String> UNIQUE_STRINGS = new HashSet<>();
 
@@ -311,6 +314,26 @@ public class AvaticaUtils {
     return statement.getUpdateCount();
   }
 
+  /** Invokes {@code Statement#executeLargeBatch}, falling back on
+   * {@link PreparedStatement#executeBatch} if the method does not exist
+   * (before JDK 1.8) or throws {@link UnsupportedOperationException}. */
+  public static long[] executeLargeBatch(Statement statement)
+      throws SQLException {
+    if (EXECUTE_LARGE_BATCH != null) {
+      try {
+        // Call Statement.executeLargeBatch
+        return (long[]) EXECUTE_LARGE_BATCH.invokeExact();
+      } catch (UnsupportedOperationException e) {
+        // ignore, and fall through to call Statement.executeBatch
+      } catch (Error | RuntimeException | SQLException e) {
+        throw e;
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+    return toLongs(statement.executeBatch());
+  }
+
   /** Generates a string that is unique in the execution of the JVM.
    * It is used by tests to ensure that they create distinct temporary tables.
    * The strings are never thrown away, so don't put too much in there!
@@ -324,6 +347,44 @@ public class AvaticaUtils {
       return s;
     }
   }
+
+  /** Converts a {@code long} to {@code int}, rounding as little as possible
+   * if the value is outside the legal range for an {@code int}. */
+  public static int toSaturatedInt(long value) {
+    if (value > Integer.MAX_VALUE) {
+      return Integer.MAX_VALUE;
+    }
+    if (value < Integer.MIN_VALUE) {
+      return Integer.MIN_VALUE;
+    }
+    return (int) value;
+  }
+
+  /**
+   * Converts an array of {@code long} values to an array of {@code int}
+   * values, truncating values outside the legal range for an {@code int}
+   * to {@link Integer#MIN_VALUE} or {@link Integer#MAX_VALUE}.
+   *
+   * @param longs An array of {@code long}s
+   * @return An array of {@code int}s
+   */
+  public static int[] toSaturatedInts(long[] longs) {
+    final int[] ints = new int[longs.length];
+    for (int i = 0; i < longs.length; i++) {
+      ints[i] = toSaturatedInt(longs[i]);
+    }
+    return ints;
+  }
+
+  /** Converts an array of {@code int} values to an array of {@code long}
+   * values. */
+  public static long[] toLongs(int[] ints) {
+    final long[] longs = new long[ints.length];
+    for (int i = 0; i < ints.length; i++) {
+      longs[i] = ints[i];
+    }
+    return longs;
+  }
 }
 
 // End AvaticaUtils.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java b/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java
index 829ab5a..c7df281 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java
@@ -225,7 +225,8 @@ public interface Meta {
    *     first frame of data
    * @deprecated See {@link #prepareAndExecute(StatementHandle, String, long, int, PrepareCallback)}
    */
-  @Deprecated ExecuteResult prepareAndExecute(StatementHandle h, String sql,
+  @Deprecated // to be removed before 2.0
+  ExecuteResult prepareAndExecute(StatementHandle h, String sql,
       long maxRowCount, PrepareCallback callback) throws NoSuchStatementException;
 
   /** Prepares and executes a statement.
@@ -234,7 +235,9 @@ public interface Meta {
    * @param sql SQL query
    * @param maxRowCount Maximum number of rows for the entire query. Negative for no limit
    *    (different meaning than JDBC).
-   * @param maxRowsInFirstFrame Maximum number of rows for the first frame.
+   * @param maxRowsInFirstFrame Maximum number of rows for the first frame. This value should
+   *    always be less than or equal to {@code maxRowCount} as the number of results are guaranteed
+   *    to be restricted by {@code maxRowCount} and the underlying database.
    * @param callback Callback to lock, clear and assign cursor
    *
    * @return Result containing statement ID, and if a query, a result set and
@@ -288,7 +291,8 @@ public interface Meta {
    * @return Execute result
    * @deprecated See {@link #execute(StatementHandle, List, int)}
    */
-  @Deprecated ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues,
+  @Deprecated // to be removed before 2.0
+  ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues,
       long maxRowCount) throws NoSuchStatementException;
 
   /** Executes a prepared statement.
@@ -307,7 +311,13 @@ public interface Meta {
    */
   StatementHandle createStatement(ConnectionHandle ch);
 
-  /** Closes a statement. */
+  /** Closes a statement.
+   *
+   * <p>If the statement handle is not known, or is already closed, does
+   * nothing.
+   *
+   * @param h Statement handle
+   */
   void closeStatement(StatementHandle h);
 
   /**
@@ -326,29 +336,30 @@ public interface Meta {
   void closeConnection(ConnectionHandle ch);
 
   /**
-   * Re-set the {@link ResultSet} on a Statement. Not a JDBC method.
+   * Re-sets the {@link ResultSet} on a Statement. Not a JDBC method.
+   *
    * @return True if there are results to fetch after resetting to the given offset. False otherwise
    */
   boolean syncResults(StatementHandle sh, QueryState state, long offset)
       throws NoSuchStatementException;
 
   /**
-   * Makes all changes since the last commit/rollback permanent. Analogy to
+   * Makes all changes since the last commit/rollback permanent. Analogous to
    * {@link Connection#commit()}.
    *
-   * @param ch A reference to the real JDBC Connection.
+   * @param ch A reference to the real JDBC Connection
    */
   void commit(ConnectionHandle ch);
 
   /**
-   * Undoes all changes since the last commit/rollback. Analogy to
+   * Undoes all changes since the last commit/rollback. Analogous to
    * {@link Connection#rollback()};
    *
-   * @param ch A reference to the real JDBC Connection.
+   * @param ch A reference to the real JDBC Connection
    */
   void rollback(ConnectionHandle ch);
 
-  /** Sync client and server view of connection properties.
+  /** Synchronizes client and server view of connection properties.
    *
    * <p>Note: this interface is considered "experimental" and may undergo further changes as this
    * functionality is extended to other aspects of state management for
@@ -522,9 +533,9 @@ public interface Meta {
    * Response from a collection of SQL commands or parameter values in a single batch.
    */
   class ExecuteBatchResult {
-    public final int[] updateCounts;
+    public final long[] updateCounts;
 
-    public ExecuteBatchResult(int[] updateCounts) {
+    public ExecuteBatchResult(long[] updateCounts) {
       this.updateCounts = Objects.requireNonNull(updateCounts);
     }
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/proto/Responses.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/proto/Responses.java b/avatica/core/src/main/java/org/apache/calcite/avatica/proto/Responses.java
index ca24f86..9070675 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/proto/Responses.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/proto/Responses.java
@@ -11627,17 +11627,17 @@ package org.apache.calcite.avatica.proto;
     int getStatementId();
 
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
-    java.util.List<java.lang.Integer> getUpdateCountsList();
+    java.util.List<java.lang.Long> getUpdateCountsList();
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
     int getUpdateCountsCount();
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
-    int getUpdateCounts(int index);
+    long getUpdateCounts(int index);
 
     /**
      * <code>optional bool missing_statement = 4;</code>
@@ -11720,21 +11720,21 @@ package org.apache.calcite.avatica.proto;
             }
             case 24: {
               if (!((mutable_bitField0_ & 0x00000004) == 0x00000004)) {
-                updateCounts_ = new java.util.ArrayList<java.lang.Integer>();
+                updateCounts_ = new java.util.ArrayList<java.lang.Long>();
                 mutable_bitField0_ |= 0x00000004;
               }
-              updateCounts_.add(input.readUInt32());
+              updateCounts_.add(input.readUInt64());
               break;
             }
             case 26: {
               int length = input.readRawVarint32();
               int limit = input.pushLimit(length);
               if (!((mutable_bitField0_ & 0x00000004) == 0x00000004) && input.getBytesUntilLimit() > 0) {
-                updateCounts_ = new java.util.ArrayList<java.lang.Integer>();
+                updateCounts_ = new java.util.ArrayList<java.lang.Long>();
                 mutable_bitField0_ |= 0x00000004;
               }
               while (input.getBytesUntilLimit() > 0) {
-                updateCounts_.add(input.readUInt32());
+                updateCounts_.add(input.readUInt64());
               }
               input.popLimit(limit);
               break;
@@ -11829,24 +11829,24 @@ package org.apache.calcite.avatica.proto;
     }
 
     public static final int UPDATE_COUNTS_FIELD_NUMBER = 3;
-    private java.util.List<java.lang.Integer> updateCounts_;
+    private java.util.List<java.lang.Long> updateCounts_;
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
-    public java.util.List<java.lang.Integer>
+    public java.util.List<java.lang.Long>
         getUpdateCountsList() {
       return updateCounts_;
     }
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
     public int getUpdateCountsCount() {
       return updateCounts_.size();
     }
     /**
-     * <code>repeated uint32 update_counts = 3;</code>
+     * <code>repeated uint64 update_counts = 3;</code>
      */
-    public int getUpdateCounts(int index) {
+    public long getUpdateCounts(int index) {
       return updateCounts_.get(index);
     }
     private int updateCountsMemoizedSerializedSize = -1;
@@ -11909,7 +11909,7 @@ package org.apache.calcite.avatica.proto;
         output.writeRawVarint32(updateCountsMemoizedSerializedSize);
       }
       for (int i = 0; i < updateCounts_.size(); i++) {
-        output.writeUInt32NoTag(updateCounts_.get(i));
+        output.writeUInt64NoTag(updateCounts_.get(i));
       }
       if (missingStatement_ != false) {
         output.writeBool(4, missingStatement_);
@@ -11935,7 +11935,7 @@ package org.apache.calcite.avatica.proto;
         int dataSize = 0;
         for (int i = 0; i < updateCounts_.size(); i++) {
           dataSize += com.google.protobuf.CodedOutputStream
-            .computeUInt32SizeNoTag(updateCounts_.get(i));
+            .computeUInt64SizeNoTag(updateCounts_.get(i));
         }
         size += dataSize;
         if (!getUpdateCountsList().isEmpty()) {
@@ -12280,56 +12280,56 @@ package org.apache.calcite.avatica.proto;
         return this;
       }
 
-      private java.util.List<java.lang.Integer> updateCounts_ = java.util.Collections.emptyList();
+      private java.util.List<java.lang.Long> updateCounts_ = java.util.Collections.emptyList();
       private void ensureUpdateCountsIsMutable() {
         if (!((bitField0_ & 0x00000004) == 0x00000004)) {
-          updateCounts_ = new java.util.ArrayList<java.lang.Integer>(updateCounts_);
+          updateCounts_ = new java.util.ArrayList<java.lang.Long>(updateCounts_);
           bitField0_ |= 0x00000004;
          }
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
-      public java.util.List<java.lang.Integer>
+      public java.util.List<java.lang.Long>
           getUpdateCountsList() {
         return java.util.Collections.unmodifiableList(updateCounts_);
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
       public int getUpdateCountsCount() {
         return updateCounts_.size();
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
-      public int getUpdateCounts(int index) {
+      public long getUpdateCounts(int index) {
         return updateCounts_.get(index);
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
       public Builder setUpdateCounts(
-          int index, int value) {
+          int index, long value) {
         ensureUpdateCountsIsMutable();
         updateCounts_.set(index, value);
         onChanged();
         return this;
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
-      public Builder addUpdateCounts(int value) {
+      public Builder addUpdateCounts(long value) {
         ensureUpdateCountsIsMutable();
         updateCounts_.add(value);
         onChanged();
         return this;
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
       public Builder addAllUpdateCounts(
-          java.lang.Iterable<? extends java.lang.Integer> values) {
+          java.lang.Iterable<? extends java.lang.Long> values) {
         ensureUpdateCountsIsMutable();
         com.google.protobuf.AbstractMessageLite.Builder.addAll(
             values, updateCounts_);
@@ -12337,7 +12337,7 @@ package org.apache.calcite.avatica.proto;
         return this;
       }
       /**
-       * <code>repeated uint32 update_counts = 3;</code>
+       * <code>repeated uint64 update_counts = 3;</code>
        */
       public Builder clearUpdateCounts() {
         updateCounts_ = java.util.Collections.emptyList();
@@ -12692,7 +12692,7 @@ package org.apache.calcite.avatica.proto;
       "ress\030\001 \001(\t\"\020\n\016CommitResponse\"\022\n\020Rollback" +
       "Response\"\225\001\n\024ExecuteBatchResponse\022\025\n\rcon" +
       "nection_id\030\001 \001(\t\022\024\n\014statement_id\030\002 \001(\r\022\025",
-      "\n\rupdate_counts\030\003 \003(\r\022\031\n\021missing_stateme" +
+      "\n\rupdate_counts\030\003 \003(\004\022\031\n\021missing_stateme" +
       "nt\030\004 \001(\010\022\036\n\010metadata\030\005 \001(\0132\014.RpcMetadata" +
       "B\"\n org.apache.calcite.avatica.protob\006pr" +
       "oto3"

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/LocalService.java b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
index a15d55f..1562446 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
@@ -16,8 +16,8 @@
  */
 package org.apache.calcite.avatica.remote;
 
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.Meta;
-
 import org.apache.calcite.avatica.Meta.ExecuteBatchResult;
 import org.apache.calcite.avatica.MetaImpl;
 import org.apache.calcite.avatica.MissingResultsException;
@@ -218,7 +218,7 @@ public class LocalService implements Service {
       try {
         final Meta.ExecuteResult executeResult =
             meta.prepareAndExecute(sh, request.sql, request.maxRowCount,
-                new Meta.PrepareCallback() {
+                request.maxRowsInFirstFrame, new Meta.PrepareCallback() {
                   @Override public Object getMonitor() {
                     return LocalService.class;
                   }
@@ -266,7 +266,7 @@ public class LocalService implements Service {
     try (final Context ctx = executeTimer.start()) {
       try {
         final Meta.ExecuteResult executeResult = meta.execute(request.statementHandle,
-            request.parameterValues, request.maxRowCount);
+            request.parameterValues, AvaticaUtils.toSaturatedInt(request.maxRowCount));
 
         final List<ResultSetResponse> results = new ArrayList<>(executeResult.resultSets.size());
         for (Meta.MetaResultSet metaResultSet : executeResult.resultSets) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
index de6419a..7ee2226 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
@@ -19,6 +19,7 @@ package org.apache.calcite.avatica.remote;
 import org.apache.calcite.avatica.AvaticaConnection;
 import org.apache.calcite.avatica.AvaticaConnection.CallableWithoutException;
 import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.ConnectionPropertiesImpl;
 import org.apache.calcite.avatica.Meta;
@@ -245,7 +246,11 @@ class RemoteMeta extends MetaImpl {
 
   @Override public ExecuteResult prepareAndExecute(StatementHandle h, String sql, long maxRowCount,
       PrepareCallback callback) throws NoSuchStatementException {
-    return prepareAndExecute(h, sql, maxRowCount, (int) maxRowCount, callback);
+    // The old semantics were that maxRowCount was also treated as the maximum number of
+    // elements in the first Frame of results. A value of -1 would also preserve this, but an
+    // explicit (positive) number is easier to follow, IMO.
+    return prepareAndExecute(h, sql, maxRowCount, AvaticaUtils.toSaturatedInt(maxRowCount),
+        callback);
   }
 
   @Override public ExecuteResult prepareAndExecute(final StatementHandle h, final String sql,
@@ -324,7 +329,7 @@ class RemoteMeta extends MetaImpl {
 
   @Override public ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues,
       long maxRowCount) throws NoSuchStatementException {
-    return execute(h, parameterValues, (int) maxRowCount);
+    return execute(h, parameterValues, AvaticaUtils.toSaturatedInt(maxRowCount));
   }
 
   @Override public ExecuteResult execute(final StatementHandle h,

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/Service.java b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
index b3accd9..affed00 100644
--- a/avatica/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
@@ -19,6 +19,7 @@ package org.apache.calcite.avatica.remote;
 import org.apache.calcite.avatica.AvaticaClientRuntimeException;
 import org.apache.calcite.avatica.AvaticaConnection;
 import org.apache.calcite.avatica.AvaticaSeverity;
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.BuiltInConnectionProperty;
 import org.apache.calcite.avatica.ConnectionPropertiesImpl;
 import org.apache.calcite.avatica.Meta;
@@ -904,7 +905,7 @@ public interface Service {
 
     public PrepareAndExecuteRequest(String connectionId, int statementId, String sql,
         long maxRowCount) {
-      this(connectionId, statementId, sql, maxRowCount, (int) maxRowCount);
+      this(connectionId, statementId, sql, maxRowCount, AvaticaUtils.toSaturatedInt(maxRowCount));
     }
 
     @JsonCreator
@@ -3028,7 +3029,7 @@ public interface Service {
 
     public final String connectionId;
     public final int statementId;
-    public final int[] updateCounts;
+    public final long[] updateCounts;
     public final boolean missingStatement;
     public final RpcMetadataResponse rpcMetadata;
 
@@ -3043,7 +3044,7 @@ public interface Service {
     @JsonCreator
     public ExecuteBatchResponse(@JsonProperty("connectionId") String connectionId,
         @JsonProperty("statementId") int statementId,
-        @JsonProperty("updateCounts") int[] updateCounts,
+        @JsonProperty("updateCounts") long[] updateCounts,
         @JsonProperty("missingStatement") boolean missingStatement,
         @JsonProperty("rpcMetadata") RpcMetadataResponse rpcMetadata) {
       this.connectionId = connectionId;
@@ -3075,9 +3076,9 @@ public interface Service {
       Responses.ExecuteBatchResponse msg = ProtobufService.castProtobufMessage(genericMsg,
           Responses.ExecuteBatchResponse.class);
 
-      int[] updateCounts = new int[msg.getUpdateCountsCount()];
+      long[] updateCounts = new long[msg.getUpdateCountsCount()];
       int i = 0;
-      for (Integer updateCount : msg.getUpdateCountsList()) {
+      for (Long updateCount : msg.getUpdateCountsList()) {
         updateCounts[i++] = updateCount;
       }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/main/protobuf/responses.proto
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/protobuf/responses.proto b/avatica/core/src/main/protobuf/responses.proto
index 47d73ab..a3cd3d2 100644
--- a/avatica/core/src/main/protobuf/responses.proto
+++ b/avatica/core/src/main/protobuf/responses.proto
@@ -129,7 +129,7 @@ message RollbackResponse {
 message ExecuteBatchResponse {
   string connection_id = 1;
   uint32 statement_id = 2;
-  repeated uint32 update_counts = 3;
+  repeated uint64 update_counts = 3;
   bool missing_statement = 4; // Did the request fail because of no-cached statement
   RpcMetadata metadata = 5;
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaStatementTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaStatementTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaStatementTest.java
new file mode 100644
index 0000000..5f6b56a
--- /dev/null
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/AvaticaStatementTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.avatica;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test class for AvaticaStatement
+ */
+public class AvaticaStatementTest {
+
+  private AvaticaStatement statement;
+
+  @Before public void setup() {
+    statement = mock(AvaticaStatement.class);
+  }
+
+  @Test public void testUpdateCounts() throws SQLException {
+    long[] longValues = new long[] {-1, -3, 1, 5, ((long) Integer.MAX_VALUE) + 1};
+    int[] intValues = new int[] {-1, -3, 1, 5, Integer.MAX_VALUE};
+    when(statement.executeBatch()).thenCallRealMethod();
+    when(statement.executeLargeBatch()).thenCallRealMethod();
+    when(statement.executeBatchInternal()).thenReturn(longValues);
+
+    assertArrayEquals(intValues, statement.executeBatch());
+    assertArrayEquals(longValues, statement.executeLargeBatch());
+  }
+}
+
+// End AvaticaStatementTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
index d439c83..b3a08c0 100644
--- a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufSerializationTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica.remote;
 
+import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.ColumnMetaData.Rep;
 import org.apache.calcite.avatica.Meta.Signature;
 import org.apache.calcite.avatica.Meta.StatementHandle;
@@ -130,7 +131,8 @@ public class ProtobufSerializationTest {
           prepareAndExecuteReq.serialize();
       assertEquals(maxRowCount, prepareAndExecuteProtoReq.getMaxRowCount());
       assertEquals(maxRowCount, prepareAndExecuteProtoReq.getMaxRowsTotal());
-      assertEquals((int) maxRowCount, prepareAndExecuteProtoReq.getFirstFrameMaxSize());
+      assertEquals(AvaticaUtils.toSaturatedInt(maxRowCount),
+          prepareAndExecuteProtoReq.getFirstFrameMaxSize());
 
       assertEquals(prepareAndExecuteReq,
           prepareAndExecuteReq.deserialize(prepareAndExecuteProtoReq));
@@ -175,7 +177,8 @@ public class ProtobufSerializationTest {
 
     prepareAndExecuteReq = new Service.PrepareAndExecuteRequest().deserialize(protoPrepare);
     assertEquals(maxRowCount, prepareAndExecuteReq.maxRowCount);
-    assertEquals((int) maxRowCount, prepareAndExecuteReq.maxRowsInFirstFrame);
+    assertEquals(AvaticaUtils.toSaturatedInt(maxRowCount),
+        prepareAndExecuteReq.maxRowsInFirstFrame);
 
     // Both the new and old provided should default to the new (firstFrameMaxSize should be the
     // the same as what ultimately is set to maxRowCount)
@@ -185,7 +188,8 @@ public class ProtobufSerializationTest {
 
     prepareAndExecuteReq = new Service.PrepareAndExecuteRequest().deserialize(protoPrepare);
     assertEquals(maxRowCount, prepareAndExecuteReq.maxRowCount);
-    assertEquals((int) maxRowCount, prepareAndExecuteReq.maxRowsInFirstFrame);
+    assertEquals(AvaticaUtils.toSaturatedInt(maxRowCount),
+        prepareAndExecuteReq.maxRowsInFirstFrame);
 
     // Same as previous example, but explicitly setting maxRowsInFirstFrame too
     protoPrepare = Requests.PrepareAndExecuteRequest.newBuilder().

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
index 8dac427..5cf3208 100644
--- a/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
@@ -358,7 +358,7 @@ public class ProtobufTranslationImplTest<T> {
     responses.add(new CommitResponse());
     responses.add(new RollbackResponse());
 
-    int[] updateCounts = new int[]{1, 0, 1, 1};
+    long[] updateCounts = new long[]{1, 0, 1, 1};
     responses.add(
         new ExecuteBatchResponse("connectionId", 12345, updateCounts, false, rpcMetadata));
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
index 8929400..8905508 100644
--- a/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
+++ b/avatica/core/src/test/java/org/apache/calcite/avatica/test/AvaticaUtilsTest.java
@@ -30,6 +30,7 @@ import java.util.Properties;
 import java.util.Set;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
@@ -148,6 +149,15 @@ public class AvaticaUtilsTest {
     assertThat(env.getString(), is("  "));
   }
 
+  @Test public void testLongToIntegerTranslation() {
+    long[] longValues = new long[] {Integer.MIN_VALUE, -5, 0, 1, Integer.MAX_VALUE,
+      ((long) Integer.MAX_VALUE) + 1L, Long.MAX_VALUE};
+    int[] convertedValues = AvaticaUtils.toSaturatedInts(longValues);
+    int[] intValues = new int[] {Integer.MIN_VALUE, -5, 0, 1, Integer.MAX_VALUE,
+      Integer.MAX_VALUE, Integer.MAX_VALUE};
+    assertArrayEquals(convertedValues, intValues);
+  }
+
   /** Dummy implementation of {@link ConnectionProperty}. */
   private static class ConnectionPropertyImpl implements ConnectionProperty {
     private final String name;

http://git-wip-us.apache.org/repos/asf/calcite/blob/08c966b9/avatica/server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
----------------------------------------------------------------------
diff --git a/avatica/server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java b/avatica/server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
index c68041f..204421f 100644
--- a/avatica/server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
+++ b/avatica/server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
@@ -710,7 +710,8 @@ public class JdbcMeta implements ProtobufMeta {
 
   public ExecuteResult prepareAndExecute(StatementHandle h, String sql,
       long maxRowCount, PrepareCallback callback) throws NoSuchStatementException {
-    return prepareAndExecute(h, sql, maxRowCount, (int) maxRowCount, callback);
+    return prepareAndExecute(h, sql, maxRowCount, AvaticaUtils.toSaturatedInt(maxRowCount),
+        callback);
   }
 
   public ExecuteResult prepareAndExecute(StatementHandle h, String sql, long maxRowCount,
@@ -818,7 +819,7 @@ public class JdbcMeta implements ProtobufMeta {
 
   @Override public ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues,
       long maxRowCount) throws NoSuchStatementException {
-    return execute(h, parameterValues, (int) maxRowCount);
+    return execute(h, parameterValues, AvaticaUtils.toSaturatedInt(maxRowCount));
   }
 
   @Override public ExecuteResult execute(StatementHandle h,
@@ -912,7 +913,7 @@ public class JdbcMeta implements ProtobufMeta {
       }
 
       // Execute the batch and return the results
-      return new ExecuteBatchResult(stmt.executeBatch());
+      return new ExecuteBatchResult(AvaticaUtils.executeLargeBatch(stmt));
     } catch (SQLException e) {
       throw propagate(e);
     }
@@ -944,7 +945,7 @@ public class JdbcMeta implements ProtobufMeta {
         }
         preparedStmt.addBatch();
       }
-      return new ExecuteBatchResult(preparedStmt.executeBatch());
+      return new ExecuteBatchResult(AvaticaUtils.executeLargeBatch(preparedStmt));
     } catch (SQLException e) {
       throw propagate(e);
     }
@@ -967,7 +968,7 @@ public class JdbcMeta implements ProtobufMeta {
         }
         preparedStmt.addBatch();
       }
-      return new ExecuteBatchResult(preparedStmt.executeBatch());
+      return new ExecuteBatchResult(AvaticaUtils.executeLargeBatch(preparedStmt));
     } catch (SQLException e) {
       throw propagate(e);
     }


[02/14] calcite git commit: [CALCITE-1228] Bind parameters in INSERT

Posted by el...@apache.org.
[CALCITE-1228] Bind parameters in INSERT

Deduce number and types of bind parameters for all DML statements, in
validator and in simple JDBC tests.

Also infer types of bind parameters in other kinds of query.


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

Branch: refs/heads/branch-avatica-1.8
Commit: f191a386aedaee05c6ffcc1d4b155193bfa4ca01
Parents: b0a996b
Author: Julian Hyde <jh...@apache.org>
Authored: Tue May 17 19:05:34 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun May 22 12:46:46 2016 -0700

----------------------------------------------------------------------
 .../java/org/apache/calcite/sql/SqlSelect.java  |  2 +-
 .../org/apache/calcite/sql/SqlSetOperator.java  |  2 +-
 .../java/org/apache/calcite/sql/SqlUpdate.java  |  5 +-
 .../calcite/sql/advise/SqlAdvisorValidator.java |  5 +-
 .../sql/type/ExplicitOperandTypeInference.java  |  7 +-
 .../calcite/sql/validate/AbstractNamespace.java | 11 ++-
 .../calcite/sql/validate/AliasNamespace.java    |  2 +-
 .../calcite/sql/validate/CollectNamespace.java  |  2 +-
 .../sql/validate/DelegatingNamespace.java       |  4 +-
 .../calcite/sql/validate/FieldNamespace.java    |  2 +-
 .../sql/validate/IdentifierNamespace.java       |  2 +-
 .../calcite/sql/validate/JoinNamespace.java     |  2 +-
 .../sql/validate/ParameterNamespace.java        |  2 +-
 .../sql/validate/ProcedureNamespace.java        |  2 +-
 .../calcite/sql/validate/SchemaNamespace.java   |  2 +-
 .../calcite/sql/validate/SelectNamespace.java   |  4 +-
 .../calcite/sql/validate/SetopNamespace.java    |  4 +-
 .../calcite/sql/validate/SqlValidator.java      |  5 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  | 59 ++++++++-------
 .../sql/validate/SqlValidatorNamespace.java     |  9 ++-
 .../sql/validate/TableConstructorNamespace.java | 10 +--
 .../calcite/sql/validate/TableNamespace.java    |  2 +-
 .../calcite/sql/validate/UnnestNamespace.java   |  2 +-
 .../calcite/sql/validate/WithItemNamespace.java |  2 +-
 .../calcite/sql/validate/WithNamespace.java     |  4 +-
 .../calcite/sql/test/SqlOperatorBaseTest.java   | 21 ++---
 .../org/apache/calcite/sql/test/SqlTester.java  | 17 ++---
 .../apache/calcite/sql/test/SqlTesterImpl.java  | 33 ++++----
 .../org/apache/calcite/sql/test/SqlTests.java   | 10 +++
 .../calcite/test/JdbcFrontLinqBackTest.java     | 35 +++++++++
 .../apache/calcite/test/SqlValidatorTest.java   | 80 ++++++++++++++++----
 .../calcite/test/SqlValidatorTestCase.java      | 14 ++++
 32 files changed, 243 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/SqlSelect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSelect.java b/core/src/main/java/org/apache/calcite/sql/SqlSelect.java
index d1cb249..c054763 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSelect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSelect.java
@@ -205,7 +205,7 @@ public class SqlSelect extends SqlCall {
   }
 
   public void validate(SqlValidator validator, SqlValidatorScope scope) {
-    validator.validateQuery(this, scope);
+    validator.validateQuery(this, scope, validator.getUnknownType());
   }
 
   // Override SqlCall, to introduce a subquery frame.

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/SqlSetOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSetOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlSetOperator.java
index d4699f3..d6e128a 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSetOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSetOperator.java
@@ -87,7 +87,7 @@ public class SqlSetOperator extends SqlBinaryOperator {
       SqlValidator validator,
       SqlValidatorScope scope,
       SqlValidatorScope operandScope) {
-    validator.validateQuery(call, operandScope);
+    validator.validateQuery(call, operandScope, validator.getUnknownType());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
index 5d8ebfa..475d00d 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
@@ -71,13 +71,14 @@ public class SqlUpdate extends SqlCall {
 
   public List<SqlNode> getOperandList() {
     return ImmutableNullableList.of(targetTable, targetColumnList,
-        sourceExpressionList, condition, sourceSelect, alias);
+        sourceExpressionList, condition, alias);
   }
 
   @Override public void setOperand(int i, SqlNode operand) {
     switch (i) {
     case 0:
-      targetTable = (SqlIdentifier) operand;
+      assert operand instanceof SqlIdentifier;
+      targetTable = operand;
       break;
     case 1:
       targetColumnList = (SqlNodeList) operand;

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java
index a7228af..4d07e79 100644
--- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java
+++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java
@@ -185,11 +185,12 @@ public class SqlAdvisorValidator extends SqlValidatorImpl {
     }
   }
 
-  protected void validateNamespace(final SqlValidatorNamespace namespace) {
+  protected void validateNamespace(final SqlValidatorNamespace namespace,
+      RelDataType targetRowType) {
     // Only attempt to validate each namespace once. Otherwise if
     // validation fails, we may end up cycling.
     if (activeNamespaces.add(namespace)) {
-      super.validateNamespace(namespace);
+      super.validateNamespace(namespace, targetRowType);
     } else {
       namespace.setType(emptyStructType);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/type/ExplicitOperandTypeInference.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ExplicitOperandTypeInference.java b/core/src/main/java/org/apache/calcite/sql/type/ExplicitOperandTypeInference.java
index 684388e..64b1025 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ExplicitOperandTypeInference.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ExplicitOperandTypeInference.java
@@ -44,7 +44,12 @@ public class ExplicitOperandTypeInference implements SqlOperandTypeInference {
       SqlCallBinding callBinding,
       RelDataType returnType,
       RelDataType[] operandTypes) {
-    assert operandTypes.length == paramTypes.size();
+    if (operandTypes.length != paramTypes.size()) {
+      // This call does not match the inference strategy.
+      // It's likely that we're just about to give a validation error.
+      // Don't make a fuss, just give up.
+      return;
+    }
     paramTypes.toArray(operandTypes);
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
index c0c1971..9ddf257 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AbstractNamespace.java
@@ -75,7 +75,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
     return validator;
   }
 
-  public final void validate() {
+  public final void validate(RelDataType targetRowType) {
     switch (status) {
     case UNVALIDATED:
       try {
@@ -83,7 +83,7 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
         Util.permAssert(
             rowType == null,
             "Namespace.rowType must be null before validate has been called");
-        RelDataType type = validateImpl();
+        RelDataType type = validateImpl(targetRowType);
         Util.permAssert(
             type != null,
             "validateImpl() returned null");
@@ -116,12 +116,15 @@ abstract class AbstractNamespace implements SqlValidatorNamespace {
    * {@link #status} field to protect against cycles.
    *
    * @return record data type, never null
+   *
+   * @param targetRowType Desired row type, must not be null, may be the data
+   *                      type 'unknown'.
    */
-  protected abstract RelDataType validateImpl();
+  protected abstract RelDataType validateImpl(RelDataType targetRowType);
 
   public RelDataType getRowType() {
     if (rowType == null) {
-      validator.validateNamespace(this);
+      validator.validateNamespace(this, validator.unknownType);
       Util.permAssert(rowType != null, "validate must set rowType");
     }
     return rowType;

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
index d425ceb..a2192c9 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java
@@ -63,7 +63,7 @@ public class AliasNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     final List<String> nameList = new ArrayList<String>();
     final List<SqlNode> operands = call.getOperandList();
     final SqlValidatorNamespace childNs =

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java
index f076358..e06a945 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/CollectNamespace.java
@@ -64,7 +64,7 @@ public class CollectNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     return child.getOperator().deriveType(validator, scope, child);
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java
index 6dfb3fd..7794002 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingNamespace.java
@@ -68,8 +68,8 @@ public abstract class DelegatingNamespace implements SqlValidatorNamespace {
     return namespace.getType();
   }
 
-  public void validate() {
-    namespace.validate();
+  public void validate(RelDataType targetRowType) {
+    namespace.validate(targetRowType);
   }
 
   public SqlNode getNode() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java
index bf6fcbe..f685d37 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/FieldNamespace.java
@@ -49,7 +49,7 @@ class FieldNamespace extends AbstractNamespace {
     throw new UnsupportedOperationException();
   }
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     return rowType;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
index 7b46690..5133f50 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
@@ -100,7 +100,7 @@ public class IdentifierNamespace extends AbstractNamespace {
     }
   }
 
-  public RelDataType validateImpl() {
+  public RelDataType validateImpl(RelDataType targetRowType) {
     resolvedNamespace = parentScope.getTableNamespace(id.names);
     if (resolvedNamespace == null) {
       throw validator.newValidationError(id,

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java
index 464d84c..ded36b0 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java
@@ -38,7 +38,7 @@ class JoinNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     RelDataType leftType =
         validator.getNamespace(join.getLeft()).getRowType();
     RelDataType rightType =

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java
index 9d9d8ba..5fb41cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/ParameterNamespace.java
@@ -42,7 +42,7 @@ class ParameterNamespace extends AbstractNamespace {
     return null;
   }
 
-  public RelDataType validateImpl() {
+  public RelDataType validateImpl(RelDataType targetRowType) {
     return type;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java
index e436527..1b226a7 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/ProcedureNamespace.java
@@ -48,7 +48,7 @@ public class ProcedureNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  public RelDataType validateImpl() {
+  public RelDataType validateImpl(RelDataType targetRowType) {
     validator.inferUnknownTypes(validator.unknownType, scope, call);
     final RelDataType type = validator.deriveTypeImpl(scope, call);
     final SqlOperator operator = call.getOperator();

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
index e8347c4..49e21f3 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SchemaNamespace.java
@@ -40,7 +40,7 @@ class SchemaNamespace extends AbstractNamespace {
     this.names = Preconditions.checkNotNull(names);
   }
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     final RelDataTypeFactory.FieldInfoBuilder builder =
         validator.getTypeFactory().builder();
     for (SqlMoniker moniker

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
index 48a45c1..c0a2dec 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java
@@ -56,8 +56,8 @@ public class SelectNamespace extends AbstractNamespace {
     return select;
   }
 
-  public RelDataType validateImpl() {
-    validator.validateSelect(select, validator.unknownType);
+  public RelDataType validateImpl(RelDataType targetRowType) {
+    validator.validateSelect(select, targetRowType);
     return rowType;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java
index c2ec583..adc6e6d 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java
@@ -89,7 +89,7 @@ public class SetopNamespace extends AbstractNamespace {
     return SqlMonotonicity.NOT_MONOTONIC;
   }
 
-  public RelDataType validateImpl() {
+  public RelDataType validateImpl(RelDataType targetRowType) {
     switch (call.getKind()) {
     case UNION:
     case INTERSECT:
@@ -100,7 +100,7 @@ public class SetopNamespace extends AbstractNamespace {
           throw validator.newValidationError(operand,
               RESOURCE.needQueryOp(operand.toString()));
         }
-        validator.validateQuery(operand, scope);
+        validator.validateQuery(operand, scope, targetRowType);
       }
       return call.getOperator().deriveType(
           validator,

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
index 1adc9d8..605c665 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
@@ -166,9 +166,12 @@ public interface SqlValidator {
    *
    * @param node  Query node
    * @param scope Scope in which the query occurs
+   * @param targetRowType Desired row type, must not be null, may be the data
+   *                      type 'unknown'.
    * @throws RuntimeException if the query is not valid
    */
-  void validateQuery(SqlNode node, SqlValidatorScope scope);
+  void validateQuery(SqlNode node, SqlValidatorScope scope,
+      RelDataType targetRowType);
 
   /**
    * Returns the type assigned to a node by validation.

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index df95dd6..9551a16 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -335,6 +335,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       expandSelectItem(
           selectItem,
           select,
+          unknownType,
           list,
           new LinkedHashSet<String>(),
           types,
@@ -399,6 +400,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
   private boolean expandSelectItem(
       final SqlNode selectItem,
       SqlSelect select,
+      RelDataType targetType,
       List<SqlNode> selectItems,
       Set<String> aliases,
       List<Map.Entry<String, RelDataType>> types,
@@ -438,6 +440,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     selectItems.add(expanded);
     aliases.add(alias);
 
+    inferUnknownTypes(targetType, scope, expanded);
     final RelDataType type = deriveType(selectScope, expanded);
     setValidatedNodeTypeImpl(expanded, type);
     types.add(Pair.of(alias, type));
@@ -847,7 +850,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     return outermostNode;
   }
 
-  public void validateQuery(SqlNode node, SqlValidatorScope scope) {
+  public void validateQuery(SqlNode node, SqlValidatorScope scope,
+      RelDataType targetRowType) {
     final SqlValidatorNamespace ns = getNamespace(node, scope);
     if (node.getKind() == SqlKind.TABLESAMPLE) {
       List<SqlNode> operands = ((SqlCall) node).getOperandList();
@@ -861,7 +865,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       }
     }
 
-    validateNamespace(ns);
+    validateNamespace(ns, targetRowType);
     if (node == top) {
       validateModality(node);
     }
@@ -873,9 +877,14 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
   /**
    * Validates a namespace.
+   *
+   * @param namespace Namespace
+   * @param targetRowType Desired row type, must not be null, may be the data
+   *                      type 'unknown'.
    */
-  protected void validateNamespace(final SqlValidatorNamespace namespace) {
-    namespace.validate();
+  protected void validateNamespace(final SqlValidatorNamespace namespace,
+      RelDataType targetRowType) {
+    namespace.validate(targetRowType);
     if (namespace.getNode() != null) {
       setValidatedNodeType(namespace.getNode(), namespace.getType());
     }
@@ -1629,25 +1638,19 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
         inferUnknownTypes(type, scope, child);
       }
     } else if (node instanceof SqlCase) {
-      // REVIEW wael: can this be done in a paramtypeinference strategy
-      // object?
-      SqlCase caseCall = (SqlCase) node;
-      RelDataType returnType = deriveType(scope, node);
+      final SqlCase caseCall = (SqlCase) node;
 
-      SqlNodeList whenList = caseCall.getWhenOperands();
-      for (int i = 0; i < whenList.size(); i++) {
-        SqlNode sqlNode = whenList.get(i);
-        inferUnknownTypes(unknownType, scope, sqlNode);
+      final RelDataType whenType =
+          caseCall.getValueOperand() == null ? booleanType : unknownType;
+      for (SqlNode sqlNode : caseCall.getWhenOperands().getList()) {
+        inferUnknownTypes(whenType, scope, sqlNode);
       }
-      SqlNodeList thenList = caseCall.getThenOperands();
-      for (int i = 0; i < thenList.size(); i++) {
-        SqlNode sqlNode = thenList.get(i);
+      RelDataType returnType = deriveType(scope, node);
+      for (SqlNode sqlNode : caseCall.getThenOperands().getList()) {
         inferUnknownTypes(returnType, scope, sqlNode);
       }
 
-      if (!SqlUtil.isNullLiteral(
-          caseCall.getElseOperand(),
-          false)) {
+      if (!SqlUtil.isNullLiteral(caseCall.getElseOperand(), false)) {
         inferUnknownTypes(
             returnType,
             scope,
@@ -2787,13 +2790,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       validateOver((SqlCall) node, scope);
       break;
     default:
-      validateQuery(node, scope);
+      validateQuery(node, scope, targetRowType);
       break;
     }
 
     // Validate the namespace representation of the node, just in case the
     // validation did not occur implicitly.
-    getNamespace(node, scope).validate();
+    getNamespace(node, scope).validate(targetRowType);
   }
 
   protected void validateOver(SqlCall call, SqlValidatorScope scope) {
@@ -3220,7 +3223,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
   public void validateWith(SqlWith with, SqlValidatorScope scope) {
     final SqlValidatorNamespace namespace = getNamespace(with);
-    validateNamespace(namespace);
+    validateNamespace(namespace, unknownType);
   }
 
   public void validateWithItem(SqlWithItem withItem) {
@@ -3499,6 +3502,10 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
         expandSelectItem(
             selectItem,
             select,
+            targetRowType.isStruct()
+                && targetRowType.getFieldCount() >= i
+                ? targetRowType.getFieldList().get(i).getType()
+                : unknownType,
             expandedSelectItems,
             aliases,
             fieldList,
@@ -3656,7 +3663,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
   public void validateInsert(SqlInsert insert) {
     SqlValidatorNamespace targetNamespace = getNamespace(insert);
-    validateNamespace(targetNamespace);
+    validateNamespace(targetNamespace, unknownType);
     SqlValidatorTable table = targetNamespace.getTable();
 
     // INSERT has an optional column name list.  If present then
@@ -3674,7 +3681,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       validateSelect(sqlSelect, targetRowType);
     } else {
       SqlValidatorScope scope = scopes.get(source);
-      validateQuery(source, scope);
+      validateQuery(source, scope, targetRowType);
     }
 
     // REVIEW jvs 4-Dec-2008: In FRG-365, this namespace row type is
@@ -3813,7 +3820,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     IdentifierNamespace targetNamespace =
         getNamespace(call.getTargetTable()).unwrap(
             IdentifierNamespace.class);
-    validateNamespace(targetNamespace);
+    validateNamespace(targetNamespace, unknownType);
     SqlValidatorTable table = targetNamespace.getTable();
 
     validateAccess(call.getTargetTable(), table, SqlAccessEnum.DELETE);
@@ -3823,7 +3830,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     IdentifierNamespace targetNamespace =
         getNamespace(call.getTargetTable()).unwrap(
             IdentifierNamespace.class);
-    validateNamespace(targetNamespace);
+    validateNamespace(targetNamespace, unknownType);
     SqlValidatorTable table = targetNamespace.getTable();
 
     RelDataType targetRowType =
@@ -3857,7 +3864,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     // Let's use the update/insert targetRowType when available.
     IdentifierNamespace targetNamespace =
         (IdentifierNamespace) getNamespace(call.getTargetTable());
-    validateNamespace(targetNamespace);
+    validateNamespace(targetNamespace, unknownType);
 
     SqlValidatorTable table = targetNamespace.getTable();
     validateAccess(call.getTargetTable(), table, SqlAccessEnum.UPDATE);

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java
index 007028d..db9fd3a 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java
@@ -83,7 +83,7 @@ public interface SqlValidatorNamespace {
    * Sets the type of this namespace.
    *
    * <p>Allows the type for the namespace to be explicitly set, but usually is
-   * called during {@link #validate()}.</p>
+   * called during {@link #validate(RelDataType)}.</p>
    *
    * <p>Implicitly also sets the row type. If the type is not a struct, then
    * the row type is the type wrapped as a struct with a single column,
@@ -105,8 +105,11 @@ public interface SqlValidatorNamespace {
    *
    * <p>Please call {@link SqlValidatorImpl#validateNamespace} rather than
    * calling this method directly.</p>
+   *
+   * @param targetRowType Desired row type, must not be null, may be the data
+   *                      type 'unknown'.
    */
-  void validate();
+  void validate(RelDataType targetRowType);
 
   /**
    * Returns the parse tree node at the root of this namespace.
@@ -192,7 +195,7 @@ public interface SqlValidatorNamespace {
    * (the body of the with-item). An {@link IdentifierNamespace} typically
    * resolves to a {@link TableNamespace}.</p>
    *
-   * <p>You must not call this method before {@link #validate()} has
+   * <p>You must not call this method before {@link #validate(RelDataType)} has
    * completed.</p> */
   SqlValidatorNamespace resolve();
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java
index da8a22f..b590f61 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/TableConstructorNamespace.java
@@ -18,7 +18,6 @@ package org.apache.calcite.sql.validate;
 
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlCall;
-import org.apache.calcite.sql.SqlInsert;
 import org.apache.calcite.sql.SqlNode;
 
 import static org.apache.calcite.util.Static.RESOURCE;
@@ -54,16 +53,9 @@ public class TableConstructorNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     // First, validate the VALUES. If VALUES is inside INSERT, infers
     // the type of NULL values based on the types of target columns.
-    final RelDataType targetRowType;
-    if (enclosingNode instanceof SqlInsert) {
-      SqlInsert node = (SqlInsert) enclosingNode;
-      targetRowType = validator.getValidatedNodeType(node);
-    } else {
-      targetRowType = validator.getUnknownType();
-    }
     validator.validateValues(values, targetRowType, scope);
     final RelDataType tableConstructorRowType =
         validator.getTableConstructorRowType(values, scope);

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java
index 044d41b..d354c5d 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/TableNamespace.java
@@ -44,7 +44,7 @@ class TableNamespace extends AbstractNamespace {
     this(validator, table, ImmutableList.<RelDataTypeField>of());
   }
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     if (extendedFields.isEmpty()) {
       return table.getRowType();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java
index 68c1108..3eee0a8 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java
@@ -47,7 +47,7 @@ class UnnestNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     // Validate the call and its arguments, and infer the return type.
     validator.validateCall(unnest, scope);
     RelDataType type =

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java
index 040f080..8173349 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java
@@ -34,7 +34,7 @@ class WithItemNamespace extends AbstractNamespace {
     this.withItem = withItem;
   }
 
-  @Override protected RelDataType validateImpl() {
+  @Override protected RelDataType validateImpl(RelDataType targetRowType) {
     final SqlValidatorNamespace childNs =
         validator.getNamespace(withItem.query);
     final RelDataType rowType = childNs.getRowTypeSansSystemColumns();

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java
index d3cfb9a..e02e794 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java
@@ -48,13 +48,13 @@ public class WithNamespace extends AbstractNamespace {
 
   //~ Methods ----------------------------------------------------------------
 
-  protected RelDataType validateImpl() {
+  protected RelDataType validateImpl(RelDataType targetRowType) {
     for (SqlNode withItem : with.withList) {
       validator.validateWithItem((SqlWithItem) withItem);
     }
     final SqlValidatorScope scope2 =
         validator.getWithScope(Util.last(with.withList.getList()));
-    validator.validateQuery(with.body, scope2);
+    validator.validateQuery(with.body, scope2, targetRowType);
     final RelDataType rowType = validator.getValidatedNodeType(with.body);
     validator.setValidatedNodeType(with, rowType);
     return rowType;

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 6c24eee..a9ab66f 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -1880,7 +1880,7 @@ public abstract class SqlOperatorBaseTest {
     // both are valid
     tester.check(
         "values 1 > 2 and sqrt(-4) = -2",
-        SqlTests.BOOLEAN_TYPE_CHECKER,
+        SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
         new ValueOrExceptionResultChecker(
             Boolean.FALSE, INVALID_ARG_FOR_POWER, CODE_2201F));
   }
@@ -2759,7 +2759,7 @@ public abstract class SqlOperatorBaseTest {
     // get error
     tester.check(
         "values 1 < cast(null as integer) or sqrt(-4) = -2",
-        SqlTests.BOOLEAN_TYPE_CHECKER,
+        SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
         new ValueOrExceptionResultChecker(
             null, INVALID_ARG_FOR_POWER, CODE_2201F));
 
@@ -2769,7 +2769,7 @@ public abstract class SqlOperatorBaseTest {
     // both are valid.
     tester.check(
         "values 1 < 2 or sqrt(-4) = -2",
-        SqlTests.BOOLEAN_TYPE_CHECKER,
+        SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
         new ValueOrExceptionResultChecker(
             Boolean.TRUE, INVALID_ARG_FOR_POWER, CODE_2201F));
 
@@ -2779,7 +2779,7 @@ public abstract class SqlOperatorBaseTest {
     // both are valid.
     tester.check(
         "values 1 < cast(null as integer) or sqrt(4) = -2",
-        SqlTests.BOOLEAN_TYPE_CHECKER,
+        SqlTests.BOOLEAN_TYPE_CHECKER, SqlTests.ANY_PARAMETER_CHECKER,
         new ValueOrExceptionResultChecker(
             null, INVALID_ARG_FOR_POWER, CODE_2201F));
 
@@ -5419,7 +5419,7 @@ public abstract class SqlOperatorBaseTest {
                 query = SqlTesterImpl.buildQuery(s);
               }
               tester.check(query, SqlTests.ANY_TYPE_CHECKER,
-                  SqlTests.ANY_RESULT_CHECKER);
+                  SqlTests.ANY_PARAMETER_CHECKER, SqlTests.ANY_RESULT_CHECKER);
             }
           } catch (Error e) {
             System.out.println(s + ": " + e.getMessage());
@@ -5558,14 +5558,9 @@ public abstract class SqlOperatorBaseTest {
       super(testFactory);
     }
 
-    @Override public void check(
-        String query,
-        TypeChecker typeChecker,
-        ResultChecker resultChecker) {
-      super.check(
-          query,
-          typeChecker,
-          resultChecker);
+    @Override public void check(String query, TypeChecker typeChecker,
+        ParameterChecker parameterChecker, ResultChecker resultChecker) {
+      super.check(query, typeChecker, parameterChecker, resultChecker);
       //noinspection unchecked
       final CalciteAssert.ConnectionFactory connectionFactory =
           (CalciteAssert.ConnectionFactory)

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/sql/test/SqlTester.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTester.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTester.java
index 2873c6d..95a02d4 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTester.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTester.java
@@ -269,12 +269,15 @@ public interface SqlTester extends AutoCloseable, SqlValidatorTestCase.Tester {
    * @param query         SQL query
    * @param typeChecker   Checks whether the result is the expected type; must
    *                      not be null
+   * @param parameterChecker Checks whether the parameters are of expected
+   *                      types
    * @param resultChecker Checks whether the result has the expected value;
    *                      must not be null
    */
   void check(
       String query,
       TypeChecker typeChecker,
+      ParameterChecker parameterChecker,
       ResultChecker resultChecker);
 
   /**
@@ -297,15 +300,6 @@ public interface SqlTester extends AutoCloseable, SqlValidatorTestCase.Tester {
       VmName... unimplementedVmNames);
 
   /**
-   * Checks to see if this tester is for the given VmName. Return false if
-   * no vm associated with this tester.
-   *
-   * @param vmName VmName to check for.
-   * @return whether or not this tester is for the given VmName.
-   */
-  boolean isVm(VmName vmName);
-
-  /**
    * Checks that an aggregate expression returns the expected result.
    *
    * <p>For example, <code>checkAgg("AVG(DISTINCT x)", new String[] {"2", "3",
@@ -383,6 +377,11 @@ public interface SqlTester extends AutoCloseable, SqlValidatorTestCase.Tester {
     void checkType(RelDataType type);
   }
 
+  /** Parameter checker. */
+  interface ParameterChecker {
+    void checkParameters(RelDataType parameterRowType);
+  }
+
   /** Result checker. */
   interface ResultChecker {
     void checkResult(ResultSet result) throws Exception;

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
index 4770344..f9f1bd1 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTesterImpl.java
@@ -440,25 +440,31 @@ public class SqlTesterImpl implements SqlTester {
       TypeChecker typeChecker,
       Object result,
       double delta) {
-    check(
-        query,
-        typeChecker,
+    check(query, typeChecker, SqlTests.ANY_PARAMETER_CHECKER,
         SqlTests.createChecker(result, delta));
   }
 
-  public void check(
-      String query,
-      TypeChecker typeChecker,
-      ResultChecker resultChecker) {
+  public void check(String query, TypeChecker typeChecker,
+      ParameterChecker parameterChecker, ResultChecker resultChecker) {
     // This implementation does NOT check the result!
-    // (It can't because we're pure Java.)
     // All it does is check the return type.
 
-    // Parse and validate. There should be no errors.
-    RelDataType actualType = getColumnType(query);
+    if (typeChecker == null) {
+      // Parse and validate. There should be no errors.
+      Util.discard(getResultType(query));
+    } else {
+      // Parse and validate. There should be no errors.
+      // There must be 1 column. Get its type.
+      RelDataType actualType = getColumnType(query);
+
+      // Check result type.
+      typeChecker.checkType(actualType);
+    }
 
-    // Check result type.
-    typeChecker.checkType(actualType);
+    SqlValidator validator = getValidator();
+    SqlNode n = parseAndValidate(validator, query);
+    final RelDataType parameterRowType = validator.getParameterRowType(n);
+    parameterChecker.checkParameters(parameterRowType);
   }
 
   public void checkMonotonic(String query,
@@ -681,9 +687,6 @@ public class SqlTesterImpl implements SqlTester {
     };
   }
 
-  public boolean isVm(VmName vmName) {
-    return false;
-  }
 }
 
 // End SqlTesterImpl.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/sql/test/SqlTests.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTests.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTests.java
index d5889fb..ea4091b 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTests.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTests.java
@@ -27,6 +27,7 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.regex.Pattern;
 
+import static org.apache.calcite.sql.test.SqlTester.ParameterChecker;
 import static org.apache.calcite.sql.test.SqlTester.ResultChecker;
 import static org.apache.calcite.sql.test.SqlTester.TypeChecker;
 
@@ -58,6 +59,15 @@ public abstract class SqlTests {
       };
 
   /**
+   * Checker that allows any number or type of parameters.
+   */
+  public static final ParameterChecker ANY_PARAMETER_CHECKER =
+      new ParameterChecker() {
+        public void checkParameters(RelDataType parameterRowType) {
+        }
+      };
+
+  /**
    * Helper function to get the string representation of a RelDataType
    * (include precision/scale but no charset or collation)
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java b/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
index c09fb0b..58c7783 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
@@ -30,6 +30,9 @@ import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.schema.impl.AbstractSchema;
 import org.apache.calcite.schema.impl.AbstractTableQueryable;
 
+import com.google.common.base.Function;
+import com.google.common.base.Throwables;
+
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -48,7 +51,9 @@ import java.util.Properties;
 import static org.apache.calcite.test.CalciteAssert.hr;
 import static org.apache.calcite.test.CalciteAssert.that;
 
+import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -239,6 +244,36 @@ public class JdbcFrontLinqBackTest {
             "name=Sebastian; C=2");
   }
 
+  @Test public void testInsertBind() throws Exception {
+    final List<JdbcTest.Employee> employees = new ArrayList<>();
+    CalciteAssert.AssertThat with = mutable(employees);
+    with.query("select count(*) as c from \"foo\".\"bar\"")
+        .returns("C=1\n");
+    with.doWithConnection(
+        new Function<CalciteConnection, Object>() {
+          public Object apply(CalciteConnection c) {
+            try {
+              final String sql = "insert into \"foo\".\"bar\"\n"
+                  + "values (?, 0, ?, 10.0, null)";
+              try (PreparedStatement p = c.prepareStatement(sql)) {
+                p.setInt(1, 1);
+                p.setString(2, "foo");
+                final int count = p.executeUpdate();
+                assertThat(count, is(1));
+              }
+              return null;
+            } catch (SQLException e) {
+              throw Throwables.propagate(e);
+            }
+          }
+        });
+    with.query("select count(*) as c from \"foo\".\"bar\"")
+        .returns("C=2\n");
+    with.query("select * from \"foo\".\"bar\"")
+        .returnsUnordered("empid=0; deptno=0; name=first; salary=0.0; commission=null",
+            "empid=1; deptno=0; name=foo; salary=10.0; commission=null");
+  }
+
   /**
    * Creates the post processor routine to be applied against a Connection.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index bc8c5e4..4a29961 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -70,7 +70,6 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
    * yellow in Intellij and maybe someone will fix them.
    */
   protected static final boolean TODO = false;
-  public static final boolean TODO_TYPE_INFERENCE = false;
   private static final String ANY = "(?s).*";
 
   protected static final Logger LOGGER =
@@ -6560,19 +6559,21 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
   }
 
   @Test public void testBind() {
-    check("select * from emp where deptno = ?");
-    check("select * from emp where deptno = ? and sal < 100000");
-    if (TODO_TYPE_INFERENCE) {
-      check("select case when deptno = ? then 1 else 2 end from emp");
-    }
-    if (TODO_TYPE_INFERENCE) {
-      check("select deptno from emp group by substring(name from ? for ?)");
-    }
-    if (TODO_TYPE_INFERENCE) {
-      check("select deptno from emp\n"
-          + "group by case when deptno = ? then 1 else 2 end");
-    }
-    check("select 1 from emp having sum(sal) < ?");
+    sql("select * from emp where deptno = ?").ok();
+    sql("select * from emp where deptno = ? and sal < 100000").ok();
+    sql("select case when deptno = ? then 1 else 2 end from emp").ok();
+    // It is not possible to infer type of ?, because SUBSTRING is overloaded
+    sql("select deptno from emp group by substring(name from ^?^ for ?)")
+        .fails("Illegal use of dynamic parameter");
+    // In principle we could infer that ? should be a VARCHAR
+    sql("select count(*) from emp group by position(^?^ in ename)")
+        .fails("Illegal use of dynamic parameter");
+    sql("select ^deptno^ from emp\n"
+        + "group by case when deptno = ? then 1 else 2 end")
+        .fails("Expression 'DEPTNO' is not being grouped");
+    sql("select deptno from emp\n"
+        + "group by deptno, case when deptno = ? then 1 else 2 end").ok();
+    sql("select 1 from emp having sum(sal) < ?").ok();
   }
 
   @Test public void testUnnest() {
@@ -7451,6 +7452,57 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         + "select 1, 1 from (values 'a')");
   }
 
+  @Test public void testInsertBind() {
+    // VALUES
+    sql("insert into emp (empno, deptno) values (?, ?)")
+        .ok()
+        .bindType("RecordType(INTEGER ?0, INTEGER ?1)");
+
+    // multiple VALUES
+    sql("insert into emp (empno, deptno) values (?, 1), (2, ?), (3, null)")
+        .ok()
+        .bindType("RecordType(INTEGER ?0, INTEGER ?1)");
+
+    // VALUES with expression
+    sql("insert into emp (ename, deptno) values (?, ? + 1)")
+        .ok()
+        .bindType("RecordType(VARCHAR(20) ?0, INTEGER ?1)");
+
+    // SELECT
+    sql("insert into emp (ename, deptno) select ?, ? from (values (1))")
+        .ok()
+        .bindType("RecordType(VARCHAR(20) ?0, INTEGER ?1)");
+
+    // WITH
+    final String sql = "insert into emp (ename, deptno)\n"
+        + "with v as (values ('a'))\n"
+        + "select ?, ? from (values (1))";
+    sql(sql).ok().bindType("RecordType(VARCHAR(20) ?0, INTEGER ?1)");
+
+    // UNION
+    final String sql2 = "insert into emp (ename, deptno)\n"
+        + "select ?, ? from (values (1))\n"
+        + "union all\n"
+        + "select ?, ? from (values (time '1:2:3'))";
+    final String expected2 = "RecordType(VARCHAR(20) ?0, INTEGER ?1,"
+        + " VARCHAR(20) ?2, INTEGER ?3)";
+    sql(sql2).ok().bindType(expected2);
+  }
+
+  @Test public void testUpdateBind() {
+    final String sql = "update emp\n"
+        + "set ename = ?\n"
+        + "where deptno = ?";
+    sql(sql).ok().bindType("RecordType(VARCHAR(20) ?0, INTEGER ?1)");
+  }
+
+  @Test public void testDeleteBind() {
+    final String sql = "delete from emp\n"
+        + "where deptno = ?\n"
+        + "or ename = ?";
+    sql(sql).ok().bindType("RecordType(INTEGER ?0, VARCHAR(20) ?1)");
+  }
+
   @Test public void testStream() {
     sql("select stream * from orders").ok();
     sql("select stream * from ^emp^")

http://git-wip-us.apache.org/repos/asf/calcite/blob/f191a386/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
index 6860851..025de3a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
@@ -25,6 +25,7 @@ import org.apache.calcite.sql.parser.SqlParserUtil;
 import org.apache.calcite.sql.test.DefaultSqlTestFactory;
 import org.apache.calcite.sql.test.SqlTester;
 import org.apache.calcite.sql.test.SqlTesterImpl;
+import org.apache.calcite.sql.test.SqlTests;
 import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.sql.validate.SqlValidator;
@@ -35,6 +36,8 @@ import java.nio.charset.Charset;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 /**
@@ -566,6 +569,17 @@ public class SqlValidatorTestCase {
       tester.checkMonotonic(sql, expectedMonotonicity);
       return this;
     }
+
+    public Sql bindType(final String bindType) {
+      tester.check(sql, null,
+          new SqlTester.ParameterChecker() {
+            public void checkParameters(RelDataType parameterRowType) {
+              assertThat(parameterRowType.toString(), is(bindType));
+            }
+          },
+          SqlTests.ANY_RESULT_CHECKER);
+      return this;
+    }
   }
 }