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;
}