calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [2/3] calcite git commit: [CALCITE-855] UNNEST with multiple arguments
Date Fri, 06 May 2016 06:32:12 GMT
[CALCITE-855] UNNEST with multiple arguments

Change a few FlatList contructor methods to return ComparableList.


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

Branch: refs/heads/master
Commit: 354e8240d49de42bd6972d985fea692e4785e8b0
Parents: 6031b7a
Author: Julian Hyde <jhyde@apache.org>
Authored: Wed May 4 15:25:00 2016 -0700
Committer: Julian Hyde <jhyde@apache.org>
Committed: Thu May 5 22:07:12 2016 -0700

----------------------------------------------------------------------
 .../adapter/enumerable/EnumerableUncollect.java |  58 +++------
 .../org/apache/calcite/runtime/FlatLists.java   |  29 +++--
 .../apache/calcite/runtime/SqlFunctions.java    | 129 ++++++++++++++++++-
 .../apache/calcite/sql/SqlUnnestOperator.java   |  36 +++---
 .../sql/type/ComparableOperandTypeChecker.java  |  18 +--
 .../sql/type/CompositeOperandTypeChecker.java   |  30 ++++-
 .../type/CompositeSingleOperandTypeChecker.java |   2 +-
 .../apache/calcite/sql/type/OperandTypes.java   |  24 +++-
 .../sql/type/SameOperandTypeChecker.java        |  12 +-
 .../calcite/sql2rel/SqlToRelConverter.java      |  20 ++-
 .../org/apache/calcite/util/BuiltInMethod.java  |   2 +-
 .../java/org/apache/calcite/test/JdbcTest.java  |  38 ++++++
 .../java/org/apache/calcite/util/UtilTest.java  |  55 ++++++--
 .../linq4j/CartesianProductEnumerator.java      |  14 +-
 .../java/org/apache/calcite/linq4j/Linq4j.java  |  23 +++-
 15 files changed, 360 insertions(+), 130 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 20bbaf2..0dfa57f 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
@@ -19,15 +19,15 @@ package org.apache.calcite.adapter.enumerable;
 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.ParameterExpression;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 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.util.BuiltInMethod;
-import org.apache.calcite.util.ImmutableIntList;
+import org.apache.calcite.util.IntList;
 
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -83,48 +83,24 @@ public class EnumerableUncollect extends Uncollect implements EnumerableRel
{
             JavaRowFormat.LIST);
 
     // final Enumerable<List<Employee>> child = <<child adapter>>;
-    // return child.selectMany(LIST_TO_ENUMERABLE);
+    // return child.selectMany(FLAT_PRODUCT);
     final Expression child_ =
         builder.append(
             "child", result.block);
-    final Expression lambda;
-    if (withOrdinality) {
-      final BlockBuilder builder2 = new BlockBuilder();
-      final ParameterExpression o_ = Expressions.parameter(Modifier.FINAL,
-          result.physType.getJavaRowType(),
-          "o");
-      final Expression list_ = builder2.append("list",
-          Expressions.new_(ArrayList.class));
-      final ParameterExpression i_ = Expressions.parameter(int.class, "i");
-      final BlockBuilder builder3 = new BlockBuilder();
-      final Expression v_ =
-          builder3.append("v",
-              Expressions.call(o_, BuiltInMethod.LIST_GET.method, i_));
-      final List<Expression> expressions = new ArrayList<>();
-      final PhysType componentPhysType = result.physType.component(0);
-      final int fieldCount = componentPhysType.getRowType().getFieldCount();
-      expressions.addAll(
-          componentPhysType.accessors(v_,
-              ImmutableIntList.identity(fieldCount)));
-      expressions.add(Expressions.add(i_, Expressions.constant(1)));
-      builder3.add(
-          Expressions.statement(
-              Expressions.call(list_, BuiltInMethod.COLLECTION_ADD.method,
-                  physType.record(expressions))));
-      builder2.add(
-          Expressions.for_(
-              Expressions.declare(0, i_, Expressions.constant(0)),
-              Expressions.lessThan(i_,
-                  Expressions.call(o_, BuiltInMethod.COLLECTION_SIZE.method)),
-              Expressions.postIncrementAssign(i_),
-              builder3.toBlock()));
-      builder2.add(
-          Expressions.return_(null,
-              Expressions.call(BuiltInMethod.AS_ENUMERABLE2.method, list_)));
-      lambda = Expressions.lambda(builder2.toBlock(), o_);
-    } else {
-      lambda = Expressions.call(BuiltInMethod.LIST_TO_ENUMERABLE.method);
+    final List<Integer> fieldCounts = 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());
+      } else {
+        fieldCounts.add(-1);
+      }
     }
+    final Expression lambda =
+        Expressions.call(BuiltInMethod.FLAT_PRODUCT.method,
+            Expressions.constant(IntList.toArray(fieldCounts)),
+            Expressions.constant(withOrdinality));
     builder.add(
         Expressions.return_(null,
             Expressions.call(child_,

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
index 5e8d046..eba51b7 100644
--- a/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
+++ b/core/src/main/java/org/apache/calcite/runtime/FlatLists.java
@@ -37,7 +37,7 @@ public class FlatLists {
       new ComparableEmptyList();
 
   /** Creates a flat list with 0 elements. */
-  public static <T> List<T> of() {
+  public static <T> ComparableList<T> of() {
     //noinspection unchecked
     return COMPARABLE_EMPTY_LIST;
   }
@@ -135,23 +135,24 @@ public class FlatLists {
    * @param t Array of members of list
    * @return List containing the given members
    */
-  private static <T extends Comparable> List<T> flatList_(T[] t, boolean copy)
{
+  private static <T extends Object & Comparable> ComparableList<T>
+  flatList_(T[] t, boolean copy) {
     switch (t.length) {
     case 0:
       //noinspection unchecked
       return COMPARABLE_EMPTY_LIST;
     case 1:
-      return Collections.singletonList(t[0]);
+      return new Flat1List<>(t[0]);
     case 2:
-      return new Flat2List<T>(t[0], t[1]);
+      return new Flat2List<>(t[0], t[1]);
     case 3:
-      return new Flat3List<T>(t[0], t[1], t[2]);
+      return new Flat3List<>(t[0], t[1], t[2]);
     case 4:
-      return new Flat4List<T>(t[0], t[1], t[2], t[3]);
+      return new Flat4List<>(t[0], t[1], t[2], t[3]);
     case 5:
-      return new Flat5List<T>(t[0], t[1], t[2], t[3], t[4]);
+      return new Flat5List<>(t[0], t[1], t[2], t[3], t[4]);
     case 6:
-      return new Flat6List<T>(t[0], t[1], t[2], t[3], t[4], t[5]);
+      return new Flat6List<>(t[0], t[1], t[2], t[3], t[4], t[5]);
     default:
       // REVIEW: AbstractList contains a modCount field; we could
       //   write our own implementation and reduce creation overhead a
@@ -202,12 +203,21 @@ public class FlatLists {
    * @return List containing the given members
    */
   public static <T> List<T> of(List<T> t) {
+    return of_(t);
+  }
+
+  public static <T extends Comparable> ComparableList<T>
+  ofComparable(List<T> t) {
+    return of_(t);
+  }
+
+  private static <T> ComparableList<T> of_(List<T> t) {
     switch (t.size()) {
     case 0:
       //noinspection unchecked
       return COMPARABLE_EMPTY_LIST;
     case 1:
-      return Collections.singletonList(t.get(0));
+      return new Flat1List<>(t.get(0));
     case 2:
       return new Flat2List<>(t.get(0), t.get(1));
     case 3:
@@ -1239,6 +1249,7 @@ public class FlatLists {
       return a.compareTo(b);
     }
   }
+
 }
 
 // End FlatLists.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 d9657b2..eed92f5 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -19,7 +19,10 @@ package org.apache.calcite.runtime;
 import org.apache.calcite.DataContext;
 import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.linq4j.AbstractEnumerable;
+import org.apache.calcite.linq4j.CartesianProductEnumerator;
 import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.function.Deterministic;
 import org.apache.calcite.linq4j.function.Function1;
@@ -32,6 +35,8 @@ import java.math.MathContext;
 import java.sql.SQLException;
 import java.sql.Timestamp;
 import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -67,6 +72,28 @@ public class SqlFunctions {
         }
       };
 
+  private static final Function1<Object[], Enumerable<Object[]>>
+  ARRAY_CARTESIAN_PRODUCT =
+      new Function1<Object[], Enumerable<Object[]>>() {
+        public Enumerable<Object[]> apply(Object[] lists) {
+          final List<Enumerator<Object>> enumerators = new ArrayList<>();
+          for (Object list : lists) {
+            enumerators.add(Linq4j.enumerator((List) list));
+          }
+          final Enumerator<List<Object>> product = Linq4j.product(enumerators);
+          return new AbstractEnumerable<Object[]>() {
+            public Enumerator<Object[]> enumerator() {
+              return Linq4j.transform(product,
+                  new Function1<List<Object>, Object[]>() {
+                    public Object[] apply(List<Object> list) {
+                      return list.toArray();
+                    }
+                  });
+            }
+          };
+        }
+      };
+
   /** Holds, for each thread, a map from sequence name to sequence current
    * value.
    *
@@ -1423,15 +1450,109 @@ public class SqlFunctions {
     }
   }
 
-  /** Returns a lambda that converts a list to an enumerable. */
-  public static <E> Function1<List<E>, Enumerable<E>> listToEnumerable()
{
-    //noinspection unchecked
-    return (Function1<List<E>, Enumerable<E>>) (Function1) LIST_AS_ENUMERABLE;
+  public static <E extends Comparable>
+  Function1<Object, Enumerable<FlatLists.ComparableList<E>>>
+  flatProduct(final int[] fieldCounts, final boolean withOrdinality) {
+    if (fieldCounts.length == 1) {
+      if (!withOrdinality) {
+        //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<FlatLists.ComparableList<E>>>()
{
+      public Enumerable<FlatLists.ComparableList<E>> apply(Object lists) {
+        return p2((Object[]) lists, fieldCounts, withOrdinality);
+      }
+    };
+  }
+
+  private static <E extends Comparable>
+  Enumerable<FlatLists.ComparableList<E>> p2(Object[] lists, int[] fieldCounts,
+      boolean withOrdinality) {
+    final List<Enumerator<List<E>>> 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];
+        enumerators.add(
+            Linq4j.transform(
+                Linq4j.enumerator(list),
+                new Function1<E, List<E>>() {
+                  public List<E> apply(E a0) {
+                    return FlatLists.of(a0);
+                  }
+                }));
+      } else {
+        totalFieldCount += fieldCount;
+        @SuppressWarnings("unchecked")
+        List<List<E>> list = (List<List<E>>) lists[i];
+        enumerators.add(Linq4j.enumerator(list));
+      }
+    }
+    if (withOrdinality) {
+      ++totalFieldCount;
+    }
+    return product(enumerators, totalFieldCount, withOrdinality);
   }
 
   public static Object[] array(Object... args) {
     return args;
   }
+
+  /** Similar to {@link Linq4j#product(Iterable)} but each resulting list
+   * implements {@link FlatLists.ComparableList}. */
+  public static <E extends Comparable>
+  Enumerable<FlatLists.ComparableList<E>>
+  product(final List<Enumerator<List<E>>> enumerators, final int fieldCount,
+      final boolean withOrdinality) {
+    return new AbstractEnumerable<FlatLists.ComparableList<E>>() {
+      public Enumerator<FlatLists.ComparableList<E>> enumerator() {
+        return new ProductComparableListEnumerator<>(enumerators, fieldCount,
+            withOrdinality);
+      }
+    };
+  }
+
+  /** Enumerates over the cartesian product of the given lists, returning
+   * a comparable list for each row. */
+  private static class ProductComparableListEnumerator<E extends Comparable>
+      extends CartesianProductEnumerator<List<E>, FlatLists.ComparableList<E>>
{
+    final E[] flatElements;
+    final List<E> list;
+    private final boolean withOrdinality;
+    private int ordinality;
+
+    ProductComparableListEnumerator(List<Enumerator<List<E>>> enumerators,
+        int fieldCount, boolean withOrdinality) {
+      super(enumerators);
+      this.withOrdinality = withOrdinality;
+      flatElements = (E[]) new Comparable[fieldCount];
+      list = Arrays.asList(flatElements);
+    }
+
+    public FlatLists.ComparableList<E> current() {
+      int i = 0;
+      for (Object element : (Object[]) elements) {
+        final List list2 = (List) element;
+        Object[] a = list2.toArray();
+        System.arraycopy(a, 0, flatElements, i, a.length);
+        i += a.length;
+      }
+      if (withOrdinality) {
+        flatElements[i] = (E) new Integer(++ordinality); // 1-based
+      }
+      return FlatLists.ofComparable(list);
+    }
+  }
 }
 
 // End SqlFunctions.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 096f361..b3d0a1b 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
@@ -21,7 +21,9 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.type.ArraySqlType;
 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.
@@ -44,33 +46,33 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
         true,
         null,
         null,
-        OperandTypes.SCALAR_OR_RECORD_COLLECTION);
+        OperandTypes.repeat(SqlOperandCountRanges.from(1),
+            OperandTypes.SCALAR_OR_RECORD_COLLECTION));
     this.withOrdinality = withOrdinality;
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  public RelDataType inferReturnType(
-      SqlOperatorBinding opBinding) {
-    RelDataType type = opBinding.getOperandType(0);
-    if (type.isStruct()) {
-      type = type.getFieldList().get(0).getType();
-    }
-    assert type instanceof ArraySqlType || type instanceof MultisetSqlType;
-    if (withOrdinality) {
-      final RelDataTypeFactory.FieldInfoBuilder builder =
-          opBinding.getTypeFactory().builder();
+  @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+    final RelDataTypeFactory.FieldInfoBuilder builder =
+        opBinding.getTypeFactory().builder();
+    for (Integer operand : Util.range(opBinding.getOperandCount())) {
+      RelDataType type = opBinding.getOperandType(operand);
+      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());
       } else {
-        builder.add(SqlUtil.deriveAliasFromOrdinal(0), type.getComponentType());
+        builder.add(SqlUtil.deriveAliasFromOrdinal(operand),
+            type.getComponentType());
       }
-      return builder
-          .add(ORDINALITY_COLUMN_NAME, SqlTypeName.INTEGER)
-          .build();
-    } else {
-      return type.getComponentType();
     }
+    if (withOrdinality) {
+      builder.add(ORDINALITY_COLUMN_NAME, SqlTypeName.INTEGER);
+    }
+    return builder.build();
   }
 
   @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
b/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
index 3370b5f..0849b58 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ComparableOperandTypeChecker.java
@@ -19,14 +19,10 @@ package org.apache.calcite.sql.type;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeComparability;
 import org.apache.calcite.sql.SqlCallBinding;
-import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlOperatorBinding;
-import org.apache.calcite.sql.SqlUtil;
 
 import com.google.common.base.Preconditions;
 
-import java.util.Collections;
-
 /**
  * Type checking strategy which verifies that types have the required attributes
  * to be used as arguments to comparison operators.
@@ -99,14 +95,7 @@ public class ComparableOperandTypeChecker extends SameOperandTypeChecker
{
     boolean b = true;
     for (int i = 0; i < nOperands; ++i) {
       RelDataType type = callBinding.getOperandType(i);
-      boolean result;
-      if (type.getComparability().ordinal()
-          < requiredComparability.ordinal()) {
-        result = false;
-      } else {
-        result = true;
-      }
-      if (!result) {
+      if (type.getComparability().ordinal() < requiredComparability.ordinal()) {
         b = false;
       }
     }
@@ -116,9 +105,8 @@ public class ComparableOperandTypeChecker extends SameOperandTypeChecker
{
     return b;
   }
 
-  public String getAllowedSignatures(SqlOperator op, String opName) {
-    return SqlUtil.getAliasedSignature(op, opName,
-        Collections.nCopies(nOperands, "COMPARABLE_TYPE"));
+  @Override protected String getTypeName() {
+    return "COMPARABLE_TYPE";
   }
 
   @Override public Consistency getConsistency() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java
b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java
index 9bfea87..cb88823 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/CompositeOperandTypeChecker.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.sql.SqlCallBinding;
 import org.apache.calcite.sql.SqlOperandCountRange;
 import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.util.Util;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -76,11 +77,12 @@ import javax.annotation.Nullable;
  * AND composition, only the first rule is used for signature generation.
  */
 public class CompositeOperandTypeChecker implements SqlOperandTypeChecker {
+  private final SqlOperandCountRange range;
   //~ Enums ------------------------------------------------------------------
 
   /** How operands are composed. */
   public enum Composition {
-    AND, OR, SEQUENCE
+    AND, OR, SEQUENCE, REPEAT
   }
 
   //~ Instance fields --------------------------------------------------------
@@ -98,11 +100,14 @@ public class CompositeOperandTypeChecker implements SqlOperandTypeChecker
{
   CompositeOperandTypeChecker(
       Composition composition,
       ImmutableList<? extends SqlOperandTypeChecker> allowedRules,
-      @Nullable String allowedSignatures) {
+      @Nullable String allowedSignatures,
+      @Nullable SqlOperandCountRange range) {
     this.allowedRules = Preconditions.checkNotNull(allowedRules);
     this.composition = Preconditions.checkNotNull(composition);
     this.allowedSignatures = allowedSignatures;
-    assert allowedRules.size() > 1;
+    this.range = range;
+    assert (range != null) == (composition == Composition.REPEAT);
+    assert allowedRules.size() + (range == null ? 0 : 1) > 1;
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -147,6 +152,8 @@ public class CompositeOperandTypeChecker implements SqlOperandTypeChecker
{
 
   public SqlOperandCountRange getOperandCountRange() {
     switch (composition) {
+    case REPEAT:
+      return range;
     case SEQUENCE:
       return SqlOperandCountRanges.of(allowedRules.size());
     case AND:
@@ -253,6 +260,23 @@ public class CompositeOperandTypeChecker implements SqlOperandTypeChecker
{
 
   private boolean check(SqlCallBinding callBinding) {
     switch (composition) {
+    case REPEAT:
+      if (!range.isValidCount(callBinding.getOperandCount())) {
+        return false;
+      }
+      for (int operand : Util.range(callBinding.getOperandCount())) {
+        for (SqlOperandTypeChecker rule : allowedRules) {
+          if (!((SqlSingleOperandTypeChecker) rule).checkSingleOperandType(
+              callBinding,
+              callBinding.getCall().operand(operand),
+              0,
+              false)) {
+            return false;
+          }
+        }
+      }
+      return true;
+
     case SEQUENCE:
       if (callBinding.getOperandCount() != allowedRules.size()) {
         return false;

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/sql/type/CompositeSingleOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/CompositeSingleOperandTypeChecker.java
b/core/src/main/java/org/apache/calcite/sql/type/CompositeSingleOperandTypeChecker.java
index de8c303..7142605 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/CompositeSingleOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/CompositeSingleOperandTypeChecker.java
@@ -41,7 +41,7 @@ public class CompositeSingleOperandTypeChecker
       CompositeOperandTypeChecker.Composition composition,
       ImmutableList<? extends SqlSingleOperandTypeChecker> allowedRules,
       String allowedSignatures) {
-    super(composition, allowedRules, allowedSignatures);
+    super(composition, allowedRules, allowedSignatures, null);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 eafd7c6..1d43ba0 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
@@ -87,17 +87,16 @@ public abstract class OperandTypes {
   public static SqlOperandTypeChecker or(SqlOperandTypeChecker... rules) {
     return new CompositeOperandTypeChecker(
         CompositeOperandTypeChecker.Composition.OR,
-        ImmutableList.copyOf(rules), null);
+        ImmutableList.copyOf(rules), null, null);
   }
 
   /**
-   * Creates a single-operand checker that passes if any one of the rules
-   * passes.
+   * Creates a checker that passes if all of the rules pass.
    */
   public static SqlOperandTypeChecker and(SqlOperandTypeChecker... rules) {
     return new CompositeOperandTypeChecker(
         CompositeOperandTypeChecker.Composition.AND,
-        ImmutableList.copyOf(rules), null);
+        ImmutableList.copyOf(rules), null, null);
   }
 
   /**
@@ -112,8 +111,8 @@ public abstract class OperandTypes {
   }
 
   /**
-   * Creates a single-operand checker that passes if any one of the rules
-   * passes.
+   * Creates a single-operand checker that passes if all of the rules
+   * pass.
    */
   public static SqlSingleOperandTypeChecker and(
       SqlSingleOperandTypeChecker... rules) {
@@ -129,7 +128,18 @@ public abstract class OperandTypes {
       SqlSingleOperandTypeChecker... rules) {
     return new CompositeOperandTypeChecker(
         CompositeOperandTypeChecker.Composition.SEQUENCE,
-        ImmutableList.copyOf(rules), allowedSignatures);
+        ImmutableList.copyOf(rules), allowedSignatures, null);
+  }
+
+  /**
+   * Creates a checker that passes if all of the rules pass for each operand,
+   * using a given operand count strategy.
+   */
+  public static SqlOperandTypeChecker repeat(SqlOperandCountRange range,
+      SqlSingleOperandTypeChecker... rules) {
+    return new CompositeOperandTypeChecker(
+        CompositeOperandTypeChecker.Composition.REPEAT,
+        ImmutableList.copyOf(rules), null, range);
   }
 
   // ----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java
index b88ea2e..2bb75e0 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SameOperandTypeChecker.java
@@ -134,12 +134,18 @@ public class SameOperandTypeChecker implements SqlSingleOperandTypeChecker
{
     }
   }
 
-  // implement SqlOperandTypeChecker
   public String getAllowedSignatures(SqlOperator op, String opName) {
+    final String typeName = getTypeName();
     return SqlUtil.getAliasedSignature(op, opName,
         nOperands == -1
-            ? ImmutableList.of("EQUIVALENT_TYPE", "EQUIVALENT_TYPE", "...")
-            : Collections.nCopies(nOperands, "EQUIVALENT_TYPE"));
+            ? ImmutableList.of(typeName, typeName, "...")
+            : Collections.nCopies(nOperands, typeName));
+  }
+
+  /** Override to change the behavior of
+   * {@link #getAllowedSignatures(SqlOperator, String)}. */
+  protected String getTypeName() {
+    return "EQUIVALENT_TYPE";
   }
 
   public boolean checkSingleOperandType(

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index e0e8325..556839b 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -2052,19 +2052,25 @@ public class SqlToRelConverter {
 
     case UNNEST:
       call = (SqlCall) from;
-      final SqlNode node = call.operand(0);
+      final List<SqlNode> nodes = call.getOperandList();
       final SqlUnnestOperator operator = (SqlUnnestOperator) call.getOperator();
-      replaceSubqueries(bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
-      final RelNode childRel =
+      for (SqlNode node : nodes) {
+        replaceSubqueries(bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+      }
+      final List<RexNode> exprs = new ArrayList<>();
+      final List<String> fieldNames = new ArrayList<>();
+      for (Ord<SqlNode> node : Ord.zip(nodes)) {
+        exprs.add(bb.convertExpression(node.e));
+        fieldNames.add(validator.deriveAlias(node.e, node.i));
+      }
+      final RelNode input =
           RelOptUtil.createProject(
               (null != bb.root) ? bb.root : LogicalValues.createOneRow(cluster),
-              Collections.singletonList(bb.convertExpression(node)),
-              Collections.singletonList(validator.deriveAlias(node, 0)),
-              true);
+              exprs, fieldNames, true);
 
       Uncollect uncollect =
           new Uncollect(cluster, cluster.traitSetOf(Convention.NONE),
-              childRel, operator.withOrdinality);
+              input, operator.withOrdinality);
       bb.setRoot(uncollect, true);
       return;
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 fd5f78f..c069b91 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -176,6 +176,7 @@ 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),
   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),
@@ -191,7 +192,6 @@ public enum BuiltInMethod {
   AS_ENUMERABLE(Linq4j.class, "asEnumerable", Object[].class),
   AS_ENUMERABLE2(Linq4j.class, "asEnumerable", Iterable.class),
   ENUMERABLE_TO_LIST(ExtendedEnumerable.class, "toList"),
-  LIST_TO_ENUMERABLE(SqlFunctions.class, "listToEnumerable"),
   AS_LIST(Primitive.class, "asList", Object.class),
   ENUMERATOR_CURRENT(Enumerator.class, "current"),
   ENUMERATOR_MOVE_NEXT(Enumerator.class, "moveNext"),

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 fb5eb8a..21c8ce1 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2248,6 +2248,44 @@ public class JdbcTest {
             "name=Sales; empid=150; deptno=10; name0=Sebastian; salary=7000.0; commission=null");
   }
 
+  @Test public void testUnnestArrayScalarArray() {
+    CalciteAssert.hr()
+        .query("select d.\"name\", e.*\n"
+            + "from \"hr\".\"depts\" as d,\n"
+            + " UNNEST(d.\"employees\", array[1, 2]) as e")
+        .returnsUnordered(
+            "name=HR; empid=200; deptno=20; name0=Eric; salary=8000.0; commission=500; EXPR$1=1",
+            "name=HR; empid=200; deptno=20; name0=Eric; salary=8000.0; commission=500; EXPR$1=2",
+            "name=Sales; empid=100; deptno=10; name0=Bill; salary=10000.0; commission=1000;
EXPR$1=1",
+            "name=Sales; empid=100; deptno=10; name0=Bill; salary=10000.0; commission=1000;
EXPR$1=2",
+            "name=Sales; empid=150; deptno=10; name0=Sebastian; salary=7000.0; commission=null;
EXPR$1=1",
+            "name=Sales; empid=150; deptno=10; name0=Sebastian; salary=7000.0; commission=null;
EXPR$1=2");
+  }
+
+  @Test public void testUnnestArrayScalarArrayAliased() {
+    CalciteAssert.hr()
+        .query("select d.\"name\", e.*\n"
+            + "from \"hr\".\"depts\" as d,\n"
+            + " UNNEST(d.\"employees\", array[1, 2]) as e (ei, d, n, s, c, i)\n"
+            + "where ei + i > 151")
+        .returnsUnordered(
+            "name=HR; EI=200; D=20; N=Eric; S=8000.0; C=500; I=1",
+            "name=HR; EI=200; D=20; N=Eric; S=8000.0; C=500; I=2",
+            "name=Sales; EI=150; D=10; N=Sebastian; S=7000.0; C=null; I=2");
+  }
+
+  @Test public void testUnnestArrayScalarArrayWithOrdinal() {
+    CalciteAssert.hr()
+        .query("select d.\"name\", e.*\n"
+            + "from \"hr\".\"depts\" as d,\n"
+            + " UNNEST(d.\"employees\", array[1, 2]) with ordinality as e (ei, d, n, s, c,
i, o)\n"
+            + "where ei + i > 151")
+        .returnsUnordered(
+            "name=HR; EI=200; D=20; N=Eric; S=8000.0; C=500; I=1; O=2",
+            "name=HR; EI=200; D=20; N=Eric; S=8000.0; C=500; I=2; O=4",
+            "name=Sales; EI=150; D=10; N=Sebastian; S=7000.0; C=null; I=2; O=5");
+  }
+
   private CalciteAssert.AssertQuery withFoodMartQuery(int id)
       throws IOException {
     final FoodmartTest.FoodMartQuerySet set =

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/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 548b538..9afa6b6 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -19,12 +19,16 @@ package org.apache.calcite.util;
 import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.util.Spaces;
 import org.apache.calcite.examples.RelBuilderExample;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.Parameter;
 import org.apache.calcite.runtime.ConsList;
 import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.runtime.Resources;
+import org.apache.calcite.runtime.SqlFunctions;
 import org.apache.calcite.runtime.Utilities;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.util.SqlBuilder;
@@ -365,7 +369,7 @@ public class UtilTest {
    * Tests {@link org.apache.calcite.util.CastingList} and {@link Util#cast}.
    */
   @Test public void testCastingList() {
-    final List<Number> numberList = new ArrayList<Number>();
+    final List<Number> numberList = new ArrayList<>();
     numberList.add(new Integer(1));
     numberList.add(null);
     numberList.add(new Integer(2));
@@ -703,7 +707,7 @@ public class UtilTest {
 
     List<String> listEmpty = Collections.emptyList();
     List<String> listAbc = Arrays.asList("a", "b", "c");
-    List<String> listEmpty2 = new ArrayList<String>();
+    List<String> listEmpty2 = new ArrayList<>();
 
     // Made up of three lists, two of which are empty
     list = CompositeList.of(listEmpty, listAbc, listEmpty2);
@@ -763,7 +767,7 @@ public class UtilTest {
             "Hello, {0}, what a nice {1}.", "world", "day"));
 
     // Our extended message format. First, just strings.
-    final HashMap<Object, Object> map = new HashMap<Object, Object>();
+    final HashMap<Object, Object> map = new HashMap<>();
     map.put("person", "world");
     map.put("time", "day");
     assertEquals(
@@ -813,7 +817,7 @@ public class UtilTest {
         Collections.<String>emptyList(), template2.getParameterNames());
     assertEquals(
         "Don't expand this {brace}.",
-        template2.format(Collections.<Object, Object>emptyMap()));
+        template2.format(Collections.emptyMap()));
 
     // Empty template.
     assertEquals("", Template.formatByName("", map));
@@ -904,7 +908,7 @@ public class UtilTest {
    */
   @Test public void testPairAdjacents() {
     List<String> strings = Arrays.asList("a", "b", "c");
-    List<String> result = new ArrayList<String>();
+    List<String> result = new ArrayList<>();
     for (Pair<String, String> pair : Pair.adjacents(strings)) {
       result.add(pair.toString());
     }
@@ -929,7 +933,7 @@ public class UtilTest {
    */
   @Test public void testPairFirstAnd() {
     List<String> strings = Arrays.asList("a", "b", "c");
-    List<String> result = new ArrayList<String>();
+    List<String> result = new ArrayList<>();
     for (Pair<String, String> pair : Pair.firstAnd(strings)) {
       result.add(pair.toString());
     }
@@ -1045,13 +1049,13 @@ public class UtilTest {
   }
 
   private List<Integer> checkIntegerIntervalSet(String s, int... ints) {
-    List<Integer> list = new ArrayList<Integer>();
+    List<Integer> list = new ArrayList<>();
     final Set<Integer> set = IntegerIntervalSet.of(s);
     assertEquals(set.size(), ints.length);
     for (Integer integer : set) {
       list.add(integer);
     }
-    assertEquals(new HashSet<Integer>(IntList.asList(ints)), set);
+    assertEquals(new HashSet<>(IntList.asList(ints)), set);
     return list;
   }
 
@@ -1210,6 +1214,41 @@ public class UtilTest {
     }
   }
 
+  private <E> List<E> l1(E e) {
+    return Collections.singletonList(e);
+  }
+
+  private <E> List<E> l2(E e0, E e1) {
+    return Arrays.asList(e0, e1);
+  }
+
+  private <E> List<E> l3(E e0, E e1, E e2) {
+    return Arrays.asList(e0, e1, e2);
+  }
+
+  @Test public void testFlatListProduct() {
+    final List<Enumerator<List<String>>> list = new ArrayList<>();
+    list.add(Linq4j.enumerator(l2(l1("a"), l1("b"))));
+    list.add(Linq4j.enumerator(l3(l2("x", "p"), l2("y", "q"), l2("z", "r"))));
+    final Enumerable<FlatLists.ComparableList<String>> product =
+        SqlFunctions.product(list, 3, false);
+    int n = 0;
+    FlatLists.ComparableList<String> previous = FlatLists.of();
+    for (FlatLists.ComparableList<String> strings : product) {
+      if (n++ == 1) {
+        assertThat(strings.size(), is(3));
+        assertThat(strings.get(0), is("a"));
+        assertThat(strings.get(1), is("y"));
+        assertThat(strings.get(2), is("q"));
+      }
+      if (previous != null) {
+        assertTrue(previous.compareTo(strings) < 0);
+      }
+      previous = strings;
+    }
+    assertThat(n, is(6));
+  }
+
   /**
    * Unit test for {@link AvaticaUtils#toCamelCase(String)}.
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/linq4j/src/main/java/org/apache/calcite/linq4j/CartesianProductEnumerator.java
----------------------------------------------------------------------
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/CartesianProductEnumerator.java
b/linq4j/src/main/java/org/apache/calcite/linq4j/CartesianProductEnumerator.java
index 9bf3da1..1f871e3 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/CartesianProductEnumerator.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/CartesianProductEnumerator.java
@@ -16,29 +16,25 @@
  */
 package org.apache.calcite.linq4j;
 
-import java.util.Arrays;
 import java.util.List;
 
 /**
  * Enumerator over the cartesian product of enumerators.
  *
- * @param <T> Element type
+ * @param <T> Input element type
+ * @param <E> Element type
  */
-class CartesianProductEnumerator<T> implements Enumerator<List<T>> {
+public abstract class CartesianProductEnumerator<T, E> implements Enumerator<E>
{
   private final List<Enumerator<T>> enumerators;
-  private final T[] elements;
+  protected final T[] elements;
   private boolean first = true;
 
-  public CartesianProductEnumerator(List<Enumerator<T>> enumerators) {
+  protected CartesianProductEnumerator(List<Enumerator<T>> enumerators) {
     this.enumerators = enumerators;
     //noinspection unchecked
     this.elements = (T[]) new Object[enumerators.size()];
   }
 
-  public List<T> current() {
-    return Arrays.asList(elements.clone());
-  }
-
   public boolean moveNext() {
     if (first) {
       int i = 0;

http://git-wip-us.apache.org/repos/asf/calcite/blob/354e8240/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
----------------------------------------------------------------------
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
index c0d8d75..3dde65c 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
@@ -384,7 +384,7 @@ public abstract class Linq4j {
    */
   public static <T> Enumerator<List<T>> product(
       List<Enumerator<T>> enumerators) {
-    return new CartesianProductEnumerator<>(enumerators);
+    return new CartesianProductListEnumerator<>(enumerators);
   }
 
   /** Returns the cartesian product of an iterable of iterables. */
@@ -397,7 +397,7 @@ public abstract class Linq4j {
           enumerators.add(iterableEnumerator(iterable));
         }
         return enumeratorIterator(
-            new CartesianProductEnumerator<>(enumerators));
+            new CartesianProductListEnumerator<>(enumerators));
       }
     };
   }
@@ -447,7 +447,7 @@ public abstract class Linq4j {
     Iterator<? extends T> iterator;
     T current;
 
-    public IterableEnumerator(Iterable<? extends T> iterable) {
+    IterableEnumerator(Iterable<? extends T> iterable) {
       this.iterable = iterable;
       iterator = iterable.iterator();
       current = (T) DUMMY;
@@ -659,7 +659,7 @@ public abstract class Linq4j {
     private final Enumerator<T> enumerator;
     boolean hasNext;
 
-    public EnumeratorIterator(Enumerator<T> enumerator) {
+    EnumeratorIterator(Enumerator<T> enumerator) {
       this.enumerator = enumerator;
       hasNext = enumerator.moveNext();
     }
@@ -688,7 +688,7 @@ public abstract class Linq4j {
     private final List<? extends V> list;
     int i = -1;
 
-    public ListEnumerator(List<? extends V> list) {
+    ListEnumerator(List<? extends V> list) {
       this.list = list;
     }
 
@@ -707,6 +707,19 @@ public abstract class Linq4j {
     public void close() {
     }
   }
+
+  /** Enumerates over the cartesian product of the given lists, returning
+   * a list for each row. */
+  private static class CartesianProductListEnumerator<E>
+      extends CartesianProductEnumerator<E, List<E>> {
+    CartesianProductListEnumerator(List<Enumerator<E>> enumerators) {
+      super(enumerators);
+    }
+
+    public List<E> current() {
+      return Arrays.asList(elements.clone());
+    }
+  }
 }
 
 // End Linq4j.java


Mime
View raw message