Search in sources :

Example 16 with GenericType

use of io.confluent.ksql.function.types.GenericType in project ksql by confluentinc.

the class UdfUtilTest method shouldGetPartialGenericFunction.

@Test
public void shouldGetPartialGenericFunction() throws NoSuchMethodException {
    // Given:
    final Type genericType = getClass().getMethod("partialGenericFunctionType").getGenericReturnType();
    // When:
    final ParamType returnType = UdfUtil.getSchemaFromType(genericType);
    // Then:
    assertThat(returnType, is(LambdaType.of(ImmutableList.of(ParamTypes.LONG), GenericType.of("U"))));
}
Also used : ParamType(io.confluent.ksql.function.types.ParamType) MapType(io.confluent.ksql.function.types.MapType) LambdaType(io.confluent.ksql.function.types.LambdaType) StructType(io.confluent.ksql.function.types.StructType) Type(java.lang.reflect.Type) ArrayType(io.confluent.ksql.function.types.ArrayType) GenericType(io.confluent.ksql.function.types.GenericType) ParamType(io.confluent.ksql.function.types.ParamType) Test(org.junit.Test)

Example 17 with GenericType

use of io.confluent.ksql.function.types.GenericType in project ksql by confluentinc.

the class FunctionArgumentsUtil method getFunctionTypeInfo.

/**
 * Compute type information given a function call node. Specifically, computes
 * the function return type, the types of all arguments, and the types of all
 * lambda parameters for arguments that are lambda expressions.
 *
 * <p>Given a function call node, we have to do a two pass processing of the
 * function arguments in order to properly handle any potential lambda functions.</p>
 *
 * <p>In the first pass, if there are lambda functions, we create a SqlLambda that only contains
 * the number of input arguments for the lambda. We pass this first argument list
 * to UdfFactory in order to get the correct function. We can make this assumption
 * due to Java's handling of type erasure (Function(T,R) is considered the same as
 * Function(U,R)).</p>
 *
 * <p>In the second pass, we use the LambdaType inputTypes field to construct SqlLambdaResolved
 * that has the proper input type list and return type. We also need to construct a list of
 * lambda type mapping that should be used when processing each function argument subtree.</p>
 *
 * @param expressionTypeManager an expression type manager
 * @param functionCall  the function expression
 * @param udfFactory  udf factory for the function in the expression
 * @param lambdaMapping a type context
 *
 * @return a wrapper that contains a list of function arguments
 *         (any lambdas are SqlLambdaResolved), the ksql function,
 *         type contexts for use in further processing the function
 *         argument child nodes, and the return type of the ksql function
 */
// CHECKSTYLE_RULES.OFF: CyclomaticComplexity
public static FunctionTypeInfo getFunctionTypeInfo(final ExpressionTypeManager expressionTypeManager, final FunctionCall functionCall, final UdfFactory udfFactory, final Map<String, SqlType> lambdaMapping) {
    // CHECKSTYLE_RULES.ON: CyclomaticComplexity
    final List<Expression> arguments = functionCall.getArguments();
    final List<SqlArgument> functionArgumentTypes = firstPassOverFunctionArguments(arguments, expressionTypeManager, lambdaMapping);
    final KsqlScalarFunction function = udfFactory.getFunction(functionArgumentTypes);
    final SqlType returnSchema;
    final List<ArgumentInfo> argumentInfoForFunction = new ArrayList<>();
    if (!functionCall.hasLambdaFunctionCallArguments()) {
        returnSchema = function.getReturnType(functionArgumentTypes);
        return FunctionTypeInfo.of(functionArgumentTypes.stream().map(argument -> ArgumentInfo.of(argument, new HashMap<>(lambdaMapping))).collect(Collectors.toList()), returnSchema, function);
    } else {
        final List<ParamType> paramTypes = function.parameters();
        final Map<GenericType, SqlType> reservedGenerics = new HashMap<>();
        final List<SqlArgument> functionArgumentTypesWithResolvedLambdaType = new ArrayList<>();
        // second pass over the function arguments to properly do lambda type checking
        for (int i = 0; i < arguments.size(); i++) {
            final Expression expression = arguments.get(i);
            final ParamType parameter = paramTypes.get(i);
            if (expression instanceof LambdaFunctionCall) {
                // lambda node at this index in the function node argument list
                if (!(parameter instanceof LambdaType)) {
                    throw new RuntimeException(String.format("Error while processing lambda function." + "Expected lambda parameter but was %s" + "This is most likely an internal error and a " + "Github issue should be filed for debugging. " + "Include the function name, the parameters passed in, the expected " + "signature, and any other relevant information.", parameter.toString()));
                }
                final ArrayList<SqlType> lambdaSqlTypes = new ArrayList<>();
                final Map<String, SqlType> variableTypeMapping = mapLambdaParametersToTypes((LambdaFunctionCall) expression, (LambdaType) parameter, reservedGenerics, lambdaSqlTypes);
                final Map<String, SqlType> updateLambdaMapping = LambdaMappingUtil.resolveOldAndNewLambdaMapping(variableTypeMapping, lambdaMapping);
                final SqlType resolvedLambdaReturnType = expressionTypeManager.getExpressionSqlType(expression, updateLambdaMapping);
                final SqlArgument lambdaArgument = SqlArgument.of(SqlLambdaResolved.of(lambdaSqlTypes, resolvedLambdaReturnType));
                functionArgumentTypesWithResolvedLambdaType.add(lambdaArgument);
                argumentInfoForFunction.add(ArgumentInfo.of(lambdaArgument, new HashMap<>(updateLambdaMapping)));
            } else {
                functionArgumentTypesWithResolvedLambdaType.add(functionArgumentTypes.get(i));
                argumentInfoForFunction.add(ArgumentInfo.of(functionArgumentTypes.get(i), new HashMap<>(lambdaMapping)));
            }
            if (GenericsUtil.hasGenerics(parameter)) {
                final Pair<Boolean, Optional<KsqlException>> success = GenericsUtil.reserveGenerics(parameter, functionArgumentTypesWithResolvedLambdaType.get(i), reservedGenerics);
                if (!success.getLeft() && success.getRight().isPresent()) {
                    throw success.getRight().get();
                }
            }
        }
        returnSchema = function.getReturnType(functionArgumentTypesWithResolvedLambdaType);
        return new FunctionTypeInfo(argumentInfoForFunction, returnSchema, function);
    }
}
Also used : SqlArgument(io.confluent.ksql.schema.ksql.SqlArgument) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) SqlType(io.confluent.ksql.schema.ksql.types.SqlType) LambdaType(io.confluent.ksql.function.types.LambdaType) GenericType(io.confluent.ksql.function.types.GenericType) Optional(java.util.Optional) LambdaFunctionCall(io.confluent.ksql.execution.expression.tree.LambdaFunctionCall) ParamType(io.confluent.ksql.function.types.ParamType) KsqlScalarFunction(io.confluent.ksql.function.KsqlScalarFunction) Expression(io.confluent.ksql.execution.expression.tree.Expression)

Example 18 with GenericType

use of io.confluent.ksql.function.types.GenericType in project ksql by confluentinc.

the class FunctionArgumentsUtil method mapLambdaParametersToTypes.

private static Map<String, SqlType> mapLambdaParametersToTypes(final LambdaFunctionCall lambdaFunctionCall, final LambdaType lambdaParameter, final Map<GenericType, SqlType> reservedGenerics, final ArrayList<SqlType> lambdaSqlTypes) {
    if (lambdaFunctionCall.getArguments().size() != lambdaParameter.inputTypes().size()) {
        throw new IllegalArgumentException("Was expecting " + lambdaParameter.inputTypes().size() + " arguments but found " + lambdaFunctionCall.getArguments().size() + ", " + lambdaFunctionCall.getArguments() + ". Check your lambda statement.");
    }
    final Iterator<String> lambdaArgs = lambdaFunctionCall.getArguments().listIterator();
    final HashMap<String, SqlType> variableTypeMapping = new HashMap<>();
    for (ParamType inputParam : lambdaParameter.inputTypes()) {
        if (inputParam instanceof GenericType) {
            final GenericType genericParam = (GenericType) inputParam;
            if (!reservedGenerics.containsKey(genericParam)) {
                throw new RuntimeException(String.format("Could not resolve type for generic %s. " + "The generic mapping so far: %s", genericParam.toString(), reservedGenerics.toString()));
            }
            variableTypeMapping.put(lambdaArgs.next(), reservedGenerics.get(genericParam));
            lambdaSqlTypes.add(reservedGenerics.get(genericParam));
        } else {
            variableTypeMapping.put(lambdaArgs.next(), SchemaConverters.functionToSqlConverter().toSqlType(inputParam));
            lambdaSqlTypes.add(SchemaConverters.functionToSqlConverter().toSqlType(inputParam));
        }
    }
    return ImmutableMap.copyOf(variableTypeMapping);
}
Also used : GenericType(io.confluent.ksql.function.types.GenericType) HashMap(java.util.HashMap) SqlType(io.confluent.ksql.schema.ksql.types.SqlType) ParamType(io.confluent.ksql.function.types.ParamType)

Example 19 with GenericType

use of io.confluent.ksql.function.types.GenericType in project ksql by confluentinc.

the class UdfIndex method getCandidates.

private void getCandidates(final List<SqlArgument> arguments, final int argIndex, final Node current, final List<Node> candidates, final Map<GenericType, SqlType> reservedGenerics, final boolean allowCasts) {
    if (argIndex == arguments.size()) {
        if (current.value != null) {
            candidates.add(current);
        }
        return;
    }
    final SqlArgument arg = arguments.get(argIndex);
    for (final Entry<Parameter, Node> candidate : current.children.entrySet()) {
        final Map<GenericType, SqlType> reservedCopy = new HashMap<>(reservedGenerics);
        if (candidate.getKey().accepts(arg, reservedCopy, allowCasts)) {
            final Node node = candidate.getValue();
            getCandidates(arguments, argIndex + 1, node, candidates, reservedCopy, allowCasts);
        }
    }
}
Also used : SqlArgument(io.confluent.ksql.schema.ksql.SqlArgument) GenericType(io.confluent.ksql.function.types.GenericType) HashMap(java.util.HashMap) SqlType(io.confluent.ksql.schema.ksql.types.SqlType)

Example 20 with GenericType

use of io.confluent.ksql.function.types.GenericType in project ksql by confluentinc.

the class UdfIndexTest method shouldNotMatchGenericMethodWithAlreadyReservedTypes.

@Test
public void shouldNotMatchGenericMethodWithAlreadyReservedTypes() {
    // Given:
    final GenericType generic = GenericType.of("A");
    givenFunctions(function(EXPECTED, false, generic, generic));
    // When:
    final Exception e = assertThrows(KsqlException.class, () -> udfIndex.getFunction(ImmutableList.of(SqlArgument.of(INTEGER), SqlArgument.of(SqlTypes.STRING))));
    // Then:
    assertThat(e.getMessage(), containsString("Function 'name' does not accept parameters " + "(INTEGER, STRING)"));
}
Also used : GenericType(io.confluent.ksql.function.types.GenericType) KsqlException(io.confluent.ksql.util.KsqlException) Test(org.junit.Test)

Aggregations

GenericType (io.confluent.ksql.function.types.GenericType)34 Test (org.junit.Test)27 LambdaType (io.confluent.ksql.function.types.LambdaType)17 ParamType (io.confluent.ksql.function.types.ParamType)17 SqlType (io.confluent.ksql.schema.ksql.types.SqlType)16 ArrayType (io.confluent.ksql.function.types.ArrayType)12 MapType (io.confluent.ksql.function.types.MapType)12 StructType (io.confluent.ksql.function.types.StructType)12 SqlArgument (io.confluent.ksql.schema.ksql.SqlArgument)11 Type (java.lang.reflect.Type)10 KsqlException (io.confluent.ksql.util.KsqlException)5 HashMap (java.util.HashMap)5 ArrayList (java.util.ArrayList)2 Optional (java.util.Optional)2 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1 Expression (io.confluent.ksql.execution.expression.tree.Expression)1 LambdaFunctionCall (io.confluent.ksql.execution.expression.tree.LambdaFunctionCall)1 UdfUtil (io.confluent.ksql.execution.function.UdfUtil)1 KsqlScalarFunction (io.confluent.ksql.function.KsqlScalarFunction)1 ParamTypes (io.confluent.ksql.function.types.ParamTypes)1