blob: a9d1251e91ec351487330ac1e1d1ade76f275913 [file] [log] [blame]
/*
* 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;
import org.apache.flink.annotation.Internal;
import org.apache.flink.table.expressions.TableSymbol;
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.CommonArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.CommonInputTypeStrategy;
import org.apache.flink.table.types.inference.strategies.ComparableTypeStrategy;
import org.apache.flink.table.types.inference.strategies.CompositeArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.ConstraintArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.ExplicitArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.FamilyArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.LiteralArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.OrArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.OrInputTypeStrategy;
import org.apache.flink.table.types.inference.strategies.OutputArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.RepeatingSequenceInputTypeStrategy;
import org.apache.flink.table.types.inference.strategies.RootArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.SequenceInputTypeStrategy;
import org.apache.flink.table.types.inference.strategies.SubsequenceInputTypeStrategy.SubsequenceStrategyBuilder;
import org.apache.flink.table.types.inference.strategies.SymbolArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.TypeLiteralArgumentTypeStrategy;
import org.apache.flink.table.types.inference.strategies.VaryingSequenceInputTypeStrategy;
import org.apache.flink.table.types.inference.strategies.WildcardInputTypeStrategy;
import org.apache.flink.table.types.logical.LogicalTypeFamily;
import org.apache.flink.table.types.logical.LogicalTypeRoot;
import org.apache.flink.table.types.logical.StructuredType.StructuredComparison;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Strategies for inferring and validating input arguments in a function call.
*
* @see InputTypeStrategy
* @see ArgumentTypeStrategy
*/
@Internal
public final class InputTypeStrategies {
// --------------------------------------------------------------------------------------------
// Input type strategies
// --------------------------------------------------------------------------------------------
/** Strategy that does not perform any modification or validation of the input. */
public static final WildcardInputTypeStrategy WILDCARD = new WildcardInputTypeStrategy();
/** Strategy that does not expect any arguments. */
public static final InputTypeStrategy NO_ARGS = sequence();
/**
* Strategy for a function signature like {@code f(STRING, NUMERIC)} using a sequence of {@link
* ArgumentTypeStrategy}s.
*/
public static InputTypeStrategy sequence(ArgumentTypeStrategy... strategies) {
return new SequenceInputTypeStrategy(Arrays.asList(strategies), null);
}
/**
* Strategy for a named function signature like {@code f(s STRING, n NUMERIC)} using a sequence
* of {@link ArgumentTypeStrategy}s.
*/
public static InputTypeStrategy sequence(
String[] argumentNames, ArgumentTypeStrategy[] strategies) {
return new SequenceInputTypeStrategy(
Arrays.asList(strategies), Arrays.asList(argumentNames));
}
/**
* Strategy for a varying function signature like {@code f(INT, STRING, NUMERIC...)} using a
* sequence of {@link ArgumentTypeStrategy}s. The first n - 1 arguments must be constant. The
* n-th argument can occur 0, 1, or more times.
*/
public static InputTypeStrategy varyingSequence(ArgumentTypeStrategy... strategies) {
return new VaryingSequenceInputTypeStrategy(Arrays.asList(strategies), null);
}
/**
* Strategy for a varying named function signature like {@code f(i INT, str STRING, num
* NUMERIC...)} using a sequence of {@link ArgumentTypeStrategy}s. The first n - 1 arguments
* must be constant. The n-th argument can occur 0, 1, or more times.
*/
public static InputTypeStrategy varyingSequence(
String[] argumentNames, ArgumentTypeStrategy[] strategies) {
return new VaryingSequenceInputTypeStrategy(
Arrays.asList(strategies), Arrays.asList(argumentNames));
}
/** Arbitrarily often repeating sequence of argument type strategies. */
public static InputTypeStrategy repeatingSequence(ArgumentTypeStrategy... strategies) {
return new RepeatingSequenceInputTypeStrategy(Arrays.asList(strategies));
}
/**
* Strategy for a function signature of explicitly defined types like {@code f(STRING, INT)}.
* Implicit casts will be inserted if possible.
*
* <p>This is equivalent to using {@link #sequence(ArgumentTypeStrategy...)} and {@link
* #explicit(DataType)}.
*/
public static InputTypeStrategy explicitSequence(DataType... expectedDataTypes) {
final List<ArgumentTypeStrategy> strategies =
Arrays.stream(expectedDataTypes)
.map(InputTypeStrategies::explicit)
.collect(Collectors.toList());
return new SequenceInputTypeStrategy(strategies, null);
}
/**
* Strategy for a named function signature of explicitly defined types like {@code f(s STRING, i
* INT)}. Implicit casts will be inserted if possible.
*
* <p>This is equivalent to using {@link #sequence(String[], ArgumentTypeStrategy[])} and {@link
* #explicit(DataType)}.
*/
public static InputTypeStrategy explicitSequence(
String[] argumentNames, DataType[] expectedDataTypes) {
final List<ArgumentTypeStrategy> strategies =
Arrays.stream(expectedDataTypes)
.map(InputTypeStrategies::explicit)
.collect(Collectors.toList());
return new SequenceInputTypeStrategy(strategies, Arrays.asList(argumentNames));
}
/**
* An strategy that lets you apply other strategies for subsequences of the actual arguments.
*
* <p>The {@link #sequence(ArgumentTypeStrategy...)} should be preferred in most of the cases.
* Use this strategy only if you need to apply a common logic to a subsequence of the arguments.
*/
public static SubsequenceStrategyBuilder compositeSequence() {
return new SubsequenceStrategyBuilder();
}
/**
* Strategy for a disjunction of multiple {@link InputTypeStrategy}s into one like {@code
* f(NUMERIC) || f(STRING)}.
*
* <p>This strategy aims to infer a list of types that are equal to the input types (to prevent
* unnecessary casting) or (if this is not possible) the first more specific, casted types.
*/
public static InputTypeStrategy or(InputTypeStrategy... strategies) {
return new OrInputTypeStrategy(Arrays.asList(strategies));
}
/**
* Strategy that does not perform any modification or validation of the input. It checks the
* argument count though.
*/
public static InputTypeStrategy wildcardWithCount(ArgumentCount argumentCount) {
return new WildcardInputTypeStrategy(argumentCount);
}
/**
* Strategy that checks all types are comparable with each other. Requires at least one
* argument.
*/
public static InputTypeStrategy comparable(
ConstantArgumentCount argumentCount, StructuredComparison requiredComparison) {
return new ComparableTypeStrategy(argumentCount, requiredComparison);
}
// --------------------------------------------------------------------------------------------
// Argument type strategies
// --------------------------------------------------------------------------------------------
/**
* Strategy for inferring an unknown argument type from the function's output {@link DataType}
* if available.
*/
public static final OutputArgumentTypeStrategy OUTPUT_IF_NULL =
new OutputArgumentTypeStrategy();
/** Strategy for an argument that can be of any type. */
public static final AnyArgumentTypeStrategy ANY = new AnyArgumentTypeStrategy();
/** Strategy that checks if an argument is a literal. */
public static final LiteralArgumentTypeStrategy LITERAL =
new LiteralArgumentTypeStrategy(false);
/** Strategy that checks if an argument is a literal or NULL. */
public static final LiteralArgumentTypeStrategy LITERAL_OR_NULL =
new LiteralArgumentTypeStrategy(true);
/** Strategy that checks if an argument is a type literal. */
public static final TypeLiteralArgumentTypeStrategy TYPE_LITERAL =
new TypeLiteralArgumentTypeStrategy();
/** Strategy that checks that the argument has a composite type. */
public static final ArgumentTypeStrategy COMPOSITE = new CompositeArgumentTypeStrategy();
/**
* Argument type strategy that checks and casts for a common, least restrictive type of all
* arguments.
*/
public static final ArgumentTypeStrategy COMMON_ARG = new CommonArgumentTypeStrategy(false);
/**
* Argument type strategy that checks and casts for a common, least restrictive type of all
* arguments. But leaves nullability untouched.
*/
public static final ArgumentTypeStrategy COMMON_ARG_NULLABLE =
new CommonArgumentTypeStrategy(true);
/**
* Strategy for an argument that corresponds to an explicitly defined type casting. Implicit
* casts will be inserted if possible.
*/
public static ExplicitArgumentTypeStrategy explicit(DataType expectedDataType) {
return new ExplicitArgumentTypeStrategy(expectedDataType);
}
/**
* Strategy for an argument that corresponds to a given {@link LogicalTypeRoot}. Implicit casts
* will be inserted if possible.
*/
public static RootArgumentTypeStrategy logical(LogicalTypeRoot expectedRoot) {
return new RootArgumentTypeStrategy(expectedRoot, null);
}
/**
* Strategy for an argument that corresponds to a given {@link LogicalTypeRoot} and nullability.
* Implicit casts will be inserted if possible.
*/
public static RootArgumentTypeStrategy logical(
LogicalTypeRoot expectedRoot, boolean expectedNullability) {
return new RootArgumentTypeStrategy(expectedRoot, expectedNullability);
}
/**
* Strategy for an argument that corresponds to a given {@link LogicalTypeFamily}. Implicit
* casts will be inserted if possible.
*/
public static FamilyArgumentTypeStrategy logical(LogicalTypeFamily expectedFamily) {
return new FamilyArgumentTypeStrategy(expectedFamily, null);
}
/**
* Strategy for an argument that corresponds to a given {@link LogicalTypeFamily} and
* nullability. Implicit casts will be inserted if possible.
*/
public static FamilyArgumentTypeStrategy logical(
LogicalTypeFamily expectedFamily, boolean expectedNullability) {
return new FamilyArgumentTypeStrategy(expectedFamily, expectedNullability);
}
/** Strategy for an argument that must fulfill a given constraint. */
public static ConstraintArgumentTypeStrategy constraint(
String constraintMessage, Function<List<DataType>, Boolean> evaluator) {
return new ConstraintArgumentTypeStrategy(constraintMessage, evaluator);
}
/**
* Strategy for a conjunction of multiple {@link ArgumentTypeStrategy}s into one like {@code
* f(NUMERIC && LITERAL)}.
*
* <p>Some {@link ArgumentTypeStrategy}s cannot contribute an inferred type that is different
* from the input type (e.g. {@link #LITERAL}). Therefore, the order {@code f(X && Y)} or {@code
* f(Y && X)} matters as it defines the precedence in case the result must be casted to a more
* specific type.
*
* <p>This strategy aims to infer the first more specific, casted type or (if this is not
* possible) a type that has been inferred from all {@link ArgumentTypeStrategy}s.
*/
public static AndArgumentTypeStrategy and(ArgumentTypeStrategy... strategies) {
return new AndArgumentTypeStrategy(Arrays.asList(strategies));
}
/**
* Strategy for a disjunction of multiple {@link ArgumentTypeStrategy}s into one like {@code
* f(NUMERIC || STRING)}.
*
* <p>Some {@link ArgumentTypeStrategy}s cannot contribute an inferred type that is different
* from the input type (e.g. {@link #LITERAL}). Therefore, the order {@code f(X || Y)} or {@code
* f(Y || X)} matters as it defines the precedence in case the result must be casted to a more
* specific type.
*
* <p>This strategy aims to infer a type that is equal to the input type (to prevent unnecessary
* casting) or (if this is not possible) the first more specific, casted type.
*/
public static OrArgumentTypeStrategy or(ArgumentTypeStrategy... strategies) {
return new OrArgumentTypeStrategy(Arrays.asList(strategies));
}
/** Strategy for a symbol argument of a specific {@link TableSymbol} enum. */
public static SymbolArgumentTypeStrategy symbol(
Class<? extends Enum<? extends TableSymbol>> clazz) {
return new SymbolArgumentTypeStrategy(clazz);
}
/**
* An {@link InputTypeStrategy} that expects {@code count} arguments that have a common type.
*/
public static InputTypeStrategy commonType(int count) {
return new CommonInputTypeStrategy(ConstantArgumentCount.of(count));
}
// --------------------------------------------------------------------------------------------
private InputTypeStrategies() {
// no instantiation
}
}