Search in sources :

Example 16 with ParamType

use of io.confluent.ksql.function.types.ParamType 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 17 with ParamType

use of io.confluent.ksql.function.types.ParamType 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 18 with ParamType

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

the class TermCompiler method visitFunctionCall.

@Override
public Term visitFunctionCall(final FunctionCall node, final Context context) {
    final UdfFactory udfFactory = functionRegistry.getUdfFactory(node.getName());
    final FunctionTypeInfo argumentsAndContext = FunctionArgumentsUtil.getFunctionTypeInfo(expressionTypeManager, node, udfFactory, context.getLambdaSqlTypeMapping());
    final List<ArgumentInfo> argumentInfos = argumentsAndContext.getArgumentInfos();
    final KsqlScalarFunction function = argumentsAndContext.getFunction();
    final SqlType functionReturnSchema = argumentsAndContext.getReturnType();
    final Class<?> javaClass = SchemaConverters.sqlToJavaConverter().toJavaType(functionReturnSchema);
    final List<Expression> arguments = node.getArguments();
    final List<Term> args = new ArrayList<>();
    for (int i = 0; i < arguments.size(); i++) {
        final Expression arg = arguments.get(i);
        // lambda arguments and null values are considered to have null type
        final SqlType sqlType = argumentInfos.get(i).getSqlArgument().getSqlType().orElse(null);
        ;
        final ParamType paramType;
        if (i >= function.parameters().size() - 1 && function.isVariadic()) {
            paramType = ((ArrayType) Iterables.getLast(function.parameters())).element();
        } else {
            paramType = function.parameters().get(i);
        }
        // This will attempt to cast to the expected argument type and will throw an error if
        // it cannot be done.
        final Term argTerm = process(convertArgument(arg, sqlType, paramType), new Context(argumentInfos.get(i).getLambdaSqlTypeMapping()));
        args.add(argTerm);
    }
    final Kudf kudf = function.newInstance(ksqlConfig);
    return new FunctionCallTerm(kudf, args, javaClass, functionReturnSchema);
}
Also used : Context(io.confluent.ksql.execution.interpreter.TermCompiler.Context) ArrayList(java.util.ArrayList) UdfFactory(io.confluent.ksql.function.UdfFactory) Term(io.confluent.ksql.execution.interpreter.terms.Term) LambdaFunction3Term(io.confluent.ksql.execution.interpreter.terms.LambdaFunctionTerms.LambdaFunction3Term) ColumnReferenceTerm(io.confluent.ksql.execution.interpreter.terms.ColumnReferenceTerm) LambdaFunction1Term(io.confluent.ksql.execution.interpreter.terms.LambdaFunctionTerms.LambdaFunction1Term) LikeTerm(io.confluent.ksql.execution.interpreter.terms.LikeTerm) FunctionCallTerm(io.confluent.ksql.execution.interpreter.terms.FunctionCallTerm) SearchedCaseTerm(io.confluent.ksql.execution.interpreter.terms.SearchedCaseTerm) StructTerm(io.confluent.ksql.execution.interpreter.terms.StructTerm) LambdaVariableTerm(io.confluent.ksql.execution.interpreter.terms.LambdaVariableTerm) CreateMapTerm(io.confluent.ksql.execution.interpreter.terms.CreateMapTerm) LambdaFunction2Term(io.confluent.ksql.execution.interpreter.terms.LambdaFunctionTerms.LambdaFunction2Term) SubscriptTerm(io.confluent.ksql.execution.interpreter.terms.SubscriptTerm) NotTerm(io.confluent.ksql.execution.interpreter.terms.NotTerm) IsNullTerm(io.confluent.ksql.execution.interpreter.terms.IsNullTerm) CreateArrayTerm(io.confluent.ksql.execution.interpreter.terms.CreateArrayTerm) DereferenceTerm(io.confluent.ksql.execution.interpreter.terms.DereferenceTerm) IsNotNullTerm(io.confluent.ksql.execution.interpreter.terms.IsNotNullTerm) InPredicateTerm(io.confluent.ksql.execution.interpreter.terms.InPredicateTerm) ParamType(io.confluent.ksql.function.types.ParamType) Kudf(io.confluent.ksql.function.udf.Kudf) KsqlScalarFunction(io.confluent.ksql.function.KsqlScalarFunction) FunctionTypeInfo(io.confluent.ksql.execution.util.FunctionArgumentsUtil.FunctionTypeInfo) LogicalBinaryExpression(io.confluent.ksql.execution.expression.tree.LogicalBinaryExpression) Expression(io.confluent.ksql.execution.expression.tree.Expression) DereferenceExpression(io.confluent.ksql.execution.expression.tree.DereferenceExpression) ArithmeticUnaryExpression(io.confluent.ksql.execution.expression.tree.ArithmeticUnaryExpression) SimpleCaseExpression(io.confluent.ksql.execution.expression.tree.SimpleCaseExpression) InListExpression(io.confluent.ksql.execution.expression.tree.InListExpression) SearchedCaseExpression(io.confluent.ksql.execution.expression.tree.SearchedCaseExpression) ArithmeticBinaryExpression(io.confluent.ksql.execution.expression.tree.ArithmeticBinaryExpression) CreateMapExpression(io.confluent.ksql.execution.expression.tree.CreateMapExpression) CreateArrayExpression(io.confluent.ksql.execution.expression.tree.CreateArrayExpression) CreateStructExpression(io.confluent.ksql.execution.expression.tree.CreateStructExpression) NotExpression(io.confluent.ksql.execution.expression.tree.NotExpression) SubscriptExpression(io.confluent.ksql.execution.expression.tree.SubscriptExpression) ComparisonExpression(io.confluent.ksql.execution.expression.tree.ComparisonExpression) SqlType(io.confluent.ksql.schema.ksql.types.SqlType) FunctionCallTerm(io.confluent.ksql.execution.interpreter.terms.FunctionCallTerm) ArgumentInfo(io.confluent.ksql.execution.util.FunctionArgumentsUtil.ArgumentInfo)

Example 19 with ParamType

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

the class KsqlScalarFunction method createLegacyBuiltIn.

/**
 * Create built in / legacy function.
 */
public static KsqlScalarFunction createLegacyBuiltIn(final SqlType returnType, final List<ParamType> arguments, final FunctionName functionName, final Class<? extends Kudf> kudfClass) {
    final ParamType javaReturnType = SchemaConverters.sqlToFunctionConverter().toFunctionType(returnType);
    final List<ParameterInfo> paramInfos = arguments.stream().map(type -> new ParameterInfo("", type, "", false)).collect(Collectors.toList());
    return create((i1, i2) -> returnType, javaReturnType, paramInfos, functionName, kudfClass, // findbugs was complaining about a dead store so I inlined this
    ksqlConfig -> {
        try {
            return kudfClass.newInstance();
        } catch (final Exception e) {
            throw new KsqlException("Failed to create instance of kudfClass " + kudfClass + " for function " + functionName, e);
        }
    }, "", INTERNAL_PATH, false);
}
Also used : Kudf(io.confluent.ksql.function.udf.Kudf) FunctionName(io.confluent.ksql.name.FunctionName) ParamType(io.confluent.ksql.function.types.ParamType) KsqlConfig(io.confluent.ksql.util.KsqlConfig) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) Immutable(com.google.errorprone.annotations.Immutable) KsqlException(io.confluent.ksql.util.KsqlException) SchemaConverters(io.confluent.ksql.schema.ksql.SchemaConverters) SqlType(io.confluent.ksql.schema.ksql.types.SqlType) SuppressFBWarnings(edu.umd.cs.findbugs.annotations.SuppressFBWarnings) EffectivelyImmutable(io.confluent.ksql.testing.EffectivelyImmutable) KsqlException(io.confluent.ksql.util.KsqlException) ParamType(io.confluent.ksql.function.types.ParamType) KsqlException(io.confluent.ksql.util.KsqlException)

Example 20 with ParamType

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

the class GenericsUtil method resolveGenerics.

/**
 * Identifies a mapping from generic type to concrete type based on a {@code schema} and
 * an {@code instance}, where the {@code instance} schema is expected to have no generic
 * types and have the same nested structure as {@code schema}. Any Generic type mapping
 * identified is added to the list passed in.
 *
 * @param mapping   a list of GenericType to SqlType mappings
 * @param schema    the schema that may contain generics
 * @param instance  a schema with the same structure as {@code schema} but with no generics
 *
 * @return whether we were able to resolve generics in the instance and schema
 */
// CHECKSTYLE_RULES.OFF: NPathComplexity
// CHECKSTYLE_RULES.OFF: CyclomaticComplexity
private static boolean resolveGenerics(final List<Entry<GenericType, SqlType>> mapping, final ParamType schema, final SqlArgument instance) {
    if (!isGeneric(schema) && !matches(schema, instance)) {
        // cannot identify from type mismatch
        return false;
    } else if (!hasGenerics(schema)) {
        // nothing left to identify
        return true;
    }
    KsqlPreconditions.checkArgument(isGeneric(schema) || (matches(schema, instance)), "Cannot resolve generics if the schema and instance have differing types: " + schema + " vs. " + instance);
    if (schema instanceof LambdaType) {
        final LambdaType lambdaType = (LambdaType) schema;
        final SqlLambda sqlLambda = instance.getSqlLambdaOrThrow();
        if (lambdaType.inputTypes().size() == sqlLambda.getNumInputs()) {
            if (sqlLambda instanceof SqlLambdaResolved) {
                final SqlLambdaResolved sqlLambdaResolved = (SqlLambdaResolved) sqlLambda;
                int i = 0;
                for (final ParamType paramType : lambdaType.inputTypes()) {
                    if (!resolveGenerics(mapping, paramType, SqlArgument.of(sqlLambdaResolved.getInputType().get(i)))) {
                        return false;
                    }
                    i++;
                }
                return resolveGenerics(mapping, lambdaType.returnType(), SqlArgument.of(sqlLambdaResolved.getReturnType()));
            } else {
                return true;
            }
        } else {
            return false;
        }
    }
    final SqlType sqlType = instance.getSqlTypeOrThrow();
    if (isGeneric(schema)) {
        mapping.add(new HashMap.SimpleEntry<>((GenericType) schema, sqlType));
    }
    if (schema instanceof ArrayType) {
        final SqlArray sqlArray = (SqlArray) sqlType;
        return resolveGenerics(mapping, ((ArrayType) schema).element(), SqlArgument.of(sqlArray.getItemType()));
    }
    if (schema instanceof MapType) {
        final SqlMap sqlMap = (SqlMap) sqlType;
        final MapType mapType = (MapType) schema;
        return resolveGenerics(mapping, mapType.key(), SqlArgument.of(sqlMap.getKeyType())) && resolveGenerics(mapping, mapType.value(), SqlArgument.of(sqlMap.getValueType()));
    }
    if (schema instanceof StructType) {
        throw new KsqlException("Generic STRUCT is not yet supported");
    }
    return true;
}
Also used : LambdaType(io.confluent.ksql.function.types.LambdaType) GenericType(io.confluent.ksql.function.types.GenericType) SqlMap(io.confluent.ksql.schema.ksql.types.SqlMap) StructType(io.confluent.ksql.function.types.StructType) SqlLambda(io.confluent.ksql.schema.ksql.types.SqlLambda) HashMap(java.util.HashMap) KsqlException(io.confluent.ksql.util.KsqlException) ParamType(io.confluent.ksql.function.types.ParamType) MapType(io.confluent.ksql.function.types.MapType) ArrayType(io.confluent.ksql.function.types.ArrayType) SqlLambdaResolved(io.confluent.ksql.schema.ksql.types.SqlLambdaResolved) SqlType(io.confluent.ksql.schema.ksql.types.SqlType) SqlArray(io.confluent.ksql.schema.ksql.types.SqlArray)

Aggregations

ParamType (io.confluent.ksql.function.types.ParamType)35 GenericType (io.confluent.ksql.function.types.GenericType)26 Test (org.junit.Test)22 LambdaType (io.confluent.ksql.function.types.LambdaType)20 StructType (io.confluent.ksql.function.types.StructType)20 ArrayType (io.confluent.ksql.function.types.ArrayType)19 Type (java.lang.reflect.Type)19 MapType (io.confluent.ksql.function.types.MapType)18 SqlType (io.confluent.ksql.schema.ksql.types.SqlType)11 HashMap (java.util.HashMap)5 KsqlException (io.confluent.ksql.util.KsqlException)4 ParameterizedType (java.lang.reflect.ParameterizedType)4 ArrayList (java.util.ArrayList)4 GenericArrayType (java.lang.reflect.GenericArrayType)3 TypeVariable (java.lang.reflect.TypeVariable)3 VisibleForTesting (com.google.common.annotations.VisibleForTesting)2 Expression (io.confluent.ksql.execution.expression.tree.Expression)2 KsqlScalarFunction (io.confluent.ksql.function.KsqlScalarFunction)2 ParamTypes (io.confluent.ksql.function.types.ParamTypes)2 Kudf (io.confluent.ksql.function.udf.Kudf)2