This is an automated email from the ASF dual-hosted git repository. rubenql pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push: new 71e30e2c70 [CALCITE-4999] ARRAY, MULTISET functions should return a collection of scalars if a sub-query returns 1 column 71e30e2c70 is described below commit 71e30e2c70e454bdd09eefaaaa12ad239ba510ce Author: dssysolyatin <dm.sysolya...@gmail.com> AuthorDate: Mon Aug 29 12:13:50 2022 +0300 [CALCITE-4999] ARRAY, MULTISET functions should return a collection of scalars if a sub-query returns 1 column --- .../adapter/enumerable/EnumerableCollect.java | 30 ++++++++++----- .../java/org/apache/calcite/rel/core/Collect.java | 45 ++++++++++++++++++++++ .../apache/calcite/rel/mutable/MutableRels.java | 3 +- .../calcite/rel/rules/SubQueryRemoveRule.java | 14 ++----- .../java/org/apache/calcite/rex/RexSubQuery.java | 9 ++++- .../calcite/sql/fun/SqlArrayQueryConstructor.java | 2 +- .../sql/fun/SqlMultisetQueryConstructor.java | 2 +- .../apache/calcite/sql/type/SqlTypeTransforms.java | 21 ++++++++++ .../org/apache/calcite/sql/type/SqlTypeUtil.java | 22 +++++++++++ .../apache/calcite/sql2rel/SqlToRelConverter.java | 6 +-- .../calcite/sql2rel/StandardConvertletTable.java | 16 ++------ .../java/org/apache/calcite/test/JdbcTest.java | 2 +- .../org/apache/calcite/test/SqlValidatorTest.java | 32 ++++++++++++++- .../org/apache/calcite/test/SqlOperatorTest.java | 31 +++++++++++++++ 14 files changed, 189 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCollect.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCollect.java index da50dff617..64ead33167 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCollect.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCollect.java @@ -25,8 +25,11 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Collect; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.util.BuiltInMethod; +import static java.util.Objects.requireNonNull; + /** Implementation of {@link org.apache.calcite.rel.core.Collect} in * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */ public class EnumerableCollect extends Collect implements EnumerableRel { @@ -91,15 +94,24 @@ public class EnumerableCollect extends Collect implements EnumerableRel { Expression child_ = builder.append( "child", result.block); - // In the internal representation of multisets , every element must be a record. In case the - // result above is a scalar type we have to wrap it around a physical type capable of - // representing records. For this reason the following conversion is necessary. - // REVIEW zabetak January 7, 2019: If we can ensure that the input to this operator - // has the correct physical type (e.g., respecting the Prefer.ARRAY above) then this conversion - // can be removed. - Expression conv_ = - builder.append( - "converted", result.physType.convertTo(child_, JavaRowFormat.ARRAY)); + + RelDataType collectionComponentType = + requireNonNull(rowType().getFieldList().get(0).getType().getComponentType()); + RelDataType childRecordType = result.physType.getRowType().getFieldList().get(0).getType(); + + Expression conv_ = child_; + if (!SqlTypeUtil.sameNamedType(collectionComponentType, childRecordType)) { + // In the internal representation of multisets , every element must be a record. In case the + // result above is a scalar type we have to wrap it around a physical type capable of + // representing records. For this reason the following conversion is necessary. + // REVIEW zabetak January 7, 2019: If we can ensure that the input to this operator + // has the correct physical type (e.g., respecting the Prefer.ARRAY above) + // then this conversion can be removed. + conv_ = + builder.append( + "converted", result.physType.convertTo(child_, JavaRowFormat.ARRAY)); + } + Expression list_ = builder.append("list", Expressions.call(conv_, diff --git a/core/src/main/java/org/apache/calcite/rel/core/Collect.java b/core/src/main/java/org/apache/calcite/rel/core/Collect.java index 28b83c5ff5..50891d5a7e 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/Collect.java +++ b/core/src/main/java/org/apache/calcite/rel/core/Collect.java @@ -25,6 +25,7 @@ import org.apache.calcite.rel.RelWriter; import org.apache.calcite.rel.SingleRel; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; @@ -124,6 +125,7 @@ public class Collect extends SingleRel { * @param collectionType ARRAY, MAP or MULTISET * @param fieldName Name of the sole output field */ + @Deprecated // to be removed before 2.0 public static Collect create(RelNode input, SqlTypeName collectionType, String fieldName) { @@ -132,6 +134,32 @@ public class Collect extends SingleRel { fieldName, input.getRowType())); } + /** + * Creates a Collect. + * + * @param input Input relational expression + * @param sqlKind SqlKind + * @param fieldName Name of the sole output field + */ + public static Collect create(RelNode input, + SqlKind sqlKind, + String fieldName) { + SqlTypeName collectionType = getCollectionType(sqlKind); + RelDataType rowType; + switch (sqlKind) { + case ARRAY_QUERY_CONSTRUCTOR: + case MULTISET_QUERY_CONSTRUCTOR: + rowType = deriveRowType(input.getCluster().getTypeFactory(), + collectionType, fieldName, + SqlTypeUtil.deriveCollectionQueryComponentType(collectionType, input.getRowType())); + break; + default: + rowType = deriveRowType(input.getCluster().getTypeFactory(), collectionType, + fieldName, input.getRowType()); + } + return create(input, rowType); + } + /** Returns the row type, guaranteed not null. * (The row type is never null after initialization, but * CheckerFramework can't deduce that references are safe.) */ @@ -173,6 +201,23 @@ public class Collect extends SingleRel { .getType().getSqlTypeName(); } + private static SqlTypeName getCollectionType(SqlKind sqlKind) { + switch (sqlKind) { + case ARRAY_QUERY_CONSTRUCTOR: + case ARRAY_VALUE_CONSTRUCTOR: + return SqlTypeName.ARRAY; + case MULTISET_QUERY_CONSTRUCTOR: + case MULTISET_VALUE_CONSTRUCTOR: + return SqlTypeName.MULTISET; + case MAP_QUERY_CONSTRUCTOR: + case MAP_VALUE_CONSTRUCTOR: + return SqlTypeName.MAP; + default: + throw new IllegalArgumentException("not a collection kind " + + sqlKind); + } + } + @Override protected RelDataType deriveRowType() { // this method should never be called; rowType is always set throw new UnsupportedOperationException(); 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 708e8816d3..dcaccbb480 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 @@ -53,7 +53,6 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexUtil; -import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.util.Util; import org.apache.calcite.util.mapping.Mapping; @@ -254,7 +253,7 @@ public abstract class MutableRels { case COLLECT: { final MutableCollect collect = (MutableCollect) node; final RelNode child = fromMutable(collect.getInput(), relBuilder); - return Collect.create(child, SqlTypeName.MULTISET, collect.fieldName); + return Collect.create(child, collect.rowType); } case UNCOLLECT: { final MutableUncollect uncollect = (MutableUncollect) node; diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java index b387724153..69e405b73e 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java @@ -40,7 +40,6 @@ import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.fun.SqlQuantifyOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; -import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql2rel.RelDecorrelator; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.util.ImmutableBitSet; @@ -94,13 +93,9 @@ public class SubQueryRemoveRule case SCALAR_QUERY: return rewriteScalarQuery(e, variablesSet, builder, inputCount, offset); case ARRAY_QUERY_CONSTRUCTOR: - return rewriteCollection(e, SqlTypeName.ARRAY, variablesSet, builder, - inputCount, offset); case MAP_QUERY_CONSTRUCTOR: - return rewriteCollection(e, SqlTypeName.MAP, variablesSet, builder, - inputCount, offset); case MULTISET_QUERY_CONSTRUCTOR: - return rewriteCollection(e, SqlTypeName.MULTISET, variablesSet, builder, + return rewriteCollection(e, variablesSet, builder, inputCount, offset); case SOME: return rewriteSome(e, variablesSet, builder); @@ -147,7 +142,6 @@ public class SubQueryRemoveRule * {@link org.apache.calcite.rel.core.Collect}. * * @param e Sub-query to rewrite - * @param collectionType Collection type (ARRAY, MAP, MULTISET) * @param variablesSet A set of variables used by a relational * expression of the specified RexSubQuery * @param builder Builder @@ -155,11 +149,11 @@ public class SubQueryRemoveRule * @return Expression that may be used to replace the RexSubQuery */ private static RexNode rewriteCollection(RexSubQuery e, - SqlTypeName collectionType, Set<CorrelationId> variablesSet, - RelBuilder builder, int inputCount, int offset) { + Set<CorrelationId> variablesSet, RelBuilder builder, + int inputCount, int offset) { builder.push(e.rel); builder.push( - Collect.create(builder.build(), collectionType, "x")); + Collect.create(builder.build(), e.getKind(), "x")); builder.join(JoinRelType.INNER, builder.literal(true), variablesSet); return field(builder, inputCount, offset); } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java index 564e2b63b1..3bb6f2892d 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java @@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlQuantifyOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.type.SqlTypeUtil; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -123,7 +124,9 @@ public class RexSubQuery extends RexCall { public static RexSubQuery array(RelNode rel) { final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); final RelDataType type = - typeFactory.createArrayType(rel.getRowType(), -1L); + typeFactory.createArrayType( + SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.ARRAY, rel.getRowType()), + -1L); return new RexSubQuery(type, SqlStdOperatorTable.ARRAY_QUERY, ImmutableList.of(), rel); } @@ -132,7 +135,9 @@ public class RexSubQuery extends RexCall { public static RexSubQuery multiset(RelNode rel) { final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory(); final RelDataType type = - typeFactory.createMultisetType(rel.getRowType(), -1L); + typeFactory.createMultisetType( + SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.MULTISET, rel.getRowType()), + -1L); return new RexSubQuery(type, SqlStdOperatorTable.MULTISET_QUERY, ImmutableList.of(), rel); } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayQueryConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayQueryConstructor.java index 810df293b9..273f6642bd 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayQueryConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayQueryConstructor.java @@ -27,6 +27,6 @@ public class SqlArrayQueryConstructor extends SqlMultisetQueryConstructor { //~ Constructors ----------------------------------------------------------- public SqlArrayQueryConstructor() { - super("ARRAY", SqlKind.ARRAY_QUERY_CONSTRUCTOR, SqlTypeTransforms.TO_ARRAY); + super("ARRAY", SqlKind.ARRAY_QUERY_CONSTRUCTOR, SqlTypeTransforms.TO_ARRAY_QUERY); } } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java index 2391bba50b..05ed42ca84 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java @@ -52,7 +52,7 @@ public class SqlMultisetQueryConstructor extends SqlSpecialOperator { public SqlMultisetQueryConstructor() { this("MULTISET", SqlKind.MULTISET_QUERY_CONSTRUCTOR, - SqlTypeTransforms.TO_MULTISET); + SqlTypeTransforms.TO_MULTISET_QUERY); } protected SqlMultisetQueryConstructor(String name, SqlKind kind, diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java index cc4e63794b..c6fce21756 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java @@ -174,6 +174,17 @@ public abstract class SqlTypeTransforms { (opBinding, typeToTransform) -> opBinding.getTypeFactory().createMultisetType(typeToTransform, -1); + /** + * Parameter type-inference transform strategy that wraps a given type in a multiset or + * wraps a field of the given type in a multiset if the given type is struct with one field. + * It is used when a multiset input is a sub-query. + */ + public static final SqlTypeTransform TO_MULTISET_QUERY = + (opBinding, typeToTransform) -> + TO_MULTISET.transformType(opBinding, + SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.MULTISET, + typeToTransform)); + /** * Parameter type-inference transform strategy that wraps a given type * in a array. @@ -184,6 +195,16 @@ public abstract class SqlTypeTransforms { (opBinding, typeToTransform) -> opBinding.getTypeFactory().createArrayType(typeToTransform, -1); + /** + * Parameter type-inference transform strategy that wraps a given type in an array or + * wraps a field of the given type in an array if the given type is struct with one field. + * It is used when an array input is a sub-query. + */ + public static final SqlTypeTransform TO_ARRAY_QUERY = + (opBinding, typeToTransform) -> + TO_ARRAY.transformType(opBinding, + SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.ARRAY, typeToTransform)); + /** * Parameter type-inference transform strategy that converts a two-field * record type to a MAP type. 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 3f5a8ec967..23d04b0328 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 @@ -140,6 +140,28 @@ public abstract class SqlTypeUtil { return true; } + /** + * Derives component type for ARRAY, MULTISET, MAP when input is sub-query. + * + * @param origin original component type + * @return component type + */ + public static RelDataType deriveCollectionQueryComponentType( + SqlTypeName collectionType, + RelDataType origin) { + switch (collectionType) { + case ARRAY: + case MULTISET: + return origin.isStruct() && origin.getFieldCount() == 1 + ? origin.getFieldList().get(0).getType() : origin; + case MAP: + return origin; + default: + throw new AssertionError( + "Impossible to derive component type for " + collectionType); + } + } + /** * Iterates over all operands, derives their types, and collects them into * a list. 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 fa02ce5849..571019c589 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -4435,13 +4435,9 @@ public class SqlToRelConverter { joinList.add(lastList); } lastList = new ArrayList<>(); - final SqlTypeName typeName = - requireNonNull(validator, "validator") - .getValidatedNodeType(call) - .getSqlTypeName(); relBuilder.push( Collect.create(requireNonNull(input, "input"), - typeName, castNonNull(validator().deriveAlias(call, i)))); + call.getKind(), castNonNull(validator().deriveAlias(call, i)))); joinList.add(relBuilder.build()); } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java index 7989ebab1a..82d5f1ced8 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java @@ -530,20 +530,10 @@ public class StandardConvertletTable extends ReflectiveConvertletTable { cx.getRexBuilder().makeInputRef( msType, rr.getOffset()); - assert msType.getComponentType() != null && msType.getComponentType().isStruct() - : "componentType of " + msType + " must be struct"; + assert msType.getComponentType() != null + : "componentType of " + msType + " must not be null"; assert originalType.getComponentType() != null - : "componentType of " + originalType + " must be struct"; - if (!originalType.getComponentType().isStruct()) { - // If the type is not a struct, the multiset operator will have - // wrapped the type as a record. Add a call to the $SLICE operator - // to compensate. For example, - // if '<ms>' has type 'RECORD (INTEGER x) MULTISET', - // then '$SLICE(<ms>) has type 'INTEGER MULTISET'. - // This will be removed as the expression is translated. - expr = - cx.getRexBuilder().makeCall(SqlStdOperatorTable.SLICE, expr); - } + : "componentType of " + originalType + " must not be null"; return expr; } 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 ff73459140..2ed221d69d 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -2112,7 +2112,7 @@ public class JdbcTest { .query("select multiset(\n" + " select \"deptno\" from \"hr\".\"emps\") as a\n" + "from (values (1))") - .returnsUnordered("A=[{10}, {20}, {10}, {10}]"); + .returnsUnordered("A=[10, 20, 10, 10]"); } @Test void testUnnestArray() { 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 c8d5fe6ec6..00dd5e5d7b 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -8171,6 +8171,20 @@ public class SqlValidatorTest extends SqlValidatorTestCase { .columnType("CHAR(3) ARRAY NOT NULL"); } + /** + * Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-4999">[CALCITE-4999] + * ARRAY, MULTISET functions should return a collection of scalars + * if a sub-query returns 1 column</a>. + */ + @Test void testArrayQueryConstructor() { + sql("select array(select 1)") + .columnType("INTEGER NOT NULL ARRAY NOT NULL"); + sql("select array(select ROW(1,2))") + .columnType( + "RecordType(INTEGER NOT NULL EXPR$0, INTEGER NOT NULL EXPR$1) NOT NULL ARRAY NOT NULL"); + } + @Test void testCastAsCollectionType() { sql("select cast(array[1,null,2] as int array) from (values (1))") .columnType("INTEGER NOT NULL ARRAY NOT NULL"); @@ -8254,6 +8268,20 @@ public class SqlValidatorTest extends SqlValidatorTestCase { .columnType("INTEGER MULTISET NOT NULL"); } + /** + * Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-4999">[CALCITE-4999] + * ARRAY, MULTISET functions should return an collection of scalars + * if a sub-query returns 1 column</a>. + */ + @Test void testMultisetQueryConstructor() { + sql("select multiset(select 1)") + .columnType("INTEGER NOT NULL MULTISET NOT NULL"); + sql("select multiset(select ROW(1,2))") + .columnType( + "RecordType(INTEGER NOT NULL EXPR$0, INTEGER NOT NULL EXPR$1) NOT NULL MULTISET NOT NULL"); + } + @Test void testUnnestArrayColumn() { final String sql1 = "select d.deptno, e.*\n" + "from dept_nested as d,\n" @@ -8305,14 +8333,14 @@ public class SqlValidatorTest extends SqlValidatorTestCase { sql("select c from unnest(\n" + " array(select deptno from dept)) with ordinality as t(^c^)") .fails("List of column aliases must have same degree as table; table has 2 " - + "columns \\('DEPTNO', 'ORDINALITY'\\), " + + "columns \\('EXPR\\$0', 'ORDINALITY'\\), " + "whereas alias list has 1 columns"); sql("select c from unnest(\n" + " array(select deptno from dept)) with ordinality as t(c, d)").ok(); sql("select c from unnest(\n" + " array(select deptno from dept)) with ordinality as t(^c, d, e^)") .fails("List of column aliases must have same degree as table; table has 2 " - + "columns \\('DEPTNO', 'ORDINALITY'\\), " + + "columns \\('EXPR\\$0', 'ORDINALITY'\\), " + "whereas alias list has 3 columns"); sql("select c\n" + "from unnest(array(select * from dept)) with ordinality as t(^c, d, e, f^)") diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java index a277b1ea65..5bac48f953 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -7125,6 +7125,37 @@ public class SqlOperatorTest { f.checkFails("^Array[]^", "Require at least 1 argument", false); } + /** + * Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-4999">[CALCITE-4999] + * ARRAY, MULTISET functions should return an collection of scalars + * if a sub-query returns 1 column</a>. + */ + @Test void testArrayQueryConstructor() { + final SqlOperatorFixture f = fixture(); + f.setFor(SqlStdOperatorTable.ARRAY_QUERY, SqlOperatorFixture.VmName.EXPAND); + f.checkScalar("array(select 1)", "[1]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.check("select array(select ROW(1,2))", + "RecordType(INTEGER NOT NULL EXPR$0, INTEGER NOT NULL EXPR$1) NOT NULL ARRAY NOT NULL", + "[{1, 2}]"); + } + + /** + * Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-4999">[CALCITE-4999] + * ARRAY, MULTISET functions should return an collection of scalars + * if a sub-query returns 1 column</a>. + */ + @Test void testMultisetQueryConstructor() { + final SqlOperatorFixture f = fixture(); + f.setFor(SqlStdOperatorTable.MULTISET_QUERY, SqlOperatorFixture.VmName.EXPAND); + f.checkScalar("multiset(select 1)", "[1]", "INTEGER NOT NULL MULTISET NOT NULL"); + f.check("select multiset(select ROW(1,2))", + "RecordType(INTEGER NOT NULL EXPR$0, INTEGER NOT NULL EXPR$1) NOT NULL MULTISET NOT NULL", + "[{1, 2}]"); + } + @Test void testItemOp() { final SqlOperatorFixture f = fixture(); f.setFor(SqlStdOperatorTable.ITEM, VmName.EXPAND);