This is an automated email from the ASF dual-hosted git repository.

danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new e44beba  [CALCITE-3789] Supports Presto style unnest with items alias 
(Will Yu)
e44beba is described below

commit e44beba286ea9049c5fd00c3a3b0e4a4f1c03356
Author: Will Yu <[email protected]>
AuthorDate: Tue Feb 25 19:44:26 2020 -0800

    [CALCITE-3789] Supports Presto style unnest with items alias (Will Yu)
    
    This patch also add a new PRESTO conformance.
    
    Fix-up (by Danny):
    - Fix SqlTypeUtil#flattenRecordType to not append field index if
      there are no duplicates
    - Rename SqlConformance#allowAliasUnnestColumns to
      SqlConformance#allowAliasUnnestItems
    - Fix RelStructuredTypeFlattener to not generate flattenned
      field based on struct field
    - Promote SqlToRelConverter#convertFrom to allow specify field
      aliases
    - Add comment to RelBuilder#uncollect
    
    close apache/calcite#1811
---
 .../adapter/enumerable/EnumerableUncollect.java    |  3 +-
 .../org/apache/calcite/rel/core/Uncollect.java     | 63 ++++++++++++-----
 .../calcite/rel/logical/ToLogicalConverter.java    |  6 +-
 .../apache/calcite/rel/mutable/MutableRels.java    |  4 +-
 .../org/apache/calcite/sql/SqlUnnestOperator.java  | 11 ++-
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   | 15 +++-
 .../sql/validate/SqlAbstractConformance.java       |  3 +
 .../calcite/sql/validate/SqlConformance.java       | 64 ++++++++++++-----
 .../calcite/sql/validate/SqlConformanceEnum.java   | 21 ++++++
 .../sql/validate/SqlDelegatingConformance.java     |  4 ++
 .../sql2rel/RelStructuredTypeFlattener.java        | 20 ++++++
 .../apache/calcite/sql2rel/SqlToRelConverter.java  | 80 ++++++++++++++--------
 .../java/org/apache/calcite/tools/RelBuilder.java  | 22 ++++++
 .../apache/calcite/sql/test/SqlAdvisorTest.java    |  1 +
 .../apache/calcite/test/SqlToRelConverterTest.java | 26 ++++++-
 .../org/apache/calcite/test/SqlValidatorTest.java  | 37 ++++++++++
 .../org/apache/calcite/test/catalog/Fixture.java   |  5 +-
 .../test/catalog/MockCatalogReaderSimple.java      | 10 +++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 30 +++++++-
 .../org/apache/calcite/piglet/PigRelBuilder.java   |  8 ++-
 20 files changed, 355 insertions(+), 78 deletions(-)

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 6666cf7..6608dda 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
@@ -32,6 +32,7 @@ import org.apache.calcite.util.BuiltInMethod;
 import com.google.common.primitives.Ints;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /** Implementation of {@link org.apache.calcite.rel.core.Uncollect} in
@@ -48,7 +49,7 @@ public class EnumerableUncollect extends Uncollect implements 
EnumerableRel {
    * <p>Use {@link #create} unless you know what you're doing. */
   public EnumerableUncollect(RelOptCluster cluster, RelTraitSet traitSet,
       RelNode child, boolean withOrdinality) {
-    super(cluster, traitSet, child, withOrdinality);
+    super(cluster, traitSet, child, withOrdinality, Collections.emptyList());
     assert getConvention() instanceof EnumerableConvention;
     assert getConvention() == child.getConvention();
   }
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 c519552..45caa41 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
@@ -30,6 +30,9 @@ import org.apache.calcite.sql.SqlUnnestOperator;
 import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
 
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -46,21 +49,31 @@ import java.util.List;
 public class Uncollect extends SingleRel {
   public final boolean withOrdinality;
 
+  // To alias the items in Uncollect list,
+  // i.e., "UNNEST(a, b, c) as T(d, e, f)"
+  // outputs as row type Record(d, e, f) where the field "d" has element type 
of "a",
+  // field "e" has element type of "b"(Presto dialect).
+
+  // Without the aliases, the expression "UNNEST(a)" outputs row type
+  // same with element type of "a".
+  private final List<String> itemAliases;
+
   //~ Constructors -----------------------------------------------------------
 
   @Deprecated // to be removed before 2.0
   public Uncollect(RelOptCluster cluster, RelTraitSet traitSet,
       RelNode child) {
-    this(cluster, traitSet, child, false);
+    this(cluster, traitSet, child, false, Collections.emptyList());
   }
 
   /** Creates an Uncollect.
    *
    * <p>Use {@link #create} unless you know what you're doing. */
   public Uncollect(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
-      boolean withOrdinality) {
+      boolean withOrdinality, List<String> itemAliases) {
     super(cluster, traitSet, input);
     this.withOrdinality = withOrdinality;
+    this.itemAliases = ImmutableList.copyOf(itemAliases);
     assert deriveRowType() != null : "invalid child rowtype";
   }
 
@@ -69,7 +82,7 @@ public class Uncollect extends SingleRel {
    */
   public Uncollect(RelInput input) {
     this(input.getCluster(), input.getTraitSet(), input.getInput(),
-        input.getBoolean("withOrdinality", false));
+        input.getBoolean("withOrdinality", false), Collections.emptyList());
   }
 
   /**
@@ -78,14 +91,18 @@ public class Uncollect extends SingleRel {
    * <p>Each field of the input relational expression must be an array or
    * multiset.
    *
-   * @param traitSet Trait set
-   * @param input    Input relational expression
+   * @param traitSet       Trait set
+   * @param input          Input relational expression
    * @param withOrdinality Whether output should contain an ORDINALITY column
+   * @param itemAliases    Aliases for the operand items
    */
-  public static Uncollect create(RelTraitSet traitSet, RelNode input,
-      boolean withOrdinality) {
+  public static Uncollect create(
+      RelTraitSet traitSet,
+      RelNode input,
+      boolean withOrdinality,
+      List<String> itemAliases) {
     final RelOptCluster cluster = input.getCluster();
-    return new Uncollect(cluster, traitSet, input, withOrdinality);
+    return new Uncollect(cluster, traitSet, input, withOrdinality, 
itemAliases);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -102,25 +119,32 @@ public class Uncollect extends SingleRel {
 
   public RelNode copy(RelTraitSet traitSet, RelNode input) {
     assert traitSet.containsIfApplicable(Convention.NONE);
-    return new Uncollect(getCluster(), traitSet, input, withOrdinality);
+    return new Uncollect(getCluster(), traitSet, input, withOrdinality, 
itemAliases);
   }
 
   protected RelDataType deriveRowType() {
-    return deriveUncollectRowType(input, withOrdinality);
+    return deriveUncollectRowType(input, withOrdinality, itemAliases);
   }
 
   /**
    * Returns the row type returned by applying the 'UNNEST' operation to a
    * relational expression.
    *
-   * <p>Each column in the relational expression must be a multiset of structs
-   * or an array. The return type is the type of that column, plus an 
ORDINALITY
-   * column if {@code withOrdinality}.
+   * <p>Each column in the relational expression must be a multiset of
+   * structs or an array. The return type is the combination of expanding
+   * element types from each column, plus an ORDINALITY column if {@code
+   * withOrdinality}. If {@code itemAliases} is not empty, the element types
+   * would not expand, each column element outputs as a whole (the return
+   * type has same column types as input type).
    */
   public static RelDataType deriveUncollectRowType(RelNode rel,
-      boolean withOrdinality) {
+      boolean withOrdinality, List<String> itemAliases) {
     RelDataType inputType = rel.getRowType();
     assert inputType.isStruct() : inputType + " is not a struct";
+
+    boolean requireAlias = !itemAliases.isEmpty();
+    assert !requireAlias || itemAliases.size() == inputType.getFieldCount();
+
     final List<RelDataTypeField> fields = inputType.getFieldList();
     final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory();
     final RelDataTypeFactory.Builder builder = typeFactory.builder();
@@ -130,19 +154,23 @@ public class Uncollect extends SingleRel {
       // Component type is unknown to Uncollect, build a row type with input 
column name
       // and Any type.
       return builder
-          .add(fields.get(0).getName(), SqlTypeName.ANY)
+          .add(requireAlias ? itemAliases.get(0) : fields.get(0).getName(), 
SqlTypeName.ANY)
           .nullable(true)
           .build();
     }
 
-    for (RelDataTypeField field : fields) {
+    for (int i = 0; i < fields.size(); i++) {
+      RelDataTypeField field = fields.get(i);
       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 {
         RelDataType ret = field.getType().getComponentType();
         assert null != ret;
-        if (ret.isStruct()) {
+
+        if (requireAlias) {
+          builder.add(itemAliases.get(i), ret);
+        } else if (ret.isStruct()) {
           builder.addAll(ret.getFieldList());
         } else {
           // Element type is not a record, use the field name of the element 
directly
@@ -150,6 +178,7 @@ public class Uncollect extends SingleRel {
         }
       }
     }
+
     if (withOrdinality) {
       builder.add(SqlUnnestOperator.ORDINALITY_COLUMN_NAME,
           SqlTypeName.INTEGER);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java 
b/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
index a1206d7..a7629d7 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/ToLogicalConverter.java
@@ -42,6 +42,8 @@ import org.apache.calcite.rel.core.Window;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.ImmutableBitSet;
 
+import java.util.Collections;
+
 /**
  * Shuttle to convert any rel plan to a plan with all logical nodes.
  */
@@ -182,8 +184,8 @@ public class ToLogicalConverter extends RelShuttleImpl {
     if (relNode instanceof Uncollect) {
       final Uncollect uncollect = (Uncollect) relNode;
       final RelNode input = visit(uncollect.getInput());
-      return new Uncollect(input.getCluster(), input.getTraitSet(), input,
-          uncollect.withOrdinality);
+      return Uncollect.create(input.getTraitSet(), input,
+          uncollect.withOrdinality, Collections.emptyList());
     }
 
     throw new AssertionError("Need to implement logical converter for "
diff --git a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java 
b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
index a266fdb..28d25c4 100644
--- a/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
+++ b/core/src/main/java/org/apache/calcite/rel/mutable/MutableRels.java
@@ -64,6 +64,7 @@ import com.google.common.collect.Lists;
 
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -254,7 +255,8 @@ public abstract class MutableRels {
     case UNCOLLECT: {
       final MutableUncollect uncollect = (MutableUncollect) node;
       final RelNode child = fromMutable(uncollect.getInput(), relBuilder);
-      return Uncollect.create(child.getTraitSet(), child, 
uncollect.withOrdinality);
+      return Uncollect.create(child.getTraitSet(), child, 
uncollect.withOrdinality,
+          Collections.emptyList());
     }
     case WINDOW: {
       final MutableWindow window = (MutableWindow) node;
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 ee339e3..6e87a92 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java
@@ -83,7 +83,7 @@ public class SqlUnnestOperator extends SqlFunctionalOperator {
         builder.add(MAP_KEY_COLUMN_NAME, type.getKeyType());
         builder.add(MAP_VALUE_COLUMN_NAME, type.getValueType());
       } else {
-        if (type.getComponentType().isStruct()) {
+        if (!allowAliasUnnestItems(opBinding) && 
type.getComponentType().isStruct()) {
           builder.addAll(type.getComponentType().getFieldList());
         } else {
           builder.add(SqlUtil.deriveAliasFromOrdinal(operand),
@@ -97,6 +97,15 @@ public class SqlUnnestOperator extends SqlFunctionalOperator 
{
     return builder.build();
   }
 
+  private boolean allowAliasUnnestItems(SqlOperatorBinding operatorBinding) {
+    return (operatorBinding instanceof SqlCallBinding)
+        && ((SqlCallBinding) operatorBinding)
+        .getValidator()
+        .config()
+        .sqlConformance()
+        .allowAliasUnnestItems();
+  }
+
   @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
       int rightPrec) {
     if (call.operandCount() == 1
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java 
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 70e5541..e06edc0 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -45,7 +45,10 @@ import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import static org.apache.calcite.util.Static.RESOURCE;
 
@@ -941,11 +944,21 @@ public abstract class SqlTypeUtil {
     }
     List<RelDataType> types = new ArrayList<>();
     List<String> fieldNames = new ArrayList<>();
+    Map<String, Long> fieldCnt = fieldList.stream()
+        .map(RelDataTypeField::getName)
+        .collect(Collectors.groupingBy(Function.identity(), 
Collectors.counting()));
     int i = -1;
     for (RelDataTypeField field : fieldList) {
       ++i;
       types.add(field.getType());
-      fieldNames.add(field.getName() + "_" + i);
+      String oriFieldName = field.getName();
+      // Patch up the field name with index if there are duplicates.
+      // There is still possibility that the patched name conflicts with 
existing ones,
+      // but that should be rare case.
+      String fieldName = fieldCnt.get(oriFieldName) > 1
+          ? oriFieldName + "_" + i
+          : oriFieldName;
+      fieldNames.add(fieldName);
     }
     return typeFactory.createStructType(types, fieldNames);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
index e88a2d9..842ee92 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
@@ -111,4 +111,7 @@ public abstract class SqlAbstractConformance implements 
SqlConformance {
     return SqlConformanceEnum.DEFAULT.allowQualifyingCommonColumn();
   }
 
+  public boolean allowAliasUnnestItems() {
+    return SqlConformanceEnum.DEFAULT.allowAliasUnnestItems();
+  }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
index f8cbdeb..dc07d1b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
@@ -73,8 +73,8 @@ public interface SqlConformance {
    *
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
-   * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#BIG_QUERY},
+   * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
    */
@@ -87,7 +87,8 @@ public interface SqlConformance {
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
-   * {@link SqlConformanceEnum#MYSQL_5};
+   * {@link SqlConformanceEnum#MYSQL_5},
+   * {@link SqlConformanceEnum#PRESTO};
    * false otherwise.
    */
   boolean isGroupByOrdinal();
@@ -98,8 +99,8 @@ public interface SqlConformance {
    *
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
-   * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#BIG_QUERY},
+   * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5};
    * false otherwise.
    */
@@ -116,10 +117,11 @@ public interface SqlConformance {
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#ORACLE_10},
    * {@link SqlConformanceEnum#ORACLE_12},
-   * {@link SqlConformanceEnum#STRICT_92},
    * {@link SqlConformanceEnum#PRAGMATIC_99},
-   * {@link SqlConformanceEnum#PRAGMATIC_2003};
-   * {@link SqlConformanceEnum#SQL_SERVER_2008};
+   * {@link SqlConformanceEnum#PRAGMATIC_2003},
+   * {@link SqlConformanceEnum#PRESTO},
+   * {@link SqlConformanceEnum#SQL_SERVER_2008},
+   * {@link SqlConformanceEnum#STRICT_92};
    * false otherwise.
    */
   boolean isSortByOrdinal();
@@ -135,8 +137,8 @@ public interface SqlConformance {
    * {@link SqlConformanceEnum#MYSQL_5},
    * {@link SqlConformanceEnum#ORACLE_10},
    * {@link SqlConformanceEnum#ORACLE_12},
+   * {@link SqlConformanceEnum#SQL_SERVER_2008},
    * {@link SqlConformanceEnum#STRICT_92};
-   * {@link SqlConformanceEnum#SQL_SERVER_2008};
    * false otherwise.
    */
   boolean isSortByAlias();
@@ -172,8 +174,9 @@ public interface SqlConformance {
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
-   * {@link SqlConformanceEnum#ORACLE_10};
-   * {@link SqlConformanceEnum#ORACLE_12};
+   * {@link SqlConformanceEnum#ORACLE_10},
+   * {@link SqlConformanceEnum#ORACLE_12},
+   * {@link SqlConformanceEnum#PRESTO};
    * false otherwise.
    */
   boolean isBangEqualAllowed();
@@ -185,7 +188,8 @@ public interface SqlConformance {
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
-   * {@link SqlConformanceEnum#MYSQL_5};
+   * {@link SqlConformanceEnum#MYSQL_5},
+   * {@link SqlConformanceEnum#PRESTO};
    * false otherwise.
    */
   boolean isPercentRemainderAllowed();
@@ -197,7 +201,7 @@ public interface SqlConformance {
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
-   * {@link SqlConformanceEnum#ORACLE_10};
+   * {@link SqlConformanceEnum#ORACLE_10},
    * {@link SqlConformanceEnum#ORACLE_12};
    * false otherwise.
    *
@@ -225,8 +229,8 @@ public interface SqlConformance {
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
+   * {@link SqlConformanceEnum#ORACLE_12},
    * {@link SqlConformanceEnum#SQL_SERVER_2008};
-   * {@link SqlConformanceEnum#ORACLE_12};
    * false otherwise.
    */
   boolean isApplyAllowed();
@@ -254,6 +258,23 @@ public interface SqlConformance {
   boolean isInsertSubsetColumnsAllowed();
 
   /**
+   * Whether directly alias array items in UNNEST.
+   *
+   * <p>E.g. in UNNEST(a_array, b_array) AS T(a, b),
+   * a and b will be aliases of elements in a_array and b_array
+   * respectively.
+   *
+   * <p>Without this flag set, T will be the alias
+   * of the element in a_array and a, b will be the top level
+   * fields of T if T is a STRUCT type.
+   *
+   * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#PRESTO};
+   * false otherwise.
+   */
+  boolean allowAliasUnnestItems();
+
+  /**
    * Whether to allow parentheses to be specified in calls to niladic functions
    * and procedures (that is, functions and procedures with no parameters).
    *
@@ -292,7 +313,8 @@ public interface SqlConformance {
    *
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#DEFAULT},
-   * {@link SqlConformanceEnum#LENIENT};
+   * {@link SqlConformanceEnum#LENIENT},
+   * {@link SqlConformanceEnum#PRESTO};
    * false otherwise.
    */
   boolean allowExplicitRowValueConstructor();
@@ -342,6 +364,7 @@ public interface SqlConformance {
    * {@link SqlConformanceEnum#BABEL},
    * {@link SqlConformanceEnum#LENIENT},
    * {@link SqlConformanceEnum#MYSQL_5},
+   * {@link SqlConformanceEnum#PRESTO},
    * {@link SqlConformanceEnum#SQL_SERVER_2008};
    * false otherwise.
    */
@@ -367,9 +390,10 @@ public interface SqlConformance {
    * <p>Among the built-in conformance levels, true in
    * {@link SqlConformanceEnum#PRAGMATIC_99},
    * {@link SqlConformanceEnum#PRAGMATIC_2003},
-   * {@link SqlConformanceEnum#MYSQL_5};
-   * {@link SqlConformanceEnum#ORACLE_10};
-   * {@link SqlConformanceEnum#ORACLE_12};
+   * {@link SqlConformanceEnum#MYSQL_5},
+   * {@link SqlConformanceEnum#ORACLE_10},
+   * {@link SqlConformanceEnum#ORACLE_12},
+   * {@link SqlConformanceEnum#PRESTO},
    * {@link SqlConformanceEnum#SQL_SERVER_2008};
    * false otherwise.
    */
@@ -426,12 +450,14 @@ public interface SqlConformance {
    * in PostgreSQL.
    *
    * <p>Among the built-in conformance levels, false in
+   * {@link SqlConformanceEnum#ORACLE_10},
+   * {@link SqlConformanceEnum#ORACLE_12},
+   * {@link SqlConformanceEnum#PRESTO},
    * {@link SqlConformanceEnum#STRICT_92},
    * {@link SqlConformanceEnum#STRICT_99},
-   * {@link SqlConformanceEnum#STRICT_2003},
-   * {@link SqlConformanceEnum#ORACLE_10},
-   * {@link SqlConformanceEnum#ORACLE_12};
+   * {@link SqlConformanceEnum#STRICT_2003};
    * true otherwise.
    */
   boolean allowQualifyingCommonColumn();
+
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
index 106cac0..3d689a1 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
@@ -72,6 +72,10 @@ public enum SqlConformanceEnum implements SqlConformance {
   PRAGMATIC_2003,
 
   /** Conformance value that instructs Calcite to use SQL semantics
+   * consistent with Presto. */
+  PRESTO,
+
+  /** Conformance value that instructs Calcite to use SQL semantics
    * consistent with Microsoft SQL Server version 2008. */
   SQL_SERVER_2008;
 
@@ -101,6 +105,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case BABEL:
     case LENIENT:
     case MYSQL_5:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -132,6 +137,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case PRAGMATIC_99:
     case PRAGMATIC_2003:
     case SQL_SERVER_2008:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -178,6 +184,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case MYSQL_5:
     case ORACLE_10:
     case ORACLE_12:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -201,6 +208,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case BABEL:
     case LENIENT:
     case MYSQL_5:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -247,6 +255,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     switch (this) {
     case DEFAULT:
     case LENIENT:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -280,6 +289,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case LENIENT:
     case MYSQL_5:
     case SQL_SERVER_2008:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -295,6 +305,7 @@ public enum SqlConformanceEnum implements SqlConformance {
     case ORACLE_10:
     case ORACLE_12:
     case SQL_SERVER_2008:
+    case PRESTO:
       return true;
     default:
       return false;
@@ -330,10 +341,20 @@ public enum SqlConformanceEnum implements SqlConformance {
     case STRICT_92:
     case STRICT_99:
     case STRICT_2003:
+    case PRESTO:
       return false;
     default:
       return true;
     }
   }
 
+  @Override public boolean allowAliasUnnestItems() {
+    switch (this) {
+    case PRESTO:
+      return true;
+    default:
+      return false;
+    }
+  }
+
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
index 1740f96..3a166fd 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
@@ -73,4 +73,8 @@ public class SqlDelegatingConformance extends 
SqlAbstractConformance {
     return delegate.allowNiladicParentheses();
   }
 
+  @Override public boolean allowAliasUnnestItems() {
+    return delegate.allowAliasUnnestItems();
+  }
+
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java 
b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
index 4bf07b1..897eb63 100644
--- 
a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
+++ 
b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
@@ -319,6 +319,20 @@ public class RelStructuredTypeFlattener implements 
ReflectiveVisitor {
           .orElseThrow(NoSuchElementException::new);
   }
 
+  /** Returns whether the old field at index {@code fieldIdx} was not 
flattened. */
+  private boolean noFlatteningForInput(int fieldIdx) {
+    final List<RelNode> inputs = currentRel.getInputs();
+    int fieldCnt = 0;
+    for (RelNode input : inputs) {
+      fieldCnt += input.getRowType().getFieldCount();
+      if (fieldCnt > fieldIdx) {
+        return getNewForOldRel(input).getRowType().getFieldList().size()
+            == input.getRowType().getFieldList().size();
+      }
+    }
+    return false;
+  }
+
   /**
    * Maps the ordinal of a field pre-flattening to the ordinal of the
    * corresponding field post-flattening, and also returns its type.
@@ -884,6 +898,12 @@ public class RelStructuredTypeFlattener implements 
ReflectiveVisitor {
           // is flattened (no struct types). We just have to create a new 
RexInputRef with the
           // correct ordinal and type.
           RexInputRef inputRef = (RexInputRef) refExp;
+          if (noFlatteningForInput(inputRef.getIndex())) {
+            // Sanity check, the input must not have struct type fields.
+            // We better have a record for each old input field
+            // whether it is flattened.
+            return fieldAccess;
+          }
           final Ord<RelDataType> newField =
               getNewFieldForOldInput(inputRef.getIndex(), iInput);
           return new RexInputRef(newField.getKey(), 
removeDistinct(newField.getValue()));
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 3f3bbb4..eaa6d88 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -47,8 +47,6 @@ import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Sample;
 import org.apache.calcite.rel.core.Sort;
-import org.apache.calcite.rel.core.Uncollect;
-import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.hint.HintStrategyTable;
 import org.apache.calcite.rel.hint.Hintable;
 import org.apache.calcite.rel.hint.RelHint;
@@ -2032,6 +2030,12 @@ public class SqlToRelConverter {
     }
   }
 
+  protected void convertFrom(
+      Blackboard bb,
+      SqlNode from) {
+    convertFrom(bb, from, Collections.emptyList());
+  }
+
   /**
    * Converts a FROM clause into a relational expression.
    *
@@ -2048,10 +2052,12 @@ public class SqlToRelConverter {
    *             <li>a query ("(SELECT * FROM EMP WHERE GENDER = 'F')"),
    *             <li>or any combination of the above.
    *             </ul>
+   * @param fieldNames Field aliases, usually come from AS clause
    */
   protected void convertFrom(
       Blackboard bb,
-      SqlNode from) {
+      SqlNode from,
+      List<String> fieldNames) {
     if (from == null) {
       bb.setRoot(LogicalValues.createOneRow(cluster), false);
       return;
@@ -2066,15 +2072,15 @@ public class SqlToRelConverter {
 
     case AS:
       call = (SqlCall) from;
-      convertFrom(bb, call.operand(0));
-      if (call.operandCount() > 2
-          && (bb.root instanceof Values || bb.root instanceof Uncollect)) {
-        final List<String> fieldNames = new ArrayList<>();
+      SqlNode firstOperand = call.operand(0);
+      final List<String> fieldNameList = new ArrayList<>();
+      if (call.operandCount() > 2) {
         for (SqlNode node : Util.skip(call.getOperandList(), 2)) {
-          fieldNames.add(((SqlIdentifier) node).getSimple());
+          fieldNameList.add(((SqlIdentifier) node).getSimple());
         }
-        bb.setRoot(relBuilder.push(bb.root).rename(fieldNames).build(), true);
+
       }
+      convertFrom(bb, firstOperand, fieldNameList);
       return;
 
     case WITH_ITEM:
@@ -2199,29 +2205,13 @@ public class SqlToRelConverter {
 
     case VALUES:
       convertValuesImpl(bb, (SqlCall) from, null);
+      if (fieldNames.size() > 0) {
+        bb.setRoot(relBuilder.push(bb.root).rename(fieldNames).build(), true);
+      }
       return;
 
     case UNNEST:
-      call = (SqlCall) from;
-      final List<SqlNode> nodes = call.getOperandList();
-      final SqlUnnestOperator operator = (SqlUnnestOperator) 
call.getOperator();
-      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));
-      }
-      RelNode child =
-          (null != bb.root) ? bb.root : LogicalValues.createOneRow(cluster);
-      relBuilder.push(child).projectNamed(exprs, fieldNames, false);
-
-      Uncollect uncollect =
-          new Uncollect(cluster, cluster.traitSetOf(Convention.NONE),
-              relBuilder.build(), operator.withOrdinality);
-      bb.setRoot(uncollect, true);
+      convertUnnest(bb, (SqlCall) from, fieldNames);
       return;
 
     case COLLECTION_TABLE:
@@ -2238,6 +2228,38 @@ public class SqlToRelConverter {
     }
   }
 
+  private void convertUnnest(Blackboard bb, SqlCall call, List<String> 
fieldNames) {
+    final List<SqlNode> nodes = call.getOperandList();
+    final SqlUnnestOperator operator = (SqlUnnestOperator) call.getOperator();
+    for (SqlNode node : nodes) {
+      replaceSubQueries(bb, node, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+    }
+    final List<RexNode> exprs = new ArrayList<>();
+    for (Ord<SqlNode> node : Ord.zip(nodes)) {
+      exprs.add(
+          relBuilder.alias(bb.convertExpression(node.e), 
validator.deriveAlias(node.e, node.i)));
+    }
+    RelNode child =
+        (null != bb.root) ? bb.root : LogicalValues.createOneRow(cluster);
+    RelNode uncollect;
+    if (validator.config().sqlConformance().allowAliasUnnestItems()) {
+      uncollect = relBuilder
+          .push(child)
+          .project(exprs)
+          .uncollect(fieldNames, operator.withOrdinality)
+          .build();
+    } else {
+      // REVIEW danny 2020-04-26: should we unify the normal field aliases and 
the item aliases ?
+      uncollect = relBuilder
+          .push(child)
+          .project(exprs)
+          .uncollect(Collections.emptyList(), operator.withOrdinality)
+          .rename(fieldNames)
+          .build();
+    }
+    bb.setRoot(uncollect, true);
+  }
+
   protected void convertMatchRecognize(Blackboard bb, SqlCall call) {
     final SqlMatchRecognize matchRecognize = (SqlMatchRecognize) call;
     final SqlValidatorNamespace ns = validator.getNamespace(matchRecognize);
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 03ecc1c..24577be 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.function.Experimental;
 import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.Contexts;
+import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptSchema;
@@ -52,6 +53,7 @@ import org.apache.calcite.rel.core.Spool;
 import org.apache.calcite.rel.core.TableFunctionScan;
 import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.TableSpool;
+import org.apache.calcite.rel.core.Uncollect;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.hint.Hintable;
@@ -1523,6 +1525,26 @@ public class RelBuilder {
     return this;
   }
 
+  /**
+   * Creates an {@link Uncollect} with given item aliases.
+   *
+   * @param itemAliases   Operand item aliases, never null
+   * @param withOrdinality If {@code withOrdinality}, the output contains an 
extra
+   * {@code ORDINALITY} column
+   */
+  public RelBuilder uncollect(List<String> itemAliases, boolean 
withOrdinality) {
+    Frame frame = stack.pop();
+    stack.push(
+        new Frame(
+          new Uncollect(
+            cluster,
+            cluster.traitSetOf(Convention.NONE),
+            frame.rel,
+            withOrdinality,
+            Objects.requireNonNull(itemAliases))));
+    return this;
+  }
+
   /** Ensures that the field names match those given.
    *
    * <p>If all fields have the same name, adds nothing;
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index ebfd102..27175ee 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -81,6 +81,7 @@ class SqlAdvisorTest extends SqlValidatorTestCase {
           "TABLE(CATALOG.SALES.EMP_ADDRESS)",
           "TABLE(CATALOG.SALES.DEPT)",
           "TABLE(CATALOG.SALES.DEPT_NESTED)",
+          "TABLE(CATALOG.SALES.DEPT_NESTED_EXPANDED)",
           "TABLE(CATALOG.SALES.BONUS)",
           "TABLE(CATALOG.SALES.ORDERS)",
           "TABLE(CATALOG.SALES.SALGRADE)",
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 338b99e..fde82e6 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1304,7 +1304,31 @@ class SqlToRelConverterTest extends SqlToRelTestBase {
     final String sql = "select d.deptno, e2.empno\n"
         + "from dept_nested as d,\n"
         + " UNNEST(d.employees) as e2(empno, y, z)";
-    sql(sql).with(getExtendedTester()).ok();
+    sql(sql).ok();
+  }
+
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3789";>[CALCITE-3789]
+   * Support validation of UNNEST multiple array columns like Presto</a>.
+   */
+  @Test public void testAliasUnnestArrayPlanWithSingleColumn() {
+    final String sql = "select d.deptno, employee.empno\n"
+        + "from dept_nested_expanded as d,\n"
+        + " UNNEST(d.employees) as t(employee)";
+    sql(sql).conformance(SqlConformanceEnum.PRESTO).ok();
+  }
+
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3789";>[CALCITE-3789]
+   * Support validation of UNNEST multiple array columns like Presto</a>.
+   */
+  @Test public void testAliasUnnestArrayPlanWithDoubleColumn() {
+    final String sql = "select d.deptno, e, k.empno\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.admins, d.employees) as t(e, k)";
+    sql(sql).conformance(SqlConformanceEnum.PRESTO).ok();
   }
 
   @Test void testArrayOfRecord() {
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 7cadfdc..fa23aaa 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7555,6 +7555,43 @@ class SqlValidatorTest extends SqlValidatorTestCase {
         .fails("Column 'C1' not found in any table");
   }
 
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3789";>[CALCITE-3789]
+   * Support validation of UNNEST multiple array columns like Presto</a>.
+   */
+  @Test public void testAliasUnnestMultipleArrays() {
+    // for accessing a field in STRUCT type unnested from array
+    sql("select e.ENAME\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.employees) as t(e)")
+        .withConformance(SqlConformanceEnum.PRESTO).columnType("VARCHAR(10) 
NOT NULL");
+
+    // for unnesting multiple arrays at the same time
+    sql("select d.deptno, e, k.empno, l.\"unit\", l.\"X\" * l.\"Y\"\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.admins, d.employees, d.offices) as t(e, k, l)")
+        .withConformance(SqlConformanceEnum.PRESTO).ok();
+
+    // Make sure validation fails properly given illegal select items
+    sql("select d.deptno, ^e^.some_column, k.empno\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.admins, d.employees) as t(e, k)")
+        .withConformance(SqlConformanceEnum.PRESTO)
+        .fails("Table 'E' not found");
+    sql("select d.deptno, e.detail, ^unknown^.detail\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.employees) as t(e)")
+        .withConformance(SqlConformanceEnum.PRESTO).fails("Incompatible 
types");
+
+    // Make sure validation fails without PRESTO conformance
+    sql("select e.ENAME\n"
+        + "from dept_nested_expanded as d CROSS JOIN\n"
+        + " UNNEST(d.employees) as t(^e^)")
+        .fails("List of column aliases must have same degree as table; table 
has 3 columns "
+            + "\\('EMPNO', 'ENAME', 'DETAIL'\\), whereas alias list has 1 
columns");
+  }
+
   @Test void testUnnestArray() {
     sql("select*from unnest(array[1])")
         .columnType("INTEGER NOT NULL");
diff --git a/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java 
b/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
index 8e499f2..7968d7c 100644
--- a/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
+++ b/core/src/test/java/org/apache/calcite/test/catalog/Fixture.java
@@ -56,6 +56,8 @@ final class Fixture extends AbstractFixture {
       .add("unit", varchar20Type)
       .kind(StructKind.PEEK_FIELDS)
       .build();
+  final RelDataType rectilinearPeekCoordMultisetType =
+      typeFactory.createMultisetType(rectilinearPeekCoordType, -1);
   final RelDataType rectilinearPeekNoExpandCoordType = typeFactory.builder()
       .add("M", intType)
       .add("SUB",
@@ -79,7 +81,8 @@ final class Fixture extends AbstractFixture {
       .add("EMPNO", intType)
       .add("ENAME", varchar10Type)
       .add("DETAIL", typeFactory.builder()
-          .add("SKILLS", array(skillRecordType)).build())
+      .add("SKILLS", array(skillRecordType)).build())
+      .kind(StructKind.PEEK_FIELDS)
       .build();
   final RelDataType empListType = array(empRecordType);
   final ObjectSqlType addressType = new ObjectSqlType(SqlTypeName.STRUCTURED,
diff --git 
a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
 
b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
index 8e21fbd..b68d641 100644
--- 
a/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
+++ 
b/core/src/test/java/org/apache/calcite/test/catalog/MockCatalogReaderSimple.java
@@ -157,6 +157,16 @@ public class MockCatalogReaderSimple extends 
MockCatalogReader {
     deptNestedTable.addColumn("EMPLOYEES", fixture.empListType);
     registerTable(deptNestedTable);
 
+    // Register "DEPT_NESTED_EXPANDED" table.
+    MockTable deptNestedExpandedTable =
+        MockTable.create(this, salesSchema, "DEPT_NESTED_EXPANDED", false, 4);
+    deptNestedExpandedTable.addColumn("DEPTNO", fixture.intType, true);
+    deptNestedExpandedTable.addColumn("NAME", fixture.varchar10Type);
+    deptNestedExpandedTable.addColumn("EMPLOYEES", fixture.empListType);
+    deptNestedExpandedTable.addColumn("ADMINS", fixture.varchar5ArrayType);
+    deptNestedExpandedTable.addColumn("OFFICES", 
fixture.rectilinearPeekCoordMultisetType);
+    registerTable(deptNestedExpandedTable);
+
     // Register "BONUS" table.
     MockTable bonusTable =
         MockTable.create(this, salesSchema, "BONUS", false, 0);
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 20da426..ca7f6b0 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5300,7 +5300,7 @@ LogicalProject(DEPTNO=[$0], EMPNO_AVG=[$7])
       LogicalAggregate(group=[{}], EMPNO_AVG=[AVG($0)])
         LogicalProject(EMPNO=[$0])
           Uncollect
-            LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES_6])
+            LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES])
               LogicalValues(tuples=[[{ 0 }]])
 ]]>
         </Resource>
@@ -5318,7 +5318,7 @@ LogicalProject(DEPTNO=[$0], EMPNO=[$7])
     LogicalProject(DEPTNO=[$0], NAME=[$1], TYPE=[$2.TYPE], DESC=[$2.DESC], 
A=[$2.OTHERS.A], B=[$2.OTHERS.B], EMPLOYEES=[$3])
       LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED]])
     Uncollect
-      LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES_6])
+      LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES])
         LogicalValues(tuples=[[{ 0 }]])
 ]]>
         </Resource>
@@ -5337,11 +5337,35 @@ LogicalProject(DEPTNO=[$0], EMPNO=[$7])
       LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED]])
     LogicalProject(EMPNO=[$0], Y=[$1], Z=[$2])
       Uncollect
-        LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES_6])
+        LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES])
           LogicalValues(tuples=[[{ 0 }]])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testAliasUnnestArrayPlanWithSingleColumn">
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], EMPNO=[$5.EMPNO])
+  LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{2}])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED_EXPANDED]])
+    Uncollect
+      LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES])
+        LogicalValues(tuples=[[{ 0 }]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testAliasUnnestArrayPlanWithDoubleColumn">
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], E=[$5], EMPNO=[$6.EMPNO])
+  LogicalCorrelate(correlation=[$cor0], joinType=[inner], requiredColumns=[{2, 
3}])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED_EXPANDED]])
+    Uncollect
+      LogicalProject(ADMINS=[$cor0.ADMINS], EMPLOYEES=[$cor0.EMPLOYEES])
+        LogicalValues(tuples=[[{ 0 }]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testWithInsideWhereExistsDecorrelateRex">
         <Resource name="sql">
             <![CDATA[select * from emp
diff --git a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java 
b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java
index 8a57028..477cb96 100644
--- a/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java
+++ b/piglet/src/main/java/org/apache/calcite/piglet/PigRelBuilder.java
@@ -55,6 +55,7 @@ import com.google.common.collect.ImmutableSet;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -510,8 +511,11 @@ public class PigRelBuilder extends RelBuilder {
   public RelBuilder multiSetFlatten() {
     // [CALCITE-3193] Add RelBuilder.uncollect method, and interface
     // UncollectFactory, to instantiate Uncollect
-    Uncollect uncollect = new Uncollect(cluster,
-        cluster.traitSetOf(Convention.NONE), build(), false);
+    Uncollect uncollect = Uncollect.create(
+        cluster.traitSetOf(Convention.NONE),
+        build(),
+        false,
+        Collections.emptyList());
     push(uncollect);
     return this;
   }

Reply via email to