[CALCITE-1250] UNNEST applied to MAP data type (Johannes Schulte) Implicit column names are KEY and VALUE.
Make UNNEST work with multiple arguments of different types. Close apache/calcite#235 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/d757201f Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/d757201f Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/d757201f Branch: refs/heads/master Commit: d757201f155bd0c900725b3ce2776899668fce4d Parents: 2f937c1 Author: baunz <[email protected]> Authored: Thu May 19 23:02:34 2016 +0200 Committer: Julian Hyde <[email protected]> Committed: Tue May 31 00:01:23 2016 -0700 ---------------------------------------------------------------------- .../adapter/enumerable/EnumerableUncollect.java | 27 +++++-- .../org/apache/calcite/rel/core/Uncollect.java | 20 +++-- .../apache/calcite/runtime/SqlFunctions.java | 80 ++++++++++++++------ .../apache/calcite/sql/SqlUnnestOperator.java | 26 +++++-- .../apache/calcite/sql/type/OperandTypes.java | 3 + .../org/apache/calcite/util/BuiltInMethod.java | 36 ++++----- .../java/org/apache/calcite/test/JdbcTest.java | 45 +++++++++++ .../apache/calcite/test/SqlValidatorTest.java | 5 ++ 8 files changed, 184 insertions(+), 58 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java index 0dfa57f..def8e24 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableUncollect.java @@ -25,6 +25,8 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Uncollect; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeField; +import org.apache.calcite.runtime.SqlFunctions.FlatProductInputType; +import org.apache.calcite.sql.type.MapSqlType; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.IntList; @@ -87,20 +89,33 @@ public class EnumerableUncollect extends Uncollect implements EnumerableRel { final Expression child_ = builder.append( "child", result.block); + final List<Integer> fieldCounts = new ArrayList<>(); + final List<FlatProductInputType> inputTypes = new ArrayList<>(); + for (RelDataTypeField field : child.getRowType().getFieldList()) { final RelDataType type = field.getType(); - final RelDataType elementType = type.getComponentType(); - if (elementType.isStruct()) { - fieldCounts.add(elementType.getFieldCount()); + if (type instanceof MapSqlType) { + fieldCounts.add(2); + inputTypes.add(FlatProductInputType.MAP); } else { - fieldCounts.add(-1); + final RelDataType elementType = type.getComponentType(); + if (elementType.isStruct()) { + fieldCounts.add(elementType.getFieldCount()); + inputTypes.add(FlatProductInputType.LIST); + } else { + fieldCounts.add(-1); + inputTypes.add(FlatProductInputType.SCALAR); + } } } + final Expression lambda = Expressions.call(BuiltInMethod.FLAT_PRODUCT.method, Expressions.constant(IntList.toArray(fieldCounts)), - Expressions.constant(withOrdinality)); + Expressions.constant(withOrdinality), + Expressions.constant( + inputTypes.toArray(new FlatProductInputType[inputTypes.size()]))); builder.add( Expressions.return_(null, Expressions.call(child_, @@ -108,6 +123,8 @@ public class EnumerableUncollect extends Uncollect implements EnumerableRel { lambda))); return implementor.result(physType, builder.toBlock()); } + } + // End EnumerableUncollect.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java index 2d46929..d391f93 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java @@ -28,6 +28,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.SqlUnnestOperator; import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.sql.type.MapSqlType; import org.apache.calcite.sql.type.SqlTypeName; import java.util.List; @@ -125,14 +126,19 @@ public class Uncollect extends SingleRel { final RelDataTypeFactory.FieldInfoBuilder builder = rel.getCluster().getTypeFactory().builder(); for (RelDataTypeField field : fields) { - RelDataType ret = field.getType().getComponentType(); - assert null != ret; - if (ret.isStruct()) { - builder.addAll(ret.getFieldList()); + if (field.getType() instanceof MapSqlType) { + builder.add(SqlUnnestOperator.MAP_KEY_COLUMN_NAME, field.getType().getKeyType()); + builder.add(SqlUnnestOperator.MAP_VALUE_COLUMN_NAME, field.getType().getValueType()); } else { - // Element type is not a record. It may be a scalar type, say - // "INTEGER". Wrap it in a struct type. - builder.add(SqlUtil.deriveAliasFromOrdinal(field.getIndex()), ret); + RelDataType ret = field.getType().getComponentType(); + assert null != ret; + if (ret.isStruct()) { + builder.addAll(ret.getFieldList()); + } else { + // Element type is not a record. It may be a scalar type, say + // "INTEGER". Wrap it in a struct type. + builder.add(SqlUtil.deriveAliasFromOrdinal(field.getIndex()), ret); + } } } if (withOrdinality) { http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index 7e2d84d..e8da557 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -29,6 +29,7 @@ import org.apache.calcite.linq4j.function.Deterministic; import org.apache.calcite.linq4j.function.Function1; import org.apache.calcite.linq4j.function.NonDeterministic; import org.apache.calcite.linq4j.tree.Primitive; +import org.apache.calcite.runtime.FlatLists.ComparableList; import java.math.BigDecimal; import java.math.BigInteger; @@ -42,6 +43,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; @@ -1489,52 +1491,78 @@ public class SqlFunctions { } } - public static <E extends Comparable> - Function1<Object, Enumerable<FlatLists.ComparableList<E>>> - flatProduct(final int[] fieldCounts, final boolean withOrdinality) { + public static Function1<Object, Enumerable<ComparableList<Comparable>>> + flatProduct(final int[] fieldCounts, final boolean withOrdinality, + final FlatProductInputType[] inputTypes) { if (fieldCounts.length == 1) { - if (!withOrdinality) { + if (!withOrdinality && inputTypes[0] == FlatProductInputType.SCALAR) { //noinspection unchecked return (Function1) LIST_AS_ENUMERABLE; } else { - return new Function1<Object, Enumerable<FlatLists.ComparableList<E>>>() { - public Enumerable<FlatLists.ComparableList<E>> apply(Object row) { - return p2(new Object[] {row}, fieldCounts, true); + return new Function1<Object, Enumerable<ComparableList<Comparable>>>() { + public Enumerable<ComparableList<Comparable>> apply(Object row) { + return p2(new Object[] { row }, fieldCounts, withOrdinality, + inputTypes); } }; } } - return new Function1<Object, Enumerable<FlatLists.ComparableList<E>>>() { - public Enumerable<FlatLists.ComparableList<E>> apply(Object lists) { - return p2((Object[]) lists, fieldCounts, withOrdinality); + return new Function1<Object, Enumerable<FlatLists.ComparableList<Comparable>>>() { + public Enumerable<FlatLists.ComparableList<Comparable>> apply(Object lists) { + return p2((Object[]) lists, fieldCounts, withOrdinality, + inputTypes); } }; } - private static <E extends Comparable> - Enumerable<FlatLists.ComparableList<E>> p2(Object[] lists, int[] fieldCounts, - boolean withOrdinality) { - final List<Enumerator<List<E>>> enumerators = new ArrayList<>(); + private static Enumerable<FlatLists.ComparableList<Comparable>> + p2(Object[] lists, int[] fieldCounts, boolean withOrdinality, + FlatProductInputType[] inputTypes) { + final List<Enumerator<List<Comparable>>> enumerators = new ArrayList<>(); int totalFieldCount = 0; for (int i = 0; i < lists.length; i++) { int fieldCount = fieldCounts[i]; - if (fieldCount < 0) { - ++totalFieldCount; - @SuppressWarnings("unchecked") - List<E> list = (List<E>) lists[i]; + FlatProductInputType inputType = inputTypes[i]; + Object inputObject = lists[i]; + switch (inputType) { + case SCALAR: + @SuppressWarnings("unchecked") List<Comparable> list = + (List<Comparable>) inputObject; enumerators.add( Linq4j.transform( Linq4j.enumerator(list), - new Function1<E, List<E>>() { - public List<E> apply(E a0) { + new Function1<Comparable, List<Comparable>>() { + public List<Comparable> apply(Comparable a0) { return FlatLists.of(a0); } })); + break; + case LIST: + @SuppressWarnings("unchecked") List<List<Comparable>> listList = + (List<List<Comparable>>) inputObject; + enumerators.add(Linq4j.enumerator(listList)); + break; + case MAP: + @SuppressWarnings("unchecked") Map<Comparable, Comparable> map = + (Map<Comparable, Comparable>) inputObject; + Enumerator<Entry<Comparable, Comparable>> enumerator = + Linq4j.enumerator(map.entrySet()); + + Enumerator<List<Comparable>> transformed = Linq4j.transform(enumerator, + new Function1<Entry<Comparable, Comparable>, List<Comparable>>() { + public List<Comparable> apply(Entry<Comparable, Comparable> entry) { + return FlatLists.<Comparable>of(entry.getKey(), entry.getValue()); + } + }); + enumerators.add(transformed); + break; + default: + break; + } + if (fieldCount < 0) { + ++totalFieldCount; } else { totalFieldCount += fieldCount; - @SuppressWarnings("unchecked") - List<List<E>> list = (List<List<E>>) lists[i]; - enumerators.add(Linq4j.enumerator(list)); } } if (withOrdinality) { @@ -1592,6 +1620,12 @@ public class SqlFunctions { return FlatLists.ofComparable(list); } } + + /** Type of argument passed into {@link #flatProduct}. */ + public enum FlatProductInputType { + SCALAR, LIST, MAP + } + } // End SqlFunctions.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java index b3d0a1b..7d32fe6 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlUnnestOperator.java @@ -16,15 +16,18 @@ */ package org.apache.calcite.sql; + import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.type.ArraySqlType; +import org.apache.calcite.sql.type.MapSqlType; import org.apache.calcite.sql.type.MultisetSqlType; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.SqlOperandCountRanges; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Util; + /** * The <code>UNNEST</code> operator. */ @@ -36,6 +39,10 @@ public class SqlUnnestOperator extends SqlFunctionalOperator { public static final String ORDINALITY_COLUMN_NAME = "ORDINALITY"; + public static final String MAP_KEY_COLUMN_NAME = "KEY"; + + public static final String MAP_VALUE_COLUMN_NAME = "VALUE"; + //~ Constructors ----------------------------------------------------------- public SqlUnnestOperator(boolean withOrdinality) { @@ -47,7 +54,7 @@ public class SqlUnnestOperator extends SqlFunctionalOperator { null, null, OperandTypes.repeat(SqlOperandCountRanges.from(1), - OperandTypes.SCALAR_OR_RECORD_COLLECTION)); + OperandTypes.SCALAR_OR_RECORD_COLLECTION_OR_MAP)); this.withOrdinality = withOrdinality; } @@ -61,12 +68,18 @@ public class SqlUnnestOperator extends SqlFunctionalOperator { if (type.isStruct()) { type = type.getFieldList().get(0).getType(); } - assert type instanceof ArraySqlType || type instanceof MultisetSqlType; - if (type.getComponentType().isStruct()) { - builder.addAll(type.getComponentType().getFieldList()); + assert type instanceof ArraySqlType || type instanceof MultisetSqlType + || type instanceof MapSqlType; + if (type instanceof MapSqlType) { + builder.add(MAP_KEY_COLUMN_NAME, type.getKeyType()); + builder.add(MAP_VALUE_COLUMN_NAME, type.getValueType()); } else { - builder.add(SqlUtil.deriveAliasFromOrdinal(operand), - type.getComponentType()); + if (type.getComponentType().isStruct()) { + builder.addAll(type.getComponentType().getFieldList()); + } else { + builder.add(SqlUtil.deriveAliasFromOrdinal(operand), + type.getComponentType()); + } } } if (withOrdinality) { @@ -86,6 +99,7 @@ public class SqlUnnestOperator extends SqlFunctionalOperator { public boolean argumentMustBeScalar(int ordinal) { return false; } + } // End SqlUnnestOperator.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java index 1d43ba0..f101ee9 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java @@ -529,6 +529,9 @@ public abstract class OperandTypes { public static final SqlSingleOperandTypeChecker SCALAR_OR_RECORD_COLLECTION = OperandTypes.or(COLLECTION, RECORD_COLLECTION); + public static final SqlSingleOperandTypeChecker SCALAR_OR_RECORD_COLLECTION_OR_MAP = + OperandTypes.or(COLLECTION_OR_MAP, RECORD_COLLECTION); + public static final SqlOperandTypeChecker MULTISET_MULTISET = new MultisetOperandTypeChecker(); http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index 53f7880..6a17843 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -43,9 +43,24 @@ import org.apache.calcite.linq4j.function.Predicate2; import org.apache.calcite.linq4j.tree.FunctionExpression; import org.apache.calcite.linq4j.tree.Primitive; import org.apache.calcite.linq4j.tree.Types; +import org.apache.calcite.rel.metadata.BuiltInMetadata.Collation; +import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnOrigin; +import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnUniqueness; +import org.apache.calcite.rel.metadata.BuiltInMetadata.CumulativeCost; +import org.apache.calcite.rel.metadata.BuiltInMetadata.DistinctRowCount; +import org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution; +import org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility; +import org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount; import org.apache.calcite.rel.metadata.BuiltInMetadata.Memory; +import org.apache.calcite.rel.metadata.BuiltInMetadata.NonCumulativeCost; import org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism; +import org.apache.calcite.rel.metadata.BuiltInMetadata.PercentageOriginalRows; +import org.apache.calcite.rel.metadata.BuiltInMetadata.PopulationSize; +import org.apache.calcite.rel.metadata.BuiltInMetadata.Predicates; +import org.apache.calcite.rel.metadata.BuiltInMetadata.RowCount; +import org.apache.calcite.rel.metadata.BuiltInMetadata.Selectivity; import org.apache.calcite.rel.metadata.BuiltInMetadata.Size; +import org.apache.calcite.rel.metadata.BuiltInMetadata.UniqueKeys; import org.apache.calcite.rel.metadata.Metadata; import org.apache.calcite.rex.RexNode; import org.apache.calcite.runtime.ArrayBindable; @@ -56,6 +71,7 @@ import org.apache.calcite.runtime.FlatLists; import org.apache.calcite.runtime.ResultSetEnumerable; import org.apache.calcite.runtime.SortedMultiMap; import org.apache.calcite.runtime.SqlFunctions; +import org.apache.calcite.runtime.SqlFunctions.FlatProductInputType; import org.apache.calcite.runtime.Utilities; import org.apache.calcite.schema.FilterableTable; import org.apache.calcite.schema.ModifiableTable; @@ -85,23 +101,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.TimeZone; -import javax.sql.DataSource; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.Collation; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnOrigin; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnUniqueness; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.CumulativeCost; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.DistinctRowCount; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.NonCumulativeCost; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.PercentageOriginalRows; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.PopulationSize; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.Predicates; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.RowCount; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.Selectivity; -import static org.apache.calcite.rel.metadata.BuiltInMetadata.UniqueKeys; +import javax.sql.DataSource; /** * Built-in methods. @@ -177,7 +178,8 @@ public enum BuiltInMethod { FUNCTION1_APPLY(Function1.class, "apply", Object.class), ARRAYS_AS_LIST(Arrays.class, "asList", Object[].class), ARRAY(SqlFunctions.class, "array", Object[].class), - FLAT_PRODUCT(SqlFunctions.class, "flatProduct", int[].class, boolean.class), + FLAT_PRODUCT(SqlFunctions.class, "flatProduct", int[].class, boolean.class, + FlatProductInputType[].class), LIST_N(FlatLists.class, "copyOf", Comparable[].class), LIST2(FlatLists.class, "of", Object.class, Object.class), LIST3(FlatLists.class, "of", Object.class, Object.class, Object.class), http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/test/java/org/apache/calcite/test/JdbcTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java index 06a6854..bab74d6 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -52,6 +52,7 @@ import org.apache.calcite.rel.core.TableModify; import org.apache.calcite.rel.logical.LogicalTableModify; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.runtime.FlatLists; import org.apache.calcite.runtime.Hook; import org.apache.calcite.runtime.SqlFunctions; import org.apache.calcite.schema.ModifiableTable; @@ -2286,6 +2287,50 @@ public class JdbcTest { "name=Sales; EI=150; D=10; N=Sebastian; S=7000.0; C=null; I=2; O=5"); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1250">[CALCITE-1250] + * UNNEST applied to MAP data type</a>. */ + @Test public void testUnnestItemsInMap() throws SQLException { + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + final String sql = "select * from unnest(MAP['a', 1, 'b', 2]) as um(k, v)"; + ResultSet resultSet = connection.createStatement().executeQuery(sql); + final String expected = "K=a; V=1\n" + + "K=b; V=2\n"; + assertThat(CalciteAssert.toString(resultSet), is(expected)); + connection.close(); + } + + @Test public void testUnnestItemsInMapWithOrdinality() throws SQLException { + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + final String sql = "select *\n" + + "from unnest(MAP['a', 1, 'b', 2]) with ordinality as um(k, v, i)"; + ResultSet resultSet = connection.createStatement().executeQuery(sql); + final String expected = "K=a; V=1; I=1\n" + + "K=b; V=2; I=2\n"; + assertThat(CalciteAssert.toString(resultSet), is(expected)); + connection.close(); + } + + @Test public void testUnnestItemsInMapWithNoAliasAndAdditionalArgument() + throws SQLException { + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + final String sql = + "select * from unnest(MAP['a', 1, 'b', 2], array[5, 6, 7])"; + ResultSet resultSet = connection.createStatement().executeQuery(sql); + + List<String> map = FlatLists.of("KEY=a; VALUE=1", "KEY=b; VALUE=2"); + List<String> array = FlatLists.of(" EXPR$1=5", " EXPR$1=6", " EXPR$1=7"); + + final StringBuilder b = new StringBuilder(); + for (List<String> row : Linq4j.product(FlatLists.of(map, array))) { + b.append(row.get(0)).append(";").append(row.get(1)).append("\n"); + } + final String expected = b.toString(); + + assertThat(CalciteAssert.toString(resultSet), is(expected)); + connection.close(); + } + private CalciteAssert.AssertQuery withFoodMartQuery(int id) throws IOException { final FoodmartTest.FoodMartQuerySet set = http://git-wip-us.apache.org/repos/asf/calcite/blob/d757201f/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index 4a29961..c05dab3 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -6700,6 +6700,11 @@ public class SqlValidatorTest extends SqlValidatorTestCase { "Column 'ORDINALITY' not found in any table"); } + @Test public void unnestMapMustNameColumnsKeyAndValueWhenNotAliased() { + checkResultType("select * from unnest(map[1, 12, 2, 22])", + "RecordType(INTEGER NOT NULL KEY, INTEGER NOT NULL VALUE) NOT NULL"); + } + @Test public void testCorrelationJoin() { check("select *," + " multiset(select * from emp where deptno=dept.deptno) "
