From 283529fcde50a43ffc4385486e65d6e6828779ac Mon Sep 17 00:00:00 2001 From: Laksh Singla Date: Sat, 4 Nov 2023 09:56:10 +0530 Subject: [PATCH] [Backport] Allow casted literal values in SQL functions accepting literals (Part 2) (#15322) Backport of #15316 to 28.0.0. --- codestyle/druid-forbidden-apis.txt | 6 + ...CompressedBigDecimalSqlAggregatorBase.java | 11 +- .../type/CastedLiteralOperandTypeChecker.java | 92 ++++++++++ .../CastedLiteralOperandTypeCheckers.java | 159 +++++++++++++++++ .../builtin/ArrayConcatSqlAggregator.java | 3 +- .../builtin/ArraySqlAggregator.java | 7 +- .../builtin/StringSqlAggregator.java | 3 +- .../sql/calcite/CalciteArraysQueryTest.java | 165 +++++++++++++++++- .../druid/sql/calcite/CalciteQueryTest.java | 2 +- 9 files changed, 438 insertions(+), 10 deletions(-) create mode 100644 sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeChecker.java create mode 100644 sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeCheckers.java diff --git a/codestyle/druid-forbidden-apis.txt b/codestyle/druid-forbidden-apis.txt index 8da588f9d31d..b35bdede7749 100644 --- a/codestyle/druid-forbidden-apis.txt +++ b/codestyle/druid-forbidden-apis.txt @@ -45,6 +45,12 @@ java.util.Random#() @ Use ThreadLocalRandom.current() or the constructor w java.lang.Math#random() @ Use ThreadLocalRandom.current() java.util.regex.Pattern#matches(java.lang.String,java.lang.CharSequence) @ Use String.startsWith(), endsWith(), contains(), or compile and cache a Pattern explicitly org.apache.calcite.sql.type.OperandTypes#LITERAL @ LITERAL type checker throws when literals with CAST are passed. Use org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker instead. +org.apache.calcite.sql.type.OperandTypes#BOOLEAN_LITERAL @ Create a type checker like org.apache.calcite.sql.type.POSITIVE_INTEGER_LITERAL and use that instead +org.apache.calcite.sql.type.OperandTypes#ARRAY_BOOLEAN_LITERAL @ Create a type checker like org.apache.calcite.sql.type.POSITIVE_INTEGER_LITERAL and use that instead +org.apache.calcite.sql.type.OperandTypes#POSITIVE_INTEGER_LITERAL @ Use org.apache.calcite.sql.type.POSITIVE_INTEGER_LITERAL instead +org.apache.calcite.sql.type.OperandTypes#UNIT_INTERVAL_NUMERIC_LITERAL @ Create a type checker like org.apache.calcite.sql.type.POSITIVE_INTEGER_LITERAL and use that instead +org.apache.calcite.sql.type.OperandTypes#NUMERIC_UNIT_INTERVAL_NUMERIC_LITERAL @ Create a type checker like org.apache.calcite.sql.type.POSITIVE_INTEGER_LITERAL and use that instead +org.apache.calcite.sql.type.OperandTypes#NULLABLE_LITERAL @ Create an instance of org.apache.calcite.sql.type.CastedLiteralOperandTypeChecker that allows nulls and use that instead org.apache.commons.io.FileUtils#getTempDirectory() @ Use org.junit.rules.TemporaryFolder for tests instead org.apache.commons.io.FileUtils#deleteDirectory(java.io.File) @ Use org.apache.druid.java.util.common.FileUtils#deleteDirectory() org.apache.commons.io.FileUtils#forceMkdir(java.io.File) @ Use org.apache.druid.java.util.common.FileUtils.mkdirp instead diff --git a/extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/CompressedBigDecimalSqlAggregatorBase.java b/extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/CompressedBigDecimalSqlAggregatorBase.java index a6c23551598e..33c010b58cc8 100644 --- a/extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/CompressedBigDecimalSqlAggregatorBase.java +++ b/extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/CompressedBigDecimalSqlAggregatorBase.java @@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.type.CastedLiteralOperandTypeCheckers; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlTypeFamily; @@ -156,7 +157,7 @@ private CompressedBigDecimalSqlAggFunction(String name) OperandTypes.sequence( "'" + name + "(column, size)'", OperandTypes.ANY, - OperandTypes.POSITIVE_INTEGER_LITERAL + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL ), OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.EXACT_NUMERIC) ), @@ -164,8 +165,8 @@ private CompressedBigDecimalSqlAggFunction(String name) OperandTypes.sequence( "'" + name + "(column, size, scale)'", OperandTypes.ANY, - OperandTypes.POSITIVE_INTEGER_LITERAL, - OperandTypes.POSITIVE_INTEGER_LITERAL + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL, + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL ), OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.EXACT_NUMERIC, SqlTypeFamily.EXACT_NUMERIC) ), @@ -173,8 +174,8 @@ private CompressedBigDecimalSqlAggFunction(String name) OperandTypes.sequence( "'" + name + "(column, size, scale, strictNumberParsing)'", OperandTypes.ANY, - OperandTypes.POSITIVE_INTEGER_LITERAL, - OperandTypes.POSITIVE_INTEGER_LITERAL, + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL, + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL, OperandTypes.BOOLEAN ), OperandTypes.family( diff --git a/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeChecker.java b/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeChecker.java new file mode 100644 index 000000000000..b5c3ccc96ba2 --- /dev/null +++ b/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeChecker.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//CHECKSTYLE.OFF: PackageName - Must be in Calcite + +package org.apache.calcite.sql.type; + +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlUtil; +import org.apache.calcite.util.Static; +import org.apache.calcite.util.Util; + +/** + * Like {@link LiteralOperandTypeChecker}, but also allows casted literals. + * + * "Casted literals" are like `CAST(100 AS INTEGER)`. While it doesn't make sense to cast a literal that the user + * themselves enter, it is important to add a broader validation to allow these literals because Calcite's JDBC driver + * doesn't allow the wildcards (?)to work without a cast, and there's no workaround it. + *

+ * This makes sure that the functions using the literal operand type checker can be workaround the JDBC's restriction, + * without being marked as invalid SQL input + */ + +public class CastedLiteralOperandTypeChecker implements SqlSingleOperandTypeChecker +{ + public static SqlSingleOperandTypeChecker LITERAL = new CastedLiteralOperandTypeChecker(false); + + private final boolean allowNull; + + CastedLiteralOperandTypeChecker(boolean allowNull) + { + this.allowNull = allowNull; + } + + @Override + public boolean checkSingleOperandType( + SqlCallBinding callBinding, + SqlNode node, + int iFormalOperand, + boolean throwOnFailure + ) + { + Util.discard(iFormalOperand); + + if (SqlUtil.isNullLiteral(node, true)) { + if (allowNull) { + return true; + } + if (throwOnFailure) { + throw callBinding.newError( + Static.RESOURCE.argumentMustNotBeNull( + callBinding.getOperator().getName())); + } + return false; + } + // The following line of code is the only difference between the OperandTypes.LITERAL and this type checker + if (!SqlUtil.isLiteral(node, true) && !SqlUtil.isLiteralChain(node)) { + if (throwOnFailure) { + throw callBinding.newError( + Static.RESOURCE.argumentMustBeLiteral( + callBinding.getOperator().getName())); + } + return false; + } + + return true; + } + + @Override + public String getAllowedSignatures(SqlOperator op, String opName) + { + return ""; + } +} diff --git a/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeCheckers.java b/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeCheckers.java new file mode 100644 index 000000000000..0155f7ba6f45 --- /dev/null +++ b/sql/src/main/java/org/apache/calcite/sql/type/CastedLiteralOperandTypeCheckers.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//CHECKSTYLE.OFF: PackageName - Must be in Calcite + +package org.apache.calcite.sql.type; + +import com.google.common.collect.ImmutableList; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlCallBinding; +import org.apache.calcite.sql.SqlLiteral; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.Static; +import org.apache.druid.error.DruidException; + +import java.math.BigDecimal; + +public class CastedLiteralOperandTypeCheckers +{ + public static final SqlSingleOperandTypeChecker LITERAL = new CastedLiteralOperandTypeChecker(false); + + /** + * Blatantly copied from {@link OperandTypes#POSITIVE_INTEGER_LITERAL}, however the reference to the {@link #LITERAL} + * is the one which accepts casted literals + */ + public static final SqlSingleOperandTypeChecker POSITIVE_INTEGER_LITERAL = + new FamilyOperandTypeChecker( + ImmutableList.of(SqlTypeFamily.INTEGER), + i -> false + ) + { + @Override + public boolean checkSingleOperandType( + SqlCallBinding callBinding, + SqlNode operand, + int iFormalOperand, + SqlTypeFamily family, + boolean throwOnFailure + ) + { + // This LITERAL refers to the above implementation, the one which allows casted literals + if (!LITERAL.checkSingleOperandType( + callBinding, + operand, + iFormalOperand, + throwOnFailure + )) { + return false; + } + + if (!super.checkSingleOperandType( + callBinding, + operand, + iFormalOperand, + family, + throwOnFailure + )) { + return false; + } + + final SqlLiteral arg = fetchPrimitiveLiteralFromCasts(operand); + final BigDecimal value = arg.getValueAs(BigDecimal.class); + if (value.compareTo(BigDecimal.ZERO) < 0 + || hasFractionalPart(value)) { + if (throwOnFailure) { + throw callBinding.newError( + Static.RESOURCE.argumentMustBePositiveInteger( + callBinding.getOperator().getName())); + } + return false; + } + if (value.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE)) > 0) { + if (throwOnFailure) { + throw callBinding.newError( + Static.RESOURCE.numberLiteralOutOfRange(value.toString())); + } + return false; + } + return true; + } + + /** Returns whether a number has any fractional part. + * + * @see BigDecimal#longValueExact() */ + private boolean hasFractionalPart(BigDecimal bd) + { + return bd.precision() - bd.scale() <= 0; + } + }; + + public static boolean isLiteral(SqlNode node, boolean allowCast) + { + assert node != null; + if (node instanceof SqlLiteral) { + return true; + } + if (!allowCast) { + return false; + } + switch (node.getKind()) { + case CAST: + // "CAST(e AS type)" is literal if "e" is literal + return isLiteral(((SqlCall) node).operand(0), true); + case MAP_VALUE_CONSTRUCTOR: + case ARRAY_VALUE_CONSTRUCTOR: + return ((SqlCall) node).getOperandList().stream() + .allMatch(o -> isLiteral(o, true)); + case DEFAULT: + return true; // DEFAULT is always NULL + default: + return false; + } + } + + /** + * Fetches primitive literals from the casts, including NULL literal. + * It throws if the entered node isn't a primitive literal, which can be cast multiple times. + * + * Therefore, it would fail on the following types: + * 1. Nodes that are not of the form CAST(....(CAST LITERAL AS TYPE).....) + * 2. ARRAY and MAP literals. This won't be required since we are only using this method in the type checker for + * primitive types + */ + private static SqlLiteral fetchPrimitiveLiteralFromCasts(SqlNode node) + { + if (node == null) { + throw DruidException.defensive("'node' cannot be null"); + } + if (node instanceof SqlLiteral) { + return (SqlLiteral) node; + } + + switch (node.getKind()) { + case CAST: + return fetchPrimitiveLiteralFromCasts(((SqlCall) node).operand(0)); + case DEFAULT: + return SqlLiteral.createNull(SqlParserPos.ZERO); + default: + throw DruidException.defensive("Expected a literal or a cast on the literal. Found [%s] instead", node.getKind()); + } + } +} diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArrayConcatSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArrayConcatSqlAggregator.java index be21701d1eb8..a5e62f5e2a9b 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArrayConcatSqlAggregator.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArrayConcatSqlAggregator.java @@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.type.CastedLiteralOperandTypeCheckers; import org.apache.calcite.sql.type.InferTypes; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; @@ -156,7 +157,7 @@ private static class ArrayConcatAggFunction extends SqlAggFunction OperandTypes.sequence( StringUtils.format("'%s(expr, maxSizeBytes)'", NAME), OperandTypes.ARRAY, - OperandTypes.POSITIVE_INTEGER_LITERAL + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL ) ), SqlFunctionCategory.USER_DEFINED_FUNCTION, diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArraySqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArraySqlAggregator.java index 9af5210905ef..efb84dca6251 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArraySqlAggregator.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/ArraySqlAggregator.java @@ -28,6 +28,7 @@ import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperatorBinding; +import org.apache.calcite.sql.type.CastedLiteralOperandTypeCheckers; import org.apache.calcite.sql.type.InferTypes; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.SqlReturnTypeInference; @@ -179,7 +180,11 @@ private static class ArrayAggFunction extends SqlAggFunction OperandTypes.or( OperandTypes.ANY, OperandTypes.and( - OperandTypes.sequence(StringUtils.format("'%s(expr, maxSizeBytes)'", NAME), OperandTypes.ANY, OperandTypes.POSITIVE_INTEGER_LITERAL), + OperandTypes.sequence( + StringUtils.format("'%s(expr, maxSizeBytes)'", NAME), + OperandTypes.ANY, + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL + ), OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC) ) ), diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/StringSqlAggregator.java b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/StringSqlAggregator.java index 7c1389de3fe3..a78b3a7a4797 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/StringSqlAggregator.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/StringSqlAggregator.java @@ -29,6 +29,7 @@ import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperatorBinding; +import org.apache.calcite.sql.type.CastedLiteralOperandTypeCheckers; import org.apache.calcite.sql.type.InferTypes; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.SqlReturnTypeInference; @@ -251,7 +252,7 @@ private static class StringAggFunction extends SqlAggFunction StringUtils.format("'%s(expr, separator, maxSizeBytes)'", name), OperandTypes.ANY, OperandTypes.STRING, - OperandTypes.POSITIVE_INTEGER_LITERAL + CastedLiteralOperandTypeCheckers.POSITIVE_INTEGER_LITERAL ), OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC) ) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java index 0756c5f21d05..41bdb5fa5887 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java @@ -2708,6 +2708,106 @@ public void testArrayAggArrays() ); } + @Test + public void testArrayAggArraysWithMaxSizeBytes() + { + // Produces nested array - ARRAY>, which frame writers don't support. A way to get this query + // to run would be to use nested columns. + msqIncompatible(); + cannotVectorize(); + testQuery( + "SELECT ARRAY_AGG(ARRAY[l1, l2], 10000), ARRAY_AGG(DISTINCT ARRAY[l1, l2], CAST(10000 AS INTEGER)) FROM numfoo", + QUERY_CONTEXT_NO_STRINGIFY_ARRAY, + ImmutableList.of( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE3) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .virtualColumns( + expressionVirtualColumn("v0", "array(\"l1\",\"l2\")", ColumnType.LONG_ARRAY) + ) + .aggregators( + aggregators( + new ExpressionLambdaAggregatorFactory( + "a0", + ImmutableSet.of("v0"), + "__acc", + "ARRAY>[]", + "ARRAY>[]", + true, + true, + false, + "array_append(\"__acc\", \"v0\")", + "array_concat(\"__acc\", \"a0\")", + null, + null, + new HumanReadableBytes(10000), + TestExprMacroTable.INSTANCE + ), + new ExpressionLambdaAggregatorFactory( + "a1", + ImmutableSet.of("v0"), + "__acc", + "ARRAY>[]", + "ARRAY>[]", + true, + true, + false, + "array_set_add(\"__acc\", \"v0\")", + "array_set_add_all(\"__acc\", \"a1\")", + null, + null, + new HumanReadableBytes(10000), + TestExprMacroTable.INSTANCE + ) + ) + ) + .context(QUERY_CONTEXT_NO_STRINGIFY_ARRAY) + .build() + ), + (sql, queryResults) -> { + // ordering is not stable in array_agg and array_concat_agg + List expected = ImmutableList.of( + useDefault ? + new Object[]{ + Arrays.asList( + Arrays.asList(7L, 0L), + Arrays.asList(325323L, 325323L), + Arrays.asList(0L, 0L), + Arrays.asList(0L, 0L), + Arrays.asList(0L, 0L), + Arrays.asList(0L, 0L) + ), + Arrays.asList( + Arrays.asList(0L, 0L), + Arrays.asList(7L, 0L), + Arrays.asList(325323L, 325323L) + ) + } + : + new Object[]{ + Arrays.asList( + Arrays.asList(7L, null), + Arrays.asList(325323L, 325323L), + Arrays.asList(0L, 0L), + Arrays.asList(null, null), + Arrays.asList(null, null), + Arrays.asList(null, null) + ), + Arrays.asList( + Arrays.asList(null, null), + Arrays.asList(0L, 0L), + Arrays.asList(7L, null), + Arrays.asList(325323L, 325323L) + ) + } + ); + assertResultsDeepEquals(sql, expected, queryResults.results); + } + ); + } + + @Test public void testArrayConcatAggArrays() { @@ -2769,6 +2869,69 @@ public void testArrayConcatAggArrays() ); } + @Test + public void testArrayConcatAggArraysWithMaxSizeBytes() + { + cannotVectorize(); + testQuery( + "SELECT ARRAY_CONCAT_AGG(ARRAY[l1, l2], 10000), ARRAY_CONCAT_AGG(DISTINCT ARRAY[l1, l2], CAST(10000 AS INTEGER)) " + + "FROM numfoo", + ImmutableList.of( + Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE3) + .intervals(querySegmentSpec(Filtration.eternity())) + .granularity(Granularities.ALL) + .virtualColumns( + expressionVirtualColumn("v0", "array(\"l1\",\"l2\")", ColumnType.LONG_ARRAY) + ) + .aggregators( + aggregators( + new ExpressionLambdaAggregatorFactory( + "a0", + ImmutableSet.of("v0"), + "__acc", + "ARRAY[]", + "ARRAY[]", + true, + false, + false, + "array_concat(\"__acc\", \"v0\")", + "array_concat(\"__acc\", \"a0\")", + null, + null, + new HumanReadableBytes(10000), + TestExprMacroTable.INSTANCE + ), + new ExpressionLambdaAggregatorFactory( + "a1", + ImmutableSet.of("v0"), + "__acc", + "ARRAY[]", + "ARRAY[]", + true, + false, + false, + "array_set_add_all(\"__acc\", \"v0\")", + "array_set_add_all(\"__acc\", \"a1\")", + null, + null, + new HumanReadableBytes(10000), + TestExprMacroTable.INSTANCE + ) + ) + ) + .context(QUERY_CONTEXT_DEFAULT) + .build() + ), + ImmutableList.of( + useDefault + ? new Object[]{"[7,0,325323,325323,0,0,0,0,0,0,0,0]", "[0,7,325323]"} + : new Object[]{"[7,null,325323,325323,0,0,null,null,null,null,null,null]", "[null,0,7,325323]"} + ) + ); + } + + @Test public void testArrayAggArrayColumns() @@ -3031,7 +3194,7 @@ public void testArrayAggMaxBytes() { cannotVectorize(); testQuery( - "SELECT ARRAY_AGG(l1, 128), ARRAY_AGG(DISTINCT l1, 128) FROM numfoo", + "SELECT ARRAY_AGG(l1, 128), ARRAY_AGG(DISTINCT l1, CAST(128 AS INTEGER)) FROM numfoo", ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource(CalciteTests.DATASOURCE3) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index fd4737488368..0831cf7fac3c 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -13478,7 +13478,7 @@ public void testStringAggMaxBytes() { cannotVectorize(); testQuery( - "SELECT STRING_AGG(l1, ',', 128), STRING_AGG(DISTINCT l1, ',', 128) FROM numfoo", + "SELECT STRING_AGG(l1, ',', 128), STRING_AGG(DISTINCT l1, ',', CAST(128 AS INTEGER)) FROM numfoo", ImmutableList.of( Druids.newTimeseriesQueryBuilder() .dataSource(CalciteTests.DATASOURCE3)