From c3426f20d13b77011942705edecedda72ad72a9a Mon Sep 17 00:00:00 2001 From: dylanhz Date: Fri, 26 Dec 2025 20:43:00 +0800 Subject: [PATCH 1/2] [FLINK-38841][table] Enforce expected nullability in FamilyArgumentStrategy --- .../strategies/FamilyArgumentTypeStrategy.java | 5 ++++- .../types/inference/InputTypeStrategiesTest.java | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java index 0847036504078..e707606ff6f02 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java @@ -86,13 +86,16 @@ public Optional inferArgumentType( if (Objects.equals(expectedNullability, Boolean.FALSE) && actualType.isNullable()) { return callContext.fail( throwOnFailure, - "Unsupported argument type. Expected nullable type of family '%s' but actual type was '%s'.", + "Unsupported argument type. Expected NOT NULL type of family '%s' but actual type was '%s'.", expectedFamily, actualType); } // type is part of the family if (actualType.getTypeRoot().getFamilies().contains(expectedFamily)) { + if (Objects.equals(expectedNullability, Boolean.TRUE) && !actualType.isNullable()) { + return Optional.of(actualDataType.nullable()); + } return Optional.of(actualDataType); } diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java index e6973c3e0d0a7..5ca7f45f80749 100644 --- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java @@ -549,23 +549,33 @@ ANY, explicit(DataTypes.INT()) sequence( logical(LogicalTypeFamily.CHARACTER_STRING, true), logical(LogicalTypeFamily.EXACT_NUMERIC), + logical(LogicalTypeFamily.EXACT_NUMERIC, true), logical(LogicalTypeFamily.APPROXIMATE_NUMERIC), logical(LogicalTypeFamily.APPROXIMATE_NUMERIC), logical(LogicalTypeFamily.APPROXIMATE_NUMERIC, false))) .calledWithArgumentTypes( DataTypes.NULL(), DataTypes.TINYINT(), + DataTypes.SMALLINT().notNull(), DataTypes.INT(), DataTypes.BIGINT().notNull(), DataTypes.DECIMAL(10, 2).notNull()) .expectSignature( - "f(, , , , )") + "f(, , , , , )") .expectArgumentTypes( DataTypes.VARCHAR(1), DataTypes.TINYINT(), + DataTypes.SMALLINT(), DataTypes.DOUBLE(), // widening with preserved nullability DataTypes.DOUBLE().notNull(), // widening with preserved nullability DataTypes.DOUBLE().notNull()), + TestSpec.forStrategy( + "Logical type family with invalid nullability", + sequence(logical(LogicalTypeFamily.EXACT_NUMERIC, false))) + .calledWithArgumentTypes(DataTypes.INT()) + .expectSignature("f()") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL type of family 'EXACT_NUMERIC' but actual type was 'INT'."), TestSpec.forStrategy( "Logical type family with invalid type", sequence(logical(LogicalTypeFamily.EXACT_NUMERIC))) From 2775ae12eb9800d31fc1b2e054362dcdf9e0b36e Mon Sep 17 00:00:00 2001 From: dylanhz Date: Mon, 22 Dec 2025 15:03:52 +0800 Subject: [PATCH 2/2] [FLINK-38826][table] Add new ArgumentStrategy to support array with specified element type --- .../functions/BuiltInFunctionDefinitions.java | 6 +- .../types/inference/InputTypeStrategies.java | 43 ++ .../ArrayOfFamilyArgumentTypeStrategy.java | 178 +++++ .../ArrayOfRootArgumentTypeStrategy.java | 155 ++++ .../ArrayOfStringArgumentTypeStrategy.java | 73 -- .../FamilyArgumentTypeStrategy.java | 21 +- .../inference/InputTypeStrategiesTest.java | 684 +++++++++--------- .../functions/CollectionFunctionsITCase.java | 56 +- 8 files changed, 774 insertions(+), 442 deletions(-) create mode 100644 flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfFamilyArgumentTypeStrategy.java create mode 100644 flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfRootArgumentTypeStrategy.java delete mode 100644 flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfStringArgumentTypeStrategy.java diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java index cea16b08a8e66..d05d367f60ae5 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/functions/BuiltInFunctionDefinitions.java @@ -36,7 +36,6 @@ import org.apache.flink.table.types.inference.StaticArgument; import org.apache.flink.table.types.inference.StaticArgumentTrait; import org.apache.flink.table.types.inference.TypeStrategies; -import org.apache.flink.table.types.inference.strategies.ArrayOfStringArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.SpecificInputTypeStrategies; import org.apache.flink.table.types.inference.strategies.SpecificTypeStrategies; import org.apache.flink.table.types.logical.LocalZonedTimestampType; @@ -82,6 +81,7 @@ import static org.apache.flink.table.types.inference.InputTypeStrategies.OUTPUT_IF_NULL; import static org.apache.flink.table.types.inference.InputTypeStrategies.TYPE_LITERAL; import static org.apache.flink.table.types.inference.InputTypeStrategies.and; +import static org.apache.flink.table.types.inference.InputTypeStrategies.arrayOf; import static org.apache.flink.table.types.inference.InputTypeStrategies.commonArrayType; import static org.apache.flink.table.types.inference.InputTypeStrategies.commonMapType; import static org.apache.flink.table.types.inference.InputTypeStrategies.commonMultipleArrayType; @@ -410,10 +410,10 @@ ANY, and(logical(LogicalTypeRoot.BOOLEAN), LITERAL) .inputTypeStrategy( or( sequence( - new ArrayOfStringArgumentTypeStrategy(), + arrayOf(LogicalTypeFamily.CHARACTER_STRING), logical(LogicalTypeFamily.CHARACTER_STRING)), sequence( - new ArrayOfStringArgumentTypeStrategy(), + arrayOf(LogicalTypeFamily.CHARACTER_STRING), logical(LogicalTypeFamily.CHARACTER_STRING), logical(LogicalTypeFamily.CHARACTER_STRING)))) .outputTypeStrategy(nullableIfArgs(explicit(DataTypes.STRING().nullable()))) diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java index ffc0ccbe476c3..42a01ad5bb9cc 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/InputTypeStrategies.java @@ -23,6 +23,8 @@ import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.inference.strategies.AndArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.AnyArgumentTypeStrategy; +import org.apache.flink.table.types.inference.strategies.ArrayOfFamilyArgumentTypeStrategy; +import org.apache.flink.table.types.inference.strategies.ArrayOfRootArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.CommonArgumentTypeStrategy; import org.apache.flink.table.types.inference.strategies.CommonArrayInputTypeStrategy; import org.apache.flink.table.types.inference.strategies.CommonInputTypeStrategy; @@ -289,6 +291,47 @@ public static FamilyArgumentTypeStrategy logical( return new FamilyArgumentTypeStrategy(expectedFamily, expectedNullability); } + /** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeRoot}. Implicit casts for element will be inserted if possible. + */ + public static ArrayOfRootArgumentTypeStrategy arrayOf(LogicalTypeRoot expectedElementRoot) { + return new ArrayOfRootArgumentTypeStrategy(expectedElementRoot, null, null); + } + + /** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeRoot} and nullability. Implicit casts for element will be inserted if possible. + */ + public static ArrayOfRootArgumentTypeStrategy arrayOf( + LogicalTypeRoot expectedElementRoot, + Boolean expectedArrayNullability, + Boolean expectedElementNullability) { + return new ArrayOfRootArgumentTypeStrategy( + expectedElementRoot, expectedArrayNullability, expectedElementNullability); + } + + /** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeFamily}. Implicit casts for element will be inserted if possible. + */ + public static ArrayOfFamilyArgumentTypeStrategy arrayOf( + LogicalTypeFamily expectedElementFamily) { + return new ArrayOfFamilyArgumentTypeStrategy(expectedElementFamily, null, null); + } + + /** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeFamily} and nullability. Implicit casts for element will be inserted if possible. + */ + public static ArrayOfFamilyArgumentTypeStrategy arrayOf( + LogicalTypeFamily expectedElementFamily, + Boolean expectedArrayNullability, + Boolean expectedElementNullability) { + return new ArrayOfFamilyArgumentTypeStrategy( + expectedElementFamily, expectedArrayNullability, expectedElementNullability); + } + /** Strategy for an argument that must fulfill a given constraint. */ public static ConstraintArgumentTypeStrategy constraint( String constraintMessage, Predicate> evaluator) { diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfFamilyArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfFamilyArgumentTypeStrategy.java new file mode 100644 index 0000000000000..52c584cb35ddd --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfFamilyArgumentTypeStrategy.java @@ -0,0 +1,178 @@ +/* + * 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. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.api.ValidationException; +import org.apache.flink.table.functions.FunctionDefinition; +import org.apache.flink.table.types.CollectionDataType; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.inference.ArgumentTypeStrategy; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.Signature.Argument; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeFamily; +import org.apache.flink.table.types.logical.LogicalTypeRoot; + +import javax.annotation.Nullable; + +import java.util.Objects; +import java.util.Optional; + +import static org.apache.flink.table.types.inference.strategies.FamilyArgumentTypeStrategy.FAMILY_TO_ROOT; +import static org.apache.flink.table.types.inference.strategies.StrategyUtils.findDataType; + +/** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeFamily} and nullability. + * + *

Implicit casts for element will be inserted if possible. + */ +@Internal +public class ArrayOfFamilyArgumentTypeStrategy implements ArgumentTypeStrategy { + + private final LogicalTypeFamily expectedElementFamily; + private final @Nullable Boolean expectedArrayNullability; + private final @Nullable Boolean expectedElementNullability; + + public ArrayOfFamilyArgumentTypeStrategy( + LogicalTypeFamily expectedElementFamily, + @Nullable Boolean expectedArrayNullability, + @Nullable Boolean expectedElementNullability) { + this.expectedElementFamily = expectedElementFamily; + this.expectedArrayNullability = expectedArrayNullability; + this.expectedElementNullability = expectedElementNullability; + } + + @Override + public Optional inferArgumentType( + CallContext callContext, int argumentPos, boolean throwOnFailure) { + DataType actualDataType = callContext.getArgumentDataTypes().get(argumentPos); + LogicalType actualLogicalType = actualDataType.getLogicalType(); + + if (!actualLogicalType.is(LogicalTypeRoot.ARRAY) + || Boolean.FALSE.equals(expectedArrayNullability) + && actualLogicalType.isNullable()) { + return callContext.fail( + throwOnFailure, + "Unsupported argument type. Expected %stype of root 'ARRAY' but actual type was '%s'.", + Boolean.FALSE.equals(expectedArrayNullability) ? "NOT NULL " : "", + actualLogicalType); + } + + return inferElementType( + callContext, + throwOnFailure, + ((CollectionDataType) actualDataType).getElementDataType()) + .map(DataTypes::ARRAY) + .map( + dataType -> + Boolean.FALSE.equals(expectedArrayNullability) + || expectedArrayNullability == null + && !actualLogicalType.isNullable() + ? dataType.notNull() + : dataType); + } + + private Optional inferElementType( + CallContext callContext, boolean throwOnFailure, DataType elementDataType) { + LogicalType elementLogicalType = elementDataType.getLogicalType(); + + if (Boolean.FALSE.equals(expectedElementNullability) && elementLogicalType.isNullable()) { + return callContext.fail( + throwOnFailure, + "Unsupported argument type. Expected NOT NULL element type of family '%s' but actual type was '%s'.", + expectedElementFamily, + elementLogicalType); + } + + // type is part of the family + if (elementLogicalType.getTypeRoot().getFamilies().contains(expectedElementFamily)) { + if (Boolean.TRUE.equals(expectedElementNullability) + && !elementLogicalType.isNullable()) { + return Optional.of(elementDataType.nullable()); + } + return Optional.of(elementDataType); + } + + Optional inferredElementDataType = Optional.empty(); + // use the smallest type in the family to find a common type + LogicalTypeRoot expectedElementRoot = FAMILY_TO_ROOT.get(expectedElementFamily); + if (expectedElementRoot != null) { + try { + inferredElementDataType = + findDataType( + callContext, + throwOnFailure, + elementDataType, + expectedElementRoot, + expectedElementNullability); + } catch (ValidationException t) { + // wrap inner exception to provide more context about element + } + } + + return inferredElementDataType.or( + () -> + callContext.fail( + throwOnFailure, + "Unsupported argument type. Expected %selement type of family '%s' but actual type was '%s'.", + Boolean.FALSE.equals(expectedElementNullability) ? "NOT NULL " : "", + expectedElementFamily, + elementLogicalType)); + } + + @Override + public Argument getExpectedArgument(FunctionDefinition def, int pos) { + String elementType = expectedElementFamily.toString(); + if (Boolean.TRUE.equals(expectedElementNullability)) { + elementType += " NULL"; + } else if (Boolean.FALSE.equals(expectedElementNullability)) { + elementType += " NOT NULL"; + } + String type = "ARRAY<<" + elementType + ">>"; + if (Boolean.TRUE.equals(expectedArrayNullability)) { + type += " NULL"; + } else if (Boolean.FALSE.equals(expectedArrayNullability)) { + type += " NOT NULL"; + } + return Argument.of(type); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ArrayOfFamilyArgumentTypeStrategy strategy = (ArrayOfFamilyArgumentTypeStrategy) o; + return expectedElementFamily == strategy.expectedElementFamily + && Objects.equals(expectedArrayNullability, strategy.expectedArrayNullability) + && Objects.equals(expectedElementNullability, strategy.expectedElementNullability); + } + + @Override + public int hashCode() { + return Objects.hash( + expectedElementFamily, expectedArrayNullability, expectedElementNullability); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfRootArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfRootArgumentTypeStrategy.java new file mode 100644 index 0000000000000..26ff5e15cbd96 --- /dev/null +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfRootArgumentTypeStrategy.java @@ -0,0 +1,155 @@ +/* + * 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. + */ + +package org.apache.flink.table.types.inference.strategies; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.api.DataTypes; +import org.apache.flink.table.api.ValidationException; +import org.apache.flink.table.functions.FunctionDefinition; +import org.apache.flink.table.types.CollectionDataType; +import org.apache.flink.table.types.DataType; +import org.apache.flink.table.types.inference.ArgumentTypeStrategy; +import org.apache.flink.table.types.inference.CallContext; +import org.apache.flink.table.types.inference.Signature.Argument; +import org.apache.flink.table.types.logical.LogicalType; +import org.apache.flink.table.types.logical.LogicalTypeRoot; + +import javax.annotation.Nullable; + +import java.util.Objects; +import java.util.Optional; + +import static org.apache.flink.table.types.inference.strategies.StrategyUtils.findDataType; + +/** + * Strategy for an argument that corresponds to an {@code ARRAY} with specified element {@link + * LogicalTypeRoot} and nullability. + * + *

Implicit casts for element will be inserted if possible. + */ +@Internal +public class ArrayOfRootArgumentTypeStrategy implements ArgumentTypeStrategy { + + private final LogicalTypeRoot expectedElementRoot; + private final @Nullable Boolean expectedArrayNullability; + private final @Nullable Boolean expectedElementNullability; + + public ArrayOfRootArgumentTypeStrategy( + LogicalTypeRoot expectedElementRoot, + @Nullable Boolean expectedArrayNullability, + @Nullable Boolean expectedElementNullability) { + this.expectedElementRoot = expectedElementRoot; + this.expectedArrayNullability = expectedArrayNullability; + this.expectedElementNullability = expectedElementNullability; + } + + @Override + public Optional inferArgumentType( + CallContext callContext, int argumentPos, boolean throwOnFailure) { + DataType actualDataType = callContext.getArgumentDataTypes().get(argumentPos); + LogicalType actualLogicalType = actualDataType.getLogicalType(); + + if (!actualLogicalType.is(LogicalTypeRoot.ARRAY) + || Boolean.FALSE.equals(expectedArrayNullability) + && actualLogicalType.isNullable()) { + return callContext.fail( + throwOnFailure, + "Unsupported argument type. Expected %stype of root 'ARRAY' but actual type was '%s'.", + Boolean.FALSE.equals(expectedArrayNullability) ? "NOT NULL " : "", + actualLogicalType); + } + + return inferElementType( + callContext, + throwOnFailure, + ((CollectionDataType) actualDataType).getElementDataType()) + .map(DataTypes::ARRAY) + .map( + dataType -> + Boolean.FALSE.equals(expectedArrayNullability) + || expectedArrayNullability == null + && !actualLogicalType.isNullable() + ? dataType.notNull() + : dataType); + } + + private Optional inferElementType( + CallContext callContext, boolean throwOnFailure, DataType elementDataType) { + LogicalType elementLogicalType = elementDataType.getLogicalType(); + + Optional inferredElementDataType = Optional.empty(); + try { + inferredElementDataType = + findDataType( + callContext, + throwOnFailure, + elementDataType, + expectedElementRoot, + expectedElementNullability); + } catch (ValidationException t) { + // wrap inner exception to provide more context about element + } + + return inferredElementDataType.or( + () -> + callContext.fail( + throwOnFailure, + "Unsupported argument type. Expected %selement type of root '%s' but actual type was '%s'.", + Boolean.FALSE.equals(expectedElementNullability) ? "NOT NULL " : "", + expectedElementRoot, + elementLogicalType)); + } + + @Override + public Argument getExpectedArgument(FunctionDefinition def, int pos) { + String elementType = expectedElementRoot.toString(); + if (Boolean.TRUE.equals(expectedElementNullability)) { + elementType += " NULL"; + } else if (Boolean.FALSE.equals(expectedElementNullability)) { + elementType += " NOT NULL"; + } + String type = "ARRAY<" + elementType + ">"; + if (Boolean.TRUE.equals(expectedArrayNullability)) { + type += " NULL"; + } else if (Boolean.FALSE.equals(expectedArrayNullability)) { + type += " NOT NULL"; + } + return Argument.of(type); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ArrayOfRootArgumentTypeStrategy strategy = (ArrayOfRootArgumentTypeStrategy) o; + return expectedElementRoot == strategy.expectedElementRoot + && Objects.equals(expectedArrayNullability, strategy.expectedArrayNullability) + && Objects.equals(expectedElementNullability, strategy.expectedElementNullability); + } + + @Override + public int hashCode() { + return Objects.hash( + expectedElementRoot, expectedArrayNullability, expectedElementNullability); + } +} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfStringArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfStringArgumentTypeStrategy.java deleted file mode 100644 index cffb251730318..0000000000000 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/ArrayOfStringArgumentTypeStrategy.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - */ - -package org.apache.flink.table.types.inference.strategies; - -import org.apache.flink.annotation.Internal; -import org.apache.flink.table.functions.FunctionDefinition; -import org.apache.flink.table.types.CollectionDataType; -import org.apache.flink.table.types.DataType; -import org.apache.flink.table.types.inference.ArgumentTypeStrategy; -import org.apache.flink.table.types.inference.CallContext; -import org.apache.flink.table.types.inference.Signature.Argument; -import org.apache.flink.table.types.logical.LogicalTypeRoot; - -import java.util.List; -import java.util.Optional; - -import static java.lang.Boolean.TRUE; - -/** Strategy for an argument that must be an array of strings. */ -@Internal -public final class ArrayOfStringArgumentTypeStrategy implements ArgumentTypeStrategy { - @Override - public Optional inferArgumentType( - CallContext callContext, int argumentPos, boolean throwOnFailure) { - - final List actualDataTypes = callContext.getArgumentDataTypes(); - DataType actualType = actualDataTypes.get(argumentPos); - if (!actualType.getLogicalType().getTypeRoot().equals(LogicalTypeRoot.ARRAY)) { - return callContext.fail( - throwOnFailure, - "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )", - actualDataTypes.toArray()); - } - DataType elementDataType = ((CollectionDataType) actualType).getElementDataType(); - Optional closedDataType = - StrategyUtils.findDataType( - callContext, - throwOnFailure, - elementDataType, - LogicalTypeRoot.VARCHAR, - TRUE); - if (closedDataType.isPresent()) { - return Optional.of(actualType); - } - return callContext.fail( - throwOnFailure, - "The input argument should be ARRAY", - actualDataTypes.toArray()); - } - - @Override - public Argument getExpectedArgument(FunctionDefinition functionDefinition, int argumentPos) { - return Argument.of("ARRAY"); - } -} diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java index e707606ff6f02..cfc3a56dca516 100644 --- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java +++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/inference/strategies/FamilyArgumentTypeStrategy.java @@ -50,20 +50,21 @@ public final class FamilyArgumentTypeStrategy implements ArgumentTypeStrategy { private final @Nullable Boolean expectedNullability; - private static final Map familyToRoot = new HashMap<>(); + public static final Map FAMILY_TO_ROOT = new HashMap<>(); static { // "fallback" root for a NULL literals, // they receive the smallest precision possible for having little impact when finding a // common type. - familyToRoot.put(LogicalTypeFamily.NUMERIC, LogicalTypeRoot.TINYINT); - familyToRoot.put(LogicalTypeFamily.INTEGER_NUMERIC, LogicalTypeRoot.TINYINT); - familyToRoot.put(LogicalTypeFamily.EXACT_NUMERIC, LogicalTypeRoot.TINYINT); - familyToRoot.put(LogicalTypeFamily.CHARACTER_STRING, LogicalTypeRoot.VARCHAR); - familyToRoot.put(LogicalTypeFamily.BINARY_STRING, LogicalTypeRoot.VARBINARY); - familyToRoot.put(LogicalTypeFamily.APPROXIMATE_NUMERIC, LogicalTypeRoot.DOUBLE); - familyToRoot.put(LogicalTypeFamily.TIMESTAMP, LogicalTypeRoot.TIMESTAMP_WITHOUT_TIME_ZONE); - familyToRoot.put(LogicalTypeFamily.TIME, LogicalTypeRoot.TIME_WITHOUT_TIME_ZONE); + FAMILY_TO_ROOT.put(LogicalTypeFamily.NUMERIC, LogicalTypeRoot.TINYINT); + FAMILY_TO_ROOT.put(LogicalTypeFamily.INTEGER_NUMERIC, LogicalTypeRoot.TINYINT); + FAMILY_TO_ROOT.put(LogicalTypeFamily.EXACT_NUMERIC, LogicalTypeRoot.TINYINT); + FAMILY_TO_ROOT.put(LogicalTypeFamily.CHARACTER_STRING, LogicalTypeRoot.VARCHAR); + FAMILY_TO_ROOT.put(LogicalTypeFamily.BINARY_STRING, LogicalTypeRoot.VARBINARY); + FAMILY_TO_ROOT.put(LogicalTypeFamily.APPROXIMATE_NUMERIC, LogicalTypeRoot.DOUBLE); + FAMILY_TO_ROOT.put( + LogicalTypeFamily.TIMESTAMP, LogicalTypeRoot.TIMESTAMP_WITHOUT_TIME_ZONE); + FAMILY_TO_ROOT.put(LogicalTypeFamily.TIME, LogicalTypeRoot.TIME_WITHOUT_TIME_ZONE); } public FamilyArgumentTypeStrategy( @@ -100,7 +101,7 @@ public Optional inferArgumentType( } // find a type for the family - final LogicalTypeRoot expectedRoot = familyToRoot.get(expectedFamily); + final LogicalTypeRoot expectedRoot = FAMILY_TO_ROOT.get(expectedFamily); final Optional inferredDataType; if (expectedRoot == null) { inferredDataType = Optional.empty(); diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java index 5ca7f45f80749..075040435068c 100644 --- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java +++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/inference/InputTypeStrategiesTest.java @@ -30,12 +30,29 @@ import java.math.BigDecimal; import java.util.stream.Stream; +import static org.apache.flink.table.api.DataTypes.ARRAY; +import static org.apache.flink.table.api.DataTypes.BIGINT; +import static org.apache.flink.table.api.DataTypes.BOOLEAN; +import static org.apache.flink.table.api.DataTypes.BYTES; +import static org.apache.flink.table.api.DataTypes.DATE; +import static org.apache.flink.table.api.DataTypes.DECIMAL; +import static org.apache.flink.table.api.DataTypes.DOUBLE; +import static org.apache.flink.table.api.DataTypes.FIELD; +import static org.apache.flink.table.api.DataTypes.FLOAT; +import static org.apache.flink.table.api.DataTypes.INT; +import static org.apache.flink.table.api.DataTypes.NULL; +import static org.apache.flink.table.api.DataTypes.ROW; +import static org.apache.flink.table.api.DataTypes.SMALLINT; +import static org.apache.flink.table.api.DataTypes.STRING; +import static org.apache.flink.table.api.DataTypes.TINYINT; +import static org.apache.flink.table.api.DataTypes.VARCHAR; import static org.apache.flink.table.types.inference.InputTypeStrategies.ANY; import static org.apache.flink.table.types.inference.InputTypeStrategies.LITERAL; import static org.apache.flink.table.types.inference.InputTypeStrategies.LITERAL_OR_NULL; import static org.apache.flink.table.types.inference.InputTypeStrategies.OUTPUT_IF_NULL; import static org.apache.flink.table.types.inference.InputTypeStrategies.WILDCARD; import static org.apache.flink.table.types.inference.InputTypeStrategies.and; +import static org.apache.flink.table.types.inference.InputTypeStrategies.arrayOf; import static org.apache.flink.table.types.inference.InputTypeStrategies.constraint; import static org.apache.flink.table.types.inference.InputTypeStrategies.explicit; import static org.apache.flink.table.types.inference.InputTypeStrategies.explicitSequence; @@ -52,12 +69,16 @@ class InputTypeStrategiesTest extends InputTypeStrategiesTestBase { @Override protected Stream testData() { + return Stream.of(defaultTestCases(), arrayOfArgumentTestCases()).flatMap(s -> s); + } + + private Stream defaultTestCases() { return Stream.of( // wildcard with 2 arguments TestSpec.forStrategy(WILDCARD) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.INT()) + .calledWithArgumentTypes(INT(), INT()) .expectSignature("f(*)") - .expectArgumentTypes(DataTypes.INT(), DataTypes.INT()), + .expectArgumentTypes(INT(), INT()), // wildcard with 0 arguments TestSpec.forStrategy(WILDCARD) @@ -66,188 +87,162 @@ protected Stream testData() { .expectArgumentTypes(), // explicit sequence - TestSpec.forStrategy( - explicitSequence( - DataTypes.INT().bridgedTo(int.class), DataTypes.BOOLEAN())) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.BOOLEAN()) + TestSpec.forStrategy(explicitSequence(INT().bridgedTo(int.class), BOOLEAN())) + .calledWithArgumentTypes(INT(), BOOLEAN()) .expectSignature("f(INT, BOOLEAN)") - .expectArgumentTypes( - DataTypes.INT().bridgedTo(int.class), DataTypes.BOOLEAN()), + .expectArgumentTypes(INT().bridgedTo(int.class), BOOLEAN()), // explicit sequence with ROW ignoring field names - TestSpec.forStrategy( - explicitSequence( - DataTypes.ROW( - DataTypes.FIELD("expected", DataTypes.INT())))) - .calledWithArgumentTypes( - DataTypes.ROW(DataTypes.FIELD("actual", DataTypes.INT()))) + TestSpec.forStrategy(explicitSequence(ROW(FIELD("expected", INT())))) + .calledWithArgumentTypes(ROW(FIELD("actual", INT()))) .expectSignature("f(ROW<`expected` INT>)") - .expectArgumentTypes( - DataTypes.ROW(DataTypes.FIELD("expected", DataTypes.INT()))), + .expectArgumentTypes(ROW(FIELD("expected", INT()))), // invalid named sequence TestSpec.forStrategy( explicitSequence( - new String[] {"i", "s"}, - new DataType[] {DataTypes.INT(), DataTypes.STRING()})) - .calledWithArgumentTypes(DataTypes.INT()) + new String[] {"i", "s"}, new DataType[] {INT(), STRING()})) + .calledWithArgumentTypes(INT()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(i INT, s STRING)"), // incompatible nullability - TestSpec.forStrategy(explicitSequence(DataTypes.BIGINT().notNull())) - .calledWithArgumentTypes(DataTypes.BIGINT()) + TestSpec.forStrategy(explicitSequence(BIGINT().notNull())) + .calledWithArgumentTypes(BIGINT()) .expectErrorMessage( "Unsupported argument type. Expected type 'BIGINT NOT NULL' but actual type was 'BIGINT'."), // implicit cast - TestSpec.forStrategy(explicitSequence(DataTypes.BIGINT())) - .calledWithArgumentTypes(DataTypes.INT()) - .expectArgumentTypes(DataTypes.BIGINT()), + TestSpec.forStrategy(explicitSequence(BIGINT())) + .calledWithArgumentTypes(INT()) + .expectArgumentTypes(BIGINT()), // incompatible types - TestSpec.forStrategy(explicitSequence(DataTypes.BIGINT())) - .calledWithArgumentTypes(DataTypes.STRING()) + TestSpec.forStrategy(explicitSequence(BIGINT())) + .calledWithArgumentTypes(STRING()) .expectErrorMessage( "Unsupported argument type. Expected type 'BIGINT' but actual type was 'STRING'."), // incompatible number of arguments - TestSpec.forStrategy(explicitSequence(DataTypes.BIGINT(), DataTypes.BIGINT())) - .calledWithArgumentTypes(DataTypes.BIGINT()) + TestSpec.forStrategy(explicitSequence(BIGINT(), BIGINT())) + .calledWithArgumentTypes(BIGINT()) .expectErrorMessage( "Invalid number of arguments. At least 2 arguments expected but 1 passed."), // any type TestSpec.forStrategy(sequence(ANY)) - .calledWithArgumentTypes(DataTypes.BIGINT()) + .calledWithArgumentTypes(BIGINT()) .expectSignature("f()") - .expectArgumentTypes(DataTypes.BIGINT()), + .expectArgumentTypes(BIGINT()), // incompatible number of arguments TestSpec.forStrategy(sequence(ANY)) - .calledWithArgumentTypes(DataTypes.BIGINT(), DataTypes.BIGINT()) + .calledWithArgumentTypes(BIGINT(), BIGINT()) .expectErrorMessage( "Invalid number of arguments. At most 1 arguments expected but 2 passed."), TestSpec.forStrategy( "OR with bridging class", or( - explicitSequence(DataTypes.STRING()), - explicitSequence(DataTypes.INT().bridgedTo(int.class)), - explicitSequence(DataTypes.BOOLEAN()))) - .calledWithArgumentTypes(DataTypes.INT()) - .calledWithArgumentTypes(DataTypes.TINYINT()) + explicitSequence(STRING()), + explicitSequence(INT().bridgedTo(int.class)), + explicitSequence(BOOLEAN()))) + .calledWithArgumentTypes(INT()) + .calledWithArgumentTypes(TINYINT()) .expectSignature("f(STRING)\nf(INT)\nf(BOOLEAN)") - .expectArgumentTypes(DataTypes.INT().bridgedTo(int.class)), + .expectArgumentTypes(INT().bridgedTo(int.class)), TestSpec.forStrategy( "OR with implicit casting", or( - explicitSequence(DataTypes.TINYINT()), - explicitSequence(DataTypes.INT()), - explicitSequence(DataTypes.BIGINT()))) - .calledWithArgumentTypes(DataTypes.SMALLINT()) - .expectArgumentTypes(DataTypes.INT()), + explicitSequence(TINYINT()), + explicitSequence(INT()), + explicitSequence(BIGINT()))) + .calledWithArgumentTypes(SMALLINT()) + .expectArgumentTypes(INT()), TestSpec.forStrategy( "OR with implicit casting of null", or( - explicitSequence(DataTypes.STRING().notNull()), - explicitSequence(DataTypes.INT().notNull()), - explicitSequence(DataTypes.BIGINT()))) - .calledWithArgumentTypes(DataTypes.NULL()) - .expectArgumentTypes(DataTypes.BIGINT()), + explicitSequence(STRING().notNull()), + explicitSequence(INT().notNull()), + explicitSequence(BIGINT()))) + .calledWithArgumentTypes(NULL()) + .expectArgumentTypes(BIGINT()), TestSpec.forStrategy( "OR with implicit casting using first match", - or( - explicitSequence(DataTypes.VARCHAR(20)), - explicitSequence(DataTypes.VARCHAR(10)))) - .calledWithArgumentTypes(DataTypes.VARCHAR(1)) - .expectArgumentTypes(DataTypes.VARCHAR(20)), + or(explicitSequence(VARCHAR(20)), explicitSequence(VARCHAR(10)))) + .calledWithArgumentTypes(VARCHAR(1)) + .expectArgumentTypes(VARCHAR(20)), TestSpec.forStrategy( "OR with invalid implicit casting of null", or( - explicitSequence(DataTypes.STRING().notNull()), - explicitSequence(DataTypes.INT().notNull()), - explicitSequence(DataTypes.BIGINT().notNull()))) - .calledWithArgumentTypes(DataTypes.NULL()) + explicitSequence(STRING().notNull()), + explicitSequence(INT().notNull()), + explicitSequence(BIGINT().notNull()))) + .calledWithArgumentTypes(NULL()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\n" + "f(STRING NOT NULL)\nf(INT NOT NULL)\nf(BIGINT NOT NULL)"), TestSpec.forStrategy( "OR with invalid type", - or( - explicitSequence(DataTypes.INT()), - explicitSequence(DataTypes.STRING()))) - .calledWithArgumentTypes(DataTypes.BOOLEAN()) + or(explicitSequence(INT()), explicitSequence(STRING()))) + .calledWithArgumentTypes(BOOLEAN()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(INT)\nf(STRING)"), // invalid typed sequence - TestSpec.forStrategy(explicitSequence(DataTypes.INT(), DataTypes.BOOLEAN())) - .calledWithArgumentTypes(DataTypes.BOOLEAN(), DataTypes.INT()) + TestSpec.forStrategy(explicitSequence(INT(), BOOLEAN())) + .calledWithArgumentTypes(BOOLEAN(), INT()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(INT, BOOLEAN)"), // sequence with wildcard - TestSpec.forStrategy(sequence(ANY, explicit(DataTypes.INT()))) - .calledWithArgumentTypes(DataTypes.BOOLEAN(), DataTypes.INT()) - .calledWithArgumentTypes(DataTypes.BOOLEAN(), DataTypes.TINYINT()) - .expectArgumentTypes(DataTypes.BOOLEAN(), DataTypes.INT()), + TestSpec.forStrategy(sequence(ANY, explicit(INT()))) + .calledWithArgumentTypes(BOOLEAN(), INT()) + .calledWithArgumentTypes(BOOLEAN(), TINYINT()) + .expectArgumentTypes(BOOLEAN(), INT()), // invalid named sequence TestSpec.forStrategy( sequence( new String[] {"any", "int"}, - new ArgumentTypeStrategy[] { - ANY, explicit(DataTypes.INT()) - })) - .calledWithArgumentTypes(DataTypes.STRING(), DataTypes.BOOLEAN()) + new ArgumentTypeStrategy[] {ANY, explicit(INT())})) + .calledWithArgumentTypes(STRING(), BOOLEAN()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(any , int INT)"), // sequence with OR and implicit casting TestSpec.forStrategy( - sequence( - explicit(DataTypes.INT()), - or( - explicit(DataTypes.BOOLEAN()), - explicit(DataTypes.INT())))) + sequence(explicit(INT()), or(explicit(BOOLEAN()), explicit(INT())))) .expectSignature("f(INT, [BOOLEAN | INT])") - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.INT()) - .calledWithArgumentTypes(DataTypes.TINYINT(), DataTypes.TINYINT()) - .expectArgumentTypes(DataTypes.INT(), DataTypes.INT()), + .calledWithArgumentTypes(INT(), INT()) + .calledWithArgumentTypes(TINYINT(), TINYINT()) + .expectArgumentTypes(INT(), INT()), // sequence with OR TestSpec.forStrategy( sequence( - explicit(DataTypes.INT()), - or( - explicit(DataTypes.BOOLEAN()), - explicit(DataTypes.STRING())))) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.BIGINT()) + explicit(INT()), + or(explicit(BOOLEAN()), explicit(STRING())))) + .calledWithArgumentTypes(INT(), BIGINT()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(INT, [BOOLEAN | STRING])"), // sequence with literal TestSpec.forStrategy(sequence(LITERAL)) .calledWithLiteralAt(0) - .calledWithArgumentTypes(DataTypes.INT()) - .expectArgumentTypes(DataTypes.INT()), + .calledWithArgumentTypes(INT()) + .expectArgumentTypes(INT()), // sequence with literal - TestSpec.forStrategy( - sequence( - and(LITERAL, explicit(DataTypes.STRING())), - explicit(DataTypes.INT()))) + TestSpec.forStrategy(sequence(and(LITERAL, explicit(STRING())), explicit(INT()))) .calledWithLiteralAt(0) - .calledWithArgumentTypes(DataTypes.STRING(), DataTypes.INT()) + .calledWithArgumentTypes(STRING(), INT()) .expectSignature("f([ & STRING], INT)") - .expectArgumentTypes(DataTypes.STRING(), DataTypes.INT()), + .expectArgumentTypes(STRING(), INT()), // sequence with missing literal TestSpec.forStrategy( - sequence( - and(explicit(DataTypes.STRING()), LITERAL_OR_NULL), - explicit(DataTypes.INT()))) - .calledWithArgumentTypes(DataTypes.STRING(), DataTypes.INT()) + sequence(and(explicit(STRING()), LITERAL_OR_NULL), explicit(INT()))) + .calledWithArgumentTypes(STRING(), INT()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf([STRING & ], INT)"), @@ -256,61 +251,43 @@ ANY, explicit(DataTypes.INT()) varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - explicit(DataTypes.BOOLEAN()) + explicit(INT()), explicit(STRING()), explicit(BOOLEAN()) })) - .calledWithArgumentTypes( - DataTypes.INT(), - DataTypes.STRING(), - DataTypes.BOOLEAN(), - DataTypes.BOOLEAN(), - DataTypes.BOOLEAN()) - .expectArgumentTypes( - DataTypes.INT(), - DataTypes.STRING(), - DataTypes.BOOLEAN(), - DataTypes.BOOLEAN(), - DataTypes.BOOLEAN()), + .calledWithArgumentTypes(INT(), STRING(), BOOLEAN(), BOOLEAN(), BOOLEAN()) + .expectArgumentTypes(INT(), STRING(), BOOLEAN(), BOOLEAN(), BOOLEAN()), // vararg sequence with conversion class TestSpec.forStrategy( varyingSequence( new String[] {"var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.BOOLEAN().bridgedTo(boolean.class)) + explicit(BOOLEAN().bridgedTo(boolean.class)) })) - .calledWithArgumentTypes( - DataTypes.BOOLEAN(), DataTypes.BOOLEAN(), DataTypes.BOOLEAN()) + .calledWithArgumentTypes(BOOLEAN(), BOOLEAN(), BOOLEAN()) .expectSignature("f(var BOOLEAN...)") .expectArgumentTypes( - DataTypes.BOOLEAN().bridgedTo(boolean.class), - DataTypes.BOOLEAN().bridgedTo(boolean.class), - DataTypes.BOOLEAN().bridgedTo(boolean.class)), + BOOLEAN().bridgedTo(boolean.class), + BOOLEAN().bridgedTo(boolean.class), + BOOLEAN().bridgedTo(boolean.class)), // vararg sequence TestSpec.forStrategy( varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - explicit(DataTypes.BOOLEAN()) + explicit(INT()), explicit(STRING()), explicit(BOOLEAN()) })) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.STRING()) - .expectArgumentTypes(DataTypes.INT(), DataTypes.STRING()), + .calledWithArgumentTypes(INT(), STRING()) + .expectArgumentTypes(INT(), STRING()), // invalid vararg type TestSpec.forStrategy( varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - explicit(DataTypes.BOOLEAN()) + explicit(INT()), explicit(STRING()), explicit(BOOLEAN()) })) - .calledWithArgumentTypes( - DataTypes.INT(), DataTypes.STRING(), DataTypes.STRING()) + .calledWithArgumentTypes(INT(), STRING(), STRING()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(i INT, s STRING, var BOOLEAN...)"), @@ -319,12 +296,9 @@ ANY, explicit(DataTypes.INT()) varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - explicit(DataTypes.BOOLEAN()) + explicit(INT()), explicit(STRING()), explicit(BOOLEAN()) })) - .calledWithArgumentTypes( - DataTypes.INT(), DataTypes.INT(), DataTypes.BOOLEAN()) + .calledWithArgumentTypes(INT(), INT(), BOOLEAN()) .expectErrorMessage( "Unsupported argument type. Expected type 'STRING' but actual type was 'INT'."), @@ -333,116 +307,89 @@ ANY, explicit(DataTypes.INT()) varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - or( - explicit(DataTypes.BOOLEAN()), - explicit(DataTypes.INT())) + explicit(INT()), + explicit(STRING()), + or(explicit(BOOLEAN()), explicit(INT())) })) - .calledWithArgumentTypes( - DataTypes.INT(), - DataTypes.STRING(), - DataTypes.INT(), - DataTypes.BOOLEAN()) - .expectArgumentTypes( - DataTypes.INT(), - DataTypes.STRING(), - DataTypes.INT(), - DataTypes.BOOLEAN()), + .calledWithArgumentTypes(INT(), STRING(), INT(), BOOLEAN()) + .expectArgumentTypes(INT(), STRING(), INT(), BOOLEAN()), // invalid OR in vararg type TestSpec.forStrategy( varyingSequence( new String[] {"i", "s", "var"}, new ArgumentTypeStrategy[] { - explicit(DataTypes.INT()), - explicit(DataTypes.STRING()), - or( - explicit(DataTypes.BOOLEAN()), - explicit(DataTypes.INT())) + explicit(INT()), + explicit(STRING()), + or(explicit(BOOLEAN()), explicit(INT())) })) - .calledWithArgumentTypes( - DataTypes.INT(), - DataTypes.STRING(), - DataTypes.STRING(), - DataTypes.STRING()) + .calledWithArgumentTypes(INT(), STRING(), STRING(), STRING()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(i INT, s STRING, var [BOOLEAN | INT]...)"), // incomplete inference TestSpec.forStrategy(WILDCARD) - .calledWithArgumentTypes( - DataTypes.NULL(), DataTypes.STRING(), DataTypes.NULL()) + .calledWithArgumentTypes(NULL(), STRING(), NULL()) .expectSignature("f(*)") - .expectArgumentTypes( - DataTypes.NULL(), DataTypes.STRING(), DataTypes.NULL()), + .expectArgumentTypes(NULL(), STRING(), NULL()), // typed arguments help inferring a type TestSpec.forStrategy(WILDCARD) - .typedArguments( - DataTypes.INT().bridgedTo(int.class), - DataTypes.STRING(), - DataTypes.BOOLEAN()) - .calledWithArgumentTypes( - DataTypes.NULL(), DataTypes.STRING(), DataTypes.NULL()) - .expectArgumentTypes( - DataTypes.INT().bridgedTo(int.class), - DataTypes.STRING(), - DataTypes.BOOLEAN()), + .typedArguments(INT().bridgedTo(int.class), STRING(), BOOLEAN()) + .calledWithArgumentTypes(NULL(), STRING(), NULL()) + .expectArgumentTypes(INT().bridgedTo(int.class), STRING(), BOOLEAN()), // surrounding function helps inferring a type TestSpec.forStrategy(sequence(OUTPUT_IF_NULL, OUTPUT_IF_NULL, OUTPUT_IF_NULL)) - .surroundingStrategy(explicitSequence(DataTypes.BOOLEAN())) - .calledWithArgumentTypes( - DataTypes.NULL(), DataTypes.STRING(), DataTypes.NULL()) + .surroundingStrategy(explicitSequence(BOOLEAN())) + .calledWithArgumentTypes(NULL(), STRING(), NULL()) .expectSignature("f(, , )") - .expectArgumentTypes( - DataTypes.BOOLEAN(), DataTypes.STRING(), DataTypes.BOOLEAN()), + .expectArgumentTypes(BOOLEAN(), STRING(), BOOLEAN()), // surrounding function helps inferring a type - TestSpec.forStrategy(sequence(or(OUTPUT_IF_NULL, explicit(DataTypes.INT())))) - .surroundingStrategy(explicitSequence(DataTypes.BOOLEAN())) - .calledWithArgumentTypes(DataTypes.NULL()) + TestSpec.forStrategy(sequence(or(OUTPUT_IF_NULL, explicit(INT())))) + .surroundingStrategy(explicitSequence(BOOLEAN())) + .calledWithArgumentTypes(NULL()) .expectSignature("f([ | INT])") - .expectArgumentTypes(DataTypes.BOOLEAN()), + .expectArgumentTypes(BOOLEAN()), // surrounding info can not infer input type and does not help inferring a type - TestSpec.forStrategy(explicitSequence(DataTypes.BOOLEAN())) + TestSpec.forStrategy(explicitSequence(BOOLEAN())) .surroundingStrategy(WILDCARD) - .calledWithArgumentTypes(DataTypes.NULL()) + .calledWithArgumentTypes(NULL()) .expectSignature("f(BOOLEAN)") - .expectArgumentTypes(DataTypes.BOOLEAN()), + .expectArgumentTypes(BOOLEAN()), // surrounding function does not help inferring a type - TestSpec.forStrategy(sequence(or(OUTPUT_IF_NULL, explicit(DataTypes.INT())))) - .calledWithArgumentTypes(DataTypes.NULL()) + TestSpec.forStrategy(sequence(or(OUTPUT_IF_NULL, explicit(INT())))) + .calledWithArgumentTypes(NULL()) .expectSignature("f([ | INT])") - .expectArgumentTypes(DataTypes.INT()), + .expectArgumentTypes(INT()), // typed arguments only with casting TestSpec.forStrategy(WILDCARD) - .typedArguments(DataTypes.INT(), DataTypes.STRING()) - .calledWithArgumentTypes(DataTypes.TINYINT(), DataTypes.STRING()) + .typedArguments(INT(), STRING()) + .calledWithArgumentTypes(TINYINT(), STRING()) .expectSignature("f(arg0 => INT, arg1 => STRING)") - .expectArgumentTypes(DataTypes.INT(), DataTypes.STRING()), + .expectArgumentTypes(INT(), STRING()), // invalid typed arguments TestSpec.forStrategy(WILDCARD) - .typedArguments(DataTypes.INT(), DataTypes.STRING()) - .calledWithArgumentTypes(DataTypes.STRING(), DataTypes.STRING()) + .typedArguments(INT(), STRING()) + .calledWithArgumentTypes(STRING(), STRING()) .expectErrorMessage( "Invalid argument type at position 0. Data type INT expected but STRING passed."), // named arguments TestSpec.forStrategy(WILDCARD) .namedArguments("i", "s") - .typedArguments(DataTypes.INT(), DataTypes.STRING()) + .typedArguments(INT(), STRING()) .expectSignature("f(i => INT, s => STRING)"), TestSpec.forStrategy( "Wildcard with count verifies arguments number", InputTypeStrategies.wildcardWithCount( ConstantArgumentCount.from(2))) - .calledWithArgumentTypes(DataTypes.STRING()) + .calledWithArgumentTypes(STRING()) .expectErrorMessage( "Invalid number of arguments. At least 2 arguments expected but 1 passed."), TestSpec.forStrategy( @@ -450,15 +397,8 @@ ANY, explicit(DataTypes.INT()) SpecificInputTypeStrategies.ARRAY) .expectSignature("f(, ...)") .calledWithArgumentTypes( - DataTypes.INT().notNull(), - DataTypes.BIGINT().notNull(), - DataTypes.DOUBLE(), - DataTypes.DOUBLE().notNull()) - .expectArgumentTypes( - DataTypes.DOUBLE(), - DataTypes.DOUBLE(), - DataTypes.DOUBLE(), - DataTypes.DOUBLE()), + INT().notNull(), BIGINT().notNull(), DOUBLE(), DOUBLE().notNull()) + .expectArgumentTypes(DOUBLE(), DOUBLE(), DOUBLE(), DOUBLE()), TestSpec.forStrategy( "Array strategy fails for no arguments", SpecificInputTypeStrategies.ARRAY) @@ -468,20 +408,14 @@ ANY, explicit(DataTypes.INT()) TestSpec.forStrategy( "Array strategy fails for null arguments", SpecificInputTypeStrategies.ARRAY) - .calledWithArgumentTypes(DataTypes.NULL()) + .calledWithArgumentTypes(NULL()) .expectErrorMessage("Could not find a common type for arguments: [NULL]"), TestSpec.forStrategy( "Map strategy infers common types", SpecificInputTypeStrategies.MAP) .calledWithArgumentTypes( - DataTypes.INT().notNull(), - DataTypes.DOUBLE(), - DataTypes.BIGINT().notNull(), - DataTypes.FLOAT().notNull()) + INT().notNull(), DOUBLE(), BIGINT().notNull(), FLOAT().notNull()) .expectArgumentTypes( - DataTypes.BIGINT().notNull(), - DataTypes.DOUBLE(), - DataTypes.BIGINT().notNull(), - DataTypes.DOUBLE()), + BIGINT().notNull(), DOUBLE(), BIGINT().notNull(), DOUBLE()), TestSpec.forStrategy( "Map strategy fails for no arguments", SpecificInputTypeStrategies.MAP) @@ -491,19 +425,18 @@ ANY, explicit(DataTypes.INT()) TestSpec.forStrategy( "Map strategy fails for an odd number of arguments", SpecificInputTypeStrategies.MAP) - .calledWithArgumentTypes( - DataTypes.BIGINT(), DataTypes.BIGINT(), DataTypes.BIGINT()) + .calledWithArgumentTypes(BIGINT(), BIGINT(), BIGINT()) .expectErrorMessage("Invalid number of arguments. 3 arguments passed."), TestSpec.forStrategy("Cast strategy", SpecificInputTypeStrategies.CAST) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.BIGINT()) - .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithArgumentTypes(INT(), BIGINT()) + .calledWithLiteralAt(1, BIGINT()) .expectSignature("f(, )") - .expectArgumentTypes(DataTypes.INT(), DataTypes.BIGINT()), + .expectArgumentTypes(INT(), BIGINT()), TestSpec.forStrategy( "Cast strategy for invalid target type", SpecificInputTypeStrategies.CAST) - .calledWithArgumentTypes(DataTypes.BOOLEAN(), DataTypes.DATE()) - .calledWithLiteralAt(1, DataTypes.DATE()) + .calledWithArgumentTypes(BOOLEAN(), DATE()) + .calledWithLiteralAt(1, DATE()) .expectErrorMessage("Unsupported cast from 'BOOLEAN' to 'DATE'."), TestSpec.forStrategy( "Logical type roots instead of concrete data types", @@ -515,32 +448,32 @@ ANY, explicit(DataTypes.INT()) logical(LogicalTypeRoot.INTEGER, false), logical(LogicalTypeRoot.INTEGER))) .calledWithArgumentTypes( - DataTypes.NULL(), - DataTypes.INT(), - DataTypes.DOUBLE(), - DataTypes.BOOLEAN().notNull(), - DataTypes.INT().notNull(), - DataTypes.INT().notNull()) + NULL(), + INT(), + DOUBLE(), + BOOLEAN().notNull(), + INT().notNull(), + INT().notNull()) .expectSignature( "f(, , , , , )") .expectArgumentTypes( - DataTypes.VARCHAR(1), - DataTypes.DECIMAL(10, 0), - DataTypes.DECIMAL(30, 15), - DataTypes.BOOLEAN().notNull(), - DataTypes.INT().notNull(), - DataTypes.INT().notNull()), + VARCHAR(1), + DECIMAL(10, 0), + DECIMAL(30, 15), + BOOLEAN().notNull(), + INT().notNull(), + INT().notNull()), TestSpec.forStrategy( "Logical type roots with wrong implicit cast", sequence(logical(LogicalTypeRoot.VARCHAR))) - .calledWithArgumentTypes(DataTypes.INT()) + .calledWithArgumentTypes(INT()) .expectSignature("f()") .expectErrorMessage( "Unsupported argument type. Expected type root 'VARCHAR' but actual type was 'INT'."), TestSpec.forStrategy( "Logical type roots with wrong nullability", sequence(logical(LogicalTypeRoot.VARCHAR, false))) - .calledWithArgumentTypes(DataTypes.VARCHAR(5)) + .calledWithArgumentTypes(VARCHAR(5)) .expectSignature("f()") .expectErrorMessage( "Unsupported argument type. Expected NOT NULL type of root 'VARCHAR' but actual type was 'VARCHAR(5)'."), @@ -554,32 +487,32 @@ ANY, explicit(DataTypes.INT()) logical(LogicalTypeFamily.APPROXIMATE_NUMERIC), logical(LogicalTypeFamily.APPROXIMATE_NUMERIC, false))) .calledWithArgumentTypes( - DataTypes.NULL(), - DataTypes.TINYINT(), - DataTypes.SMALLINT().notNull(), - DataTypes.INT(), - DataTypes.BIGINT().notNull(), - DataTypes.DECIMAL(10, 2).notNull()) + NULL(), + TINYINT(), + SMALLINT().notNull(), + INT(), + BIGINT().notNull(), + DECIMAL(10, 2).notNull()) .expectSignature( "f(, , , , , )") .expectArgumentTypes( - DataTypes.VARCHAR(1), - DataTypes.TINYINT(), - DataTypes.SMALLINT(), - DataTypes.DOUBLE(), // widening with preserved nullability - DataTypes.DOUBLE().notNull(), // widening with preserved nullability - DataTypes.DOUBLE().notNull()), + VARCHAR(1), + TINYINT(), + SMALLINT(), + DOUBLE(), // widening with preserved nullability + DOUBLE().notNull(), // widening with preserved nullability + DOUBLE().notNull()), TestSpec.forStrategy( "Logical type family with invalid nullability", sequence(logical(LogicalTypeFamily.EXACT_NUMERIC, false))) - .calledWithArgumentTypes(DataTypes.INT()) + .calledWithArgumentTypes(INT()) .expectSignature("f()") .expectErrorMessage( "Unsupported argument type. Expected NOT NULL type of family 'EXACT_NUMERIC' but actual type was 'INT'."), TestSpec.forStrategy( "Logical type family with invalid type", sequence(logical(LogicalTypeFamily.EXACT_NUMERIC))) - .calledWithArgumentTypes(DataTypes.FLOAT()) + .calledWithArgumentTypes(FLOAT()) .expectSignature("f()") .expectErrorMessage( "Unsupported argument type. Expected type of family 'EXACT_NUMERIC' but actual type was 'FLOAT'."), @@ -587,38 +520,36 @@ ANY, explicit(DataTypes.INT()) "Constraint argument type strategy", sequence( and( - explicit(DataTypes.BOOLEAN()), + explicit(BOOLEAN()), constraint( "%s must be nullable.", args -> args.get(0) .getLogicalType() .isNullable())))) - .calledWithArgumentTypes(DataTypes.BOOLEAN()) + .calledWithArgumentTypes(BOOLEAN()) .expectSignature("f([BOOLEAN & ])") - .expectArgumentTypes(DataTypes.BOOLEAN()), + .expectArgumentTypes(BOOLEAN()), TestSpec.forStrategy( "Constraint argument type strategy invalid", sequence( and( - explicit(DataTypes.BOOLEAN().notNull()), + explicit(BOOLEAN().notNull()), constraint( "My constraint says %s must be nullable.", args -> args.get(0) .getLogicalType() .isNullable())))) - .calledWithArgumentTypes(DataTypes.BOOLEAN().notNull()) + .calledWithArgumentTypes(BOOLEAN().notNull()) .expectErrorMessage( "My constraint says BOOLEAN NOT NULL must be nullable."), TestSpec.forStrategy( "Composite type strategy with ROW", sequence(InputTypeStrategies.COMPOSITE)) - .calledWithArgumentTypes( - DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.BIGINT()))) + .calledWithArgumentTypes(ROW(FIELD("f0", BIGINT()))) .expectSignature("f()") - .expectArgumentTypes( - DataTypes.ROW(DataTypes.FIELD("f0", DataTypes.BIGINT()))), + .expectArgumentTypes(ROW(FIELD("f0", BIGINT()))), TestSpec.forStrategy( "Composite type strategy with STRUCTURED type", sequence(InputTypeStrategies.COMPOSITE)) @@ -627,11 +558,9 @@ ANY, explicit(DataTypes.INT()) .expectArgumentTypes(DataTypes.of(SimpleStructuredType.class).notNull()), TestSpec.forStrategy( "Same named arguments for overloaded method.", - or( - sequence(explicit(DataTypes.STRING())), - sequence(explicit(DataTypes.INT())))) + or(sequence(explicit(STRING())), sequence(explicit(INT())))) .namedArguments("sameName") - .calledWithArgumentTypes(DataTypes.BOOLEAN()) + .calledWithArgumentTypes(BOOLEAN()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\nf(STRING)\nf(INT)"), TestSpec.forStrategy( @@ -639,31 +568,27 @@ ANY, explicit(DataTypes.INT()) sequence( InputTypeStrategies.COMMON_ARG, InputTypeStrategies.COMMON_ARG)) - .calledWithArgumentTypes(DataTypes.INT(), DataTypes.BIGINT()) + .calledWithArgumentTypes(INT(), BIGINT()) .expectSignature("f(, )") - .expectArgumentTypes(DataTypes.BIGINT(), DataTypes.BIGINT()), + .expectArgumentTypes(BIGINT(), BIGINT()), TestSpec.forStrategy( "ArrayElement argument type strategy", sequence( logical(LogicalTypeRoot.ARRAY), SpecificInputTypeStrategies.ARRAY_ELEMENT_ARG)) - .calledWithArgumentTypes( - DataTypes.ARRAY(DataTypes.INT().notNull()).notNull(), - DataTypes.INT()) + .calledWithArgumentTypes(ARRAY(INT().notNull()).notNull(), INT()) .expectSignature("f(, )") - .expectArgumentTypes( - DataTypes.ARRAY(DataTypes.INT().notNull()).notNull(), - DataTypes.INT()), + .expectArgumentTypes(ARRAY(INT().notNull()).notNull(), INT()), TestSpec.forStrategy(sequence(SpecificInputTypeStrategies.ARRAY_FULLY_COMPARABLE)) .expectSignature("f(>)") - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.ROW())) + .calledWithArgumentTypes(ARRAY(ROW())) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\n" + "f(>)"), TestSpec.forStrategy( "Strategy fails if input argument type is not ARRAY", sequence(SpecificInputTypeStrategies.ARRAY_FULLY_COMPARABLE)) - .calledWithArgumentTypes(DataTypes.INT()) + .calledWithArgumentTypes(INT()) .expectErrorMessage( "Invalid input arguments. Expected signatures are:\n" + "f(>)"), @@ -678,7 +603,7 @@ ANY, explicit(DataTypes.INT()) "PROCTIME type strategy on non time indicator", SpecificInputTypeStrategies.windowTimeIndicator( TimestampKind.PROCTIME)) - .calledWithArgumentTypes(DataTypes.BIGINT()) + .calledWithArgumentTypes(BIGINT()) .expectErrorMessage("Reference to a rowtime or proctime window required."), TestSpec.forStrategy( "ROWTIME type strategy", @@ -704,157 +629,147 @@ ANY, explicit(DataTypes.INT()) "ROWTIME type strategy on long in batch mode", SpecificInputTypeStrategies.windowTimeIndicator( TimestampKind.ROWTIME)) - .calledWithArgumentTypes(DataTypes.BIGINT()) - .expectArgumentTypes(DataTypes.BIGINT()), + .calledWithArgumentTypes(BIGINT()) + .expectArgumentTypes(BIGINT()), TestSpec.forStrategy( "ROWTIME type strategy on non time attribute", SpecificInputTypeStrategies.windowTimeIndicator( TimestampKind.ROWTIME)) - .calledWithArgumentTypes(DataTypes.SMALLINT()) + .calledWithArgumentTypes(SMALLINT()) .expectErrorMessage("Reference to a rowtime or proctime window required."), TestSpec.forStrategy( "PROCTIME type strategy on non time attribute", SpecificInputTypeStrategies.windowTimeIndicator( TimestampKind.PROCTIME)) - .calledWithArgumentTypes(DataTypes.SMALLINT()) + .calledWithArgumentTypes(SMALLINT()) .expectErrorMessage("Reference to a rowtime or proctime window required."), TestSpec.forStrategy( "Reinterpret_cast strategy", SpecificInputTypeStrategies.REINTERPRET_CAST) - .calledWithArgumentTypes( - DataTypes.DATE(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) - .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithArgumentTypes(DATE(), BIGINT(), BOOLEAN().notNull()) + .calledWithLiteralAt(1, BIGINT()) .calledWithLiteralAt(2, true) .expectSignature("f(, , )") - .expectArgumentTypes( - DataTypes.DATE(), - DataTypes.BIGINT(), - DataTypes.BOOLEAN().notNull()), + .expectArgumentTypes(DATE(), BIGINT(), BOOLEAN().notNull()), TestSpec.forStrategy( "Reinterpret_cast strategy non literal overflow", SpecificInputTypeStrategies.REINTERPRET_CAST) - .calledWithArgumentTypes( - DataTypes.DATE(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) - .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithArgumentTypes(DATE(), BIGINT(), BOOLEAN().notNull()) + .calledWithLiteralAt(1, BIGINT()) .expectErrorMessage("Not null boolean literal expected for overflow."), TestSpec.forStrategy( "Reinterpret_cast strategy not supported cast", SpecificInputTypeStrategies.REINTERPRET_CAST) - .calledWithArgumentTypes( - DataTypes.INT(), DataTypes.BIGINT(), DataTypes.BOOLEAN().notNull()) - .calledWithLiteralAt(1, DataTypes.BIGINT()) + .calledWithArgumentTypes(INT(), BIGINT(), BOOLEAN().notNull()) + .calledWithLiteralAt(1, BIGINT()) .calledWithLiteralAt(2, true) .expectErrorMessage("Unsupported reinterpret cast from 'INT' to 'BIGINT'"), TestSpec.forStrategy("IndexArgumentTypeStrategy", sequence(INDEX)) - .calledWithArgumentTypes(DataTypes.TINYINT()) + .calledWithArgumentTypes(TINYINT()) .expectSignature("f()") - .expectArgumentTypes(DataTypes.TINYINT()), + .expectArgumentTypes(TINYINT()), TestSpec.forStrategy("IndexArgumentTypeStrategy", sequence(INDEX)) - .calledWithArgumentTypes(DataTypes.INT()) + .calledWithArgumentTypes(INT()) .calledWithLiteralAt(0) - .expectArgumentTypes(DataTypes.INT()), + .expectArgumentTypes(INT()), TestSpec.forStrategy("IndexArgumentTypeStrategy BIGINT support", sequence(INDEX)) - .calledWithArgumentTypes(DataTypes.BIGINT().notNull()) + .calledWithArgumentTypes(BIGINT().notNull()) .calledWithLiteralAt(0, Long.MAX_VALUE) - .expectArgumentTypes(DataTypes.BIGINT().notNull()), + .expectArgumentTypes(BIGINT().notNull()), TestSpec.forStrategy("IndexArgumentTypeStrategy index range", sequence(INDEX)) - .calledWithArgumentTypes(DataTypes.INT().notNull()) + .calledWithArgumentTypes(INT().notNull()) .calledWithLiteralAt(0, -1) .expectErrorMessage( "Index must be an integer starting from '0', but was '-1'."), TestSpec.forStrategy("IndexArgumentTypeStrategy index type", sequence(INDEX)) - .calledWithArgumentTypes(DataTypes.DECIMAL(10, 5)) + .calledWithArgumentTypes(DECIMAL(10, 5)) .expectErrorMessage("Index can only be an INTEGER NUMERIC type."), // Percentage ArgumentStrategy TestSpec.forStrategy("normal", sequence(percentage(true))) - .calledWithArgumentTypes(DataTypes.DOUBLE()) + .calledWithArgumentTypes(DOUBLE()) .expectSignature("f()") - .expectArgumentTypes(DataTypes.DOUBLE()), + .expectArgumentTypes(DOUBLE()), TestSpec.forStrategy("implicit cast", sequence(percentage(false))) - .calledWithArgumentTypes(DataTypes.DECIMAL(5, 2).notNull()) + .calledWithArgumentTypes(DECIMAL(5, 2).notNull()) .expectSignature("f()") - .expectArgumentTypes(DataTypes.DOUBLE().notNull()), + .expectArgumentTypes(DOUBLE().notNull()), TestSpec.forStrategy("literal", sequence(percentage(true))) - .calledWithArgumentTypes(DataTypes.DECIMAL(2, 2)) + .calledWithArgumentTypes(DECIMAL(2, 2)) .calledWithLiteralAt(0, BigDecimal.valueOf(45, 2)) - .expectArgumentTypes(DataTypes.DOUBLE()), + .expectArgumentTypes(DOUBLE()), TestSpec.forStrategy("literal", sequence(percentage(false))) - .calledWithArgumentTypes(DataTypes.INT().notNull()) + .calledWithArgumentTypes(INT().notNull()) .calledWithLiteralAt(0, 1) - .expectArgumentTypes(DataTypes.DOUBLE().notNull()), + .expectArgumentTypes(DOUBLE().notNull()), TestSpec.forStrategy("invalid type", sequence(percentage(true))) - .calledWithArgumentTypes(DataTypes.STRING()) + .calledWithArgumentTypes(STRING()) .expectErrorMessage("Percentage must be of NUMERIC type."), TestSpec.forStrategy("invalid nullability", sequence(percentage(false))) - .calledWithArgumentTypes(DataTypes.DOUBLE()) + .calledWithArgumentTypes(DOUBLE()) .expectErrorMessage("Percentage must be of NOT NULL type."), TestSpec.forStrategy("invalid literal value", sequence(percentage(false))) - .calledWithArgumentTypes(DataTypes.DECIMAL(2, 1).notNull()) + .calledWithArgumentTypes(DECIMAL(2, 1).notNull()) .calledWithLiteralAt(0, BigDecimal.valueOf(20, 1)) .expectErrorMessage( "Percentage must be between [0.0, 1.0], but was '2.0'."), TestSpec.forStrategy("invalid literal value", sequence(percentage(false))) - .calledWithArgumentTypes(DataTypes.DECIMAL(2, 1).notNull()) + .calledWithArgumentTypes(DECIMAL(2, 1).notNull()) .calledWithLiteralAt(0, BigDecimal.valueOf(-5, 1)) .expectErrorMessage( "Percentage must be between [0.0, 1.0], but was '-0.5'."), // Percentage Array ArgumentStrategy TestSpec.forStrategy("normal", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())) + .calledWithArgumentTypes(ARRAY(DOUBLE())) .expectSignature("f(ARRAY)") - .expectArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())), + .expectArgumentTypes(ARRAY(DOUBLE())), TestSpec.forStrategy("implicit cast", sequence(percentageArray(false))) - .calledWithArgumentTypes( - DataTypes.ARRAY(DataTypes.DECIMAL(5, 2).notNull()).notNull()) + .calledWithArgumentTypes(ARRAY(DECIMAL(5, 2).notNull()).notNull()) .expectSignature("f(ARRAY NOT NULL)") - .expectArgumentTypes( - DataTypes.ARRAY(DataTypes.DOUBLE().notNull()).notNull()), + .expectArgumentTypes(ARRAY(DOUBLE().notNull()).notNull()), TestSpec.forStrategy("literal", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())) + .calledWithArgumentTypes(ARRAY(DOUBLE())) .calledWithLiteralAt(0, new Double[] {0.45, 0.55}) - .expectArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())), + .expectArgumentTypes(ARRAY(DOUBLE())), TestSpec.forStrategy("literal", sequence(percentageArray(false))) - .calledWithArgumentTypes( - DataTypes.ARRAY(DataTypes.DECIMAL(2, 2).notNull()).notNull()) + .calledWithArgumentTypes(ARRAY(DECIMAL(2, 2).notNull()).notNull()) .calledWithLiteralAt( 0, new BigDecimal[] { BigDecimal.valueOf(45, 2), BigDecimal.valueOf(55, 2) }) - .expectArgumentTypes( - DataTypes.ARRAY(DataTypes.DOUBLE().notNull()).notNull()), + .expectArgumentTypes(ARRAY(DOUBLE().notNull()).notNull()), TestSpec.forStrategy("literal", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.INT())) + .calledWithArgumentTypes(ARRAY(INT())) .calledWithLiteralAt(0, new Integer[] {0, 1}) - .expectArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())), + .expectArgumentTypes(ARRAY(DOUBLE())), TestSpec.forStrategy("empty literal array", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())) + .calledWithArgumentTypes(ARRAY(DOUBLE())) .calledWithLiteralAt(0, new Double[0]) - .expectArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())), + .expectArgumentTypes(ARRAY(DOUBLE())), TestSpec.forStrategy("not array", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.DOUBLE()) + .calledWithArgumentTypes(DOUBLE()) .expectErrorMessage("Percentage must be an array."), TestSpec.forStrategy("invalid array nullability", sequence(percentageArray(false))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.STRING().notNull())) + .calledWithArgumentTypes(ARRAY(STRING().notNull())) .expectErrorMessage("Percentage must be a non-null array."), TestSpec.forStrategy("invalid element type", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.STRING())) + .calledWithArgumentTypes(ARRAY(STRING())) .expectErrorMessage( "Value in the percentage array must be of NUMERIC type."), TestSpec.forStrategy( "invalid element nullability", sequence(percentageArray(false))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE()).notNull()) + .calledWithArgumentTypes(ARRAY(DOUBLE()).notNull()) .expectErrorMessage( "Value in the percentage array must be of NOT NULL type."), TestSpec.forStrategy("invalid literal", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DOUBLE())) + .calledWithArgumentTypes(ARRAY(DOUBLE())) .calledWithLiteralAt(0, new Double[] {0.5, 1.5}) .expectErrorMessage( "Value in the percentage array must be between [0.0, 1.0], but was '1.5'."), TestSpec.forStrategy("invalid literal", sequence(percentageArray(true))) - .calledWithArgumentTypes(DataTypes.ARRAY(DataTypes.DECIMAL(3, 2))) + .calledWithArgumentTypes(ARRAY(DECIMAL(3, 2))) .calledWithLiteralAt( 0, new BigDecimal[] { @@ -864,6 +779,119 @@ ANY, explicit(DataTypes.INT()) "Value in the percentage array must be between [0.0, 1.0], but was '-0.1'.")); } + private Stream arrayOfArgumentTestCases() { + return Stream.of( + // ArrayOfRoot + TestSpec.forStrategy( + "Array of logical element type roots instead of concrete data types", + sequence( + arrayOf(LogicalTypeRoot.VARCHAR), + arrayOf(LogicalTypeRoot.INTEGER), + arrayOf(LogicalTypeRoot.DECIMAL, true, null), + arrayOf(LogicalTypeRoot.BOOLEAN, null, false), + arrayOf(LogicalTypeRoot.INTEGER, false, true))) + .calledWithArgumentTypes( + ARRAY(NULL()), + ARRAY(INT().notNull()), + ARRAY(INT()), + ARRAY(BOOLEAN().notNull()).notNull(), + ARRAY(INT().notNull()).notNull()) + .expectSignature( + "f(ARRAY, ARRAY, ARRAY NULL, ARRAY, ARRAY NOT NULL)") + .expectArgumentTypes( + ARRAY(VARCHAR(1)), + ARRAY(INT().notNull()), + ARRAY(DECIMAL(10, 0)), + ARRAY(BOOLEAN().notNull()).notNull(), + ARRAY(INT()).notNull()), + TestSpec.forStrategy("Not an array", sequence(arrayOf(LogicalTypeRoot.VARCHAR))) + .calledWithArgumentTypes(VARCHAR(5)) + .expectSignature("f(ARRAY)") + .expectErrorMessage( + "Unsupported argument type. Expected type of root 'ARRAY' but actual type was 'VARCHAR(5)'."), + TestSpec.forStrategy( + "Array with wrong nullability", + sequence(arrayOf(LogicalTypeRoot.VARCHAR, false, null))) + .calledWithArgumentTypes(ARRAY(VARCHAR(5))) + .expectSignature("f(ARRAY NOT NULL)") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL type of root 'ARRAY' but actual type was 'ARRAY'."), + TestSpec.forStrategy( + "Element with wrong nullability", + sequence(arrayOf(LogicalTypeRoot.VARCHAR, null, false))) + .calledWithArgumentTypes(ARRAY(VARCHAR(5))) + .expectSignature("f(ARRAY)") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL element type of root 'VARCHAR' but actual type was 'VARCHAR(5)'."), + TestSpec.forStrategy( + "Array of logical element type roots with wrong implicit cast", + sequence(arrayOf(LogicalTypeRoot.VARCHAR))) + .calledWithArgumentTypes(ARRAY(INT())) + .expectSignature("f(ARRAY)") + .expectErrorMessage( + "Unsupported argument type. Expected element type of root 'VARCHAR' but actual type was 'INT'."), + // ArrayOfFamily + TestSpec.forStrategy( + "Array of logical element type family instead of concrete data types", + sequence( + arrayOf(LogicalTypeFamily.CHARACTER_STRING), + arrayOf(LogicalTypeFamily.EXACT_NUMERIC), + arrayOf(LogicalTypeFamily.APPROXIMATE_NUMERIC, null, true), + arrayOf(LogicalTypeFamily.APPROXIMATE_NUMERIC, false, null), + arrayOf(LogicalTypeFamily.APPROXIMATE_NUMERIC, null, false), + arrayOf(LogicalTypeFamily.EXACT_NUMERIC, true, true))) + .calledWithArgumentTypes( + ARRAY(NULL()), + ARRAY(TINYINT().notNull()), + ARRAY(FLOAT()), + ARRAY(INT().notNull()).notNull(), + ARRAY(BIGINT().notNull()).notNull(), + ARRAY(DECIMAL(10, 2).notNull()).notNull()) + .expectSignature( + "f(ARRAY<>, ARRAY<>, ARRAY<>, ARRAY<> NOT NULL, ARRAY<>, ARRAY<> NULL)") + .expectArgumentTypes( + ARRAY(VARCHAR(1)), + ARRAY(TINYINT().notNull()), + ARRAY(FLOAT()), + ARRAY(DOUBLE().notNull()).notNull(), + ARRAY(DOUBLE().notNull()).notNull(), + ARRAY(DECIMAL(10, 2))), + TestSpec.forStrategy( + "Not an array", sequence(arrayOf(LogicalTypeFamily.BINARY_STRING))) + .calledWithArgumentTypes(BYTES()) + .expectSignature("f(ARRAY<>)") + .expectErrorMessage( + "Unsupported argument type. Expected type of root 'ARRAY' but actual type was 'BYTES'."), + TestSpec.forStrategy( + "Array with wrong nullability", + sequence(arrayOf(LogicalTypeFamily.CHARACTER_STRING, false, null))) + .calledWithArgumentTypes(ARRAY(STRING())) + .expectSignature("f(ARRAY<> NOT NULL)") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL type of root 'ARRAY' but actual type was 'ARRAY'."), + TestSpec.forStrategy( + "Element with wrong nullability", + sequence(arrayOf(LogicalTypeFamily.BINARY_STRING, null, false))) + .calledWithArgumentTypes(ARRAY(BYTES())) + .expectSignature("f(ARRAY<>)") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL element type of family 'BINARY_STRING' but actual type was 'BYTES'."), + TestSpec.forStrategy( + "Array of logical element type family with invalid type", + sequence(arrayOf(LogicalTypeFamily.EXACT_NUMERIC))) + .calledWithArgumentTypes(ARRAY(FLOAT())) + .expectSignature("f(ARRAY<>)") + .expectErrorMessage( + "Unsupported argument type. Expected element type of family 'EXACT_NUMERIC' but actual type was 'FLOAT'."), + TestSpec.forStrategy( + "Array of logical element type family without a smallest precision type", + sequence(arrayOf(LogicalTypeFamily.DATETIME, null, false))) + .calledWithArgumentTypes(ARRAY(BIGINT().notNull())) + .expectSignature("f(ARRAY<>)") + .expectErrorMessage( + "Unsupported argument type. Expected NOT NULL element type of family 'DATETIME' but actual type was 'BIGINT NOT NULL'.")); + } + private static DataType timeIndicatorType(TimestampKind timestampKind) { return TypeConversions.fromLogicalToDataType( new LocalZonedTimestampType(false, timestampKind, 3)); diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java index f67d422c994a6..09c6b352d418d 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CollectionFunctionsITCase.java @@ -1226,39 +1226,39 @@ private Stream arrayJoinTestCases() { "ARRAY_JOIN(f0)", "No match found for function signature ARRAY_JOIN().\n" + "Supported signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN()", "No match found for function signature ARRAY_JOIN().\n" + "Supported signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN(f5, '+')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f5"), "+"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN(f6, '+')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f6"), "+", "abc"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN(f7, '+', 'abc')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f7"), "+"), "Invalid function call:\n" @@ -1266,38 +1266,38 @@ private Stream arrayJoinTestCases() { .testSqlValidationError( "ARRAY_JOIN(f8, '+', 'abc')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f8"), "+"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN(f9, '+', 'abc')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f9"), "+"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testSqlValidationError( "ARRAY_JOIN(f10, '+', 'abc')", "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f10"), "+"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )") + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )") .testTableApiValidationError( call("ARRAY_JOIN", $("f0"), "+", "+", "+"), "Invalid input arguments. Expected signatures are:\n" - + "ARRAY_JOIN(ARRAY, )\n" - + "ARRAY_JOIN(ARRAY, , )")); + + "ARRAY_JOIN(ARRAY<>, )\n" + + "ARRAY_JOIN(ARRAY<>, , )")); } public static class CreateEmptyArray extends ScalarFunction {