Search in sources :

Example 1 with BOXED_NULLABLE

use of io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE in project trino by trinodb.

the class ScalarFunctionAdapter method adaptParameter.

private MethodHandle adaptParameter(MethodHandle methodHandle, int parameterIndex, Type argumentType, InvocationArgumentConvention actualArgumentConvention, InvocationArgumentConvention expectedArgumentConvention, InvocationReturnConvention returnConvention) {
    if (actualArgumentConvention == expectedArgumentConvention) {
        return methodHandle;
    }
    if (actualArgumentConvention == BLOCK_POSITION) {
        throw new IllegalArgumentException("Block and position argument can not be adapted");
    }
    if (actualArgumentConvention == FUNCTION) {
        throw new IllegalArgumentException("Function argument can not be adapted");
    }
    // caller will never pass null
    if (expectedArgumentConvention == NEVER_NULL) {
        if (actualArgumentConvention == BOXED_NULLABLE) {
            // if actual argument is boxed primitive, change method handle to accept a primitive and then box to actual method
            if (isWrapperType(methodHandle.type().parameterType(parameterIndex))) {
                MethodType targetType = methodHandle.type().changeParameterType(parameterIndex, unwrap(methodHandle.type().parameterType(parameterIndex)));
                methodHandle = explicitCastArguments(methodHandle, targetType);
            }
            return methodHandle;
        }
        if (actualArgumentConvention == NULL_FLAG) {
            // actual method takes value and null flag, so change method handle to not have the flag and always pass false to the actual method
            return insertArguments(methodHandle, parameterIndex + 1, false);
        }
        throw new IllegalArgumentException("Unsupported actual argument convention: " + actualArgumentConvention);
    }
    // caller will pass Java null for SQL null
    if (expectedArgumentConvention == BOXED_NULLABLE) {
        if (actualArgumentConvention == NEVER_NULL) {
            if (nullAdaptationPolicy == UNSUPPORTED) {
                throw new IllegalArgumentException("Not null argument can not be adapted to nullable");
            }
            // box argument
            Class<?> boxedType = wrap(methodHandle.type().parameterType(parameterIndex));
            MethodType targetType = methodHandle.type().changeParameterType(parameterIndex, boxedType);
            methodHandle = explicitCastArguments(methodHandle, targetType);
            if (nullAdaptationPolicy == UNDEFINED_VALUE_FOR_NULL) {
                // currently, we just perform unboxing, which converts nulls to Java primitive default value
                return methodHandle;
            }
            if (nullAdaptationPolicy == RETURN_NULL_ON_NULL) {
                if (returnConvention == FAIL_ON_NULL) {
                    throw new IllegalArgumentException("RETURN_NULL_ON_NULL adaptation can not be used with FAIL_ON_NULL return convention");
                }
                return guardWithTest(isNullArgument(methodHandle.type(), parameterIndex), returnNull(methodHandle.type()), methodHandle);
            }
            if (nullAdaptationPolicy == THROW_ON_NULL) {
                MethodType adapterType = methodType(boxedType, boxedType);
                MethodHandle adapter = guardWithTest(isNullArgument(adapterType, 0), throwTrinoNullArgumentException(adapterType), identity(boxedType));
                return collectArguments(methodHandle, parameterIndex, adapter);
            }
        }
        if (actualArgumentConvention == NULL_FLAG) {
            // The conversion is described below in reverse order as this is how method handle adaptation works.  The provided example
            // signature is based on a boxed Long argument.
            // 3. unbox the value (if null the java default is sent)
            // long, boolean => Long, boolean
            Class<?> parameterType = methodHandle.type().parameterType(parameterIndex);
            methodHandle = explicitCastArguments(methodHandle, methodHandle.type().changeParameterType(parameterIndex, wrap(parameterType)));
            // 2. replace second argument with the result of isNull
            // long, boolean => Long, Long
            methodHandle = filterArguments(methodHandle, parameterIndex + 1, explicitCastArguments(IS_NULL_METHOD, methodType(boolean.class, wrap(parameterType))));
            // 1. Duplicate the argument, so we have two copies of the value
            // Long, Long => Long
            int[] reorder = IntStream.range(0, methodHandle.type().parameterCount()).map(i -> i <= parameterIndex ? i : i - 1).toArray();
            MethodType newType = methodHandle.type().dropParameterTypes(parameterIndex + 1, parameterIndex + 2);
            methodHandle = permuteArguments(methodHandle, newType, reorder);
            return methodHandle;
        }
        throw new IllegalArgumentException("Unsupported actual argument convention: " + actualArgumentConvention);
    }
    // caller will pass boolean true in the next argument for SQL null
    if (expectedArgumentConvention == NULL_FLAG) {
        if (actualArgumentConvention == NEVER_NULL) {
            if (nullAdaptationPolicy == UNSUPPORTED) {
                throw new IllegalArgumentException("Not null argument can not be adapted to nullable");
            }
            if (nullAdaptationPolicy == UNDEFINED_VALUE_FOR_NULL) {
                // add null flag to call
                methodHandle = dropArguments(methodHandle, parameterIndex + 1, boolean.class);
                return methodHandle;
            }
            // if caller sets null flag, return null, otherwise invoke target
            if (nullAdaptationPolicy == RETURN_NULL_ON_NULL) {
                if (returnConvention == FAIL_ON_NULL) {
                    throw new IllegalArgumentException("RETURN_NULL_ON_NULL adaptation can not be used with FAIL_ON_NULL return convention");
                }
                // add null flag to call
                methodHandle = dropArguments(methodHandle, parameterIndex + 1, boolean.class);
                return guardWithTest(isTrueNullFlag(methodHandle.type(), parameterIndex), returnNull(methodHandle.type()), methodHandle);
            }
            if (nullAdaptationPolicy == THROW_ON_NULL) {
                MethodHandle adapter = identity(methodHandle.type().parameterType(parameterIndex));
                adapter = dropArguments(adapter, 1, boolean.class);
                adapter = guardWithTest(isTrueNullFlag(adapter.type(), 0), throwTrinoNullArgumentException(adapter.type()), adapter);
                return collectArguments(methodHandle, parameterIndex, adapter);
            }
        }
        if (actualArgumentConvention == BOXED_NULLABLE) {
            return collectArguments(methodHandle, parameterIndex, boxedToNullFlagFilter(methodHandle.type().parameterType(parameterIndex)));
        }
        throw new IllegalArgumentException("Unsupported actual argument convention: " + actualArgumentConvention);
    }
    // caller will pass boolean true in the next argument for SQL null
    if (expectedArgumentConvention == BLOCK_POSITION) {
        MethodHandle getBlockValue = getBlockValue(argumentType, methodHandle.type().parameterType(parameterIndex));
        if (actualArgumentConvention == NEVER_NULL) {
            if (nullAdaptationPolicy == UNDEFINED_VALUE_FOR_NULL) {
                // Current, null is not checked, so whatever type returned is passed through
                methodHandle = collectArguments(methodHandle, parameterIndex, getBlockValue);
                return methodHandle;
            }
            if (nullAdaptationPolicy == RETURN_NULL_ON_NULL && returnConvention != FAIL_ON_NULL) {
                // if caller sets null flag, return null, otherwise invoke target
                methodHandle = collectArguments(methodHandle, parameterIndex, getBlockValue);
                return guardWithTest(isBlockPositionNull(methodHandle.type(), parameterIndex), returnNull(methodHandle.type()), methodHandle);
            }
            if (nullAdaptationPolicy == THROW_ON_NULL || nullAdaptationPolicy == UNSUPPORTED || nullAdaptationPolicy == RETURN_NULL_ON_NULL) {
                MethodHandle adapter = guardWithTest(isBlockPositionNull(getBlockValue.type(), 0), throwTrinoNullArgumentException(getBlockValue.type()), getBlockValue);
                return collectArguments(methodHandle, parameterIndex, adapter);
            }
        }
        if (actualArgumentConvention == BOXED_NULLABLE) {
            getBlockValue = explicitCastArguments(getBlockValue, getBlockValue.type().changeReturnType(wrap(getBlockValue.type().returnType())));
            getBlockValue = guardWithTest(isBlockPositionNull(getBlockValue.type(), 0), returnNull(getBlockValue.type()), getBlockValue);
            methodHandle = collectArguments(methodHandle, parameterIndex, getBlockValue);
            return methodHandle;
        }
        if (actualArgumentConvention == NULL_FLAG) {
            // long, boolean => long, Block, int
            MethodHandle isNull = isBlockPositionNull(getBlockValue.type(), 0);
            methodHandle = collectArguments(methodHandle, parameterIndex + 1, isNull);
            // long, Block, int => Block, int, Block, int
            getBlockValue = guardWithTest(isBlockPositionNull(getBlockValue.type(), 0), returnNull(getBlockValue.type()), getBlockValue);
            methodHandle = collectArguments(methodHandle, parameterIndex, getBlockValue);
            int[] reorder = IntStream.range(0, methodHandle.type().parameterCount()).map(i -> i <= parameterIndex + 1 ? i : i - 2).toArray();
            MethodType newType = methodHandle.type().dropParameterTypes(parameterIndex + 2, parameterIndex + 4);
            methodHandle = permuteArguments(methodHandle, newType, reorder);
            return methodHandle;
        }
        throw new IllegalArgumentException("Unsupported actual argument convention: " + actualArgumentConvention);
    }
    throw new IllegalArgumentException("Unsupported expected argument convention: " + expectedArgumentConvention);
}
Also used : IntStream(java.util.stream.IntStream) MethodHandles.guardWithTest(java.lang.invoke.MethodHandles.guardWithTest) MethodHandle(java.lang.invoke.MethodHandle) BLOCK_POSITION(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION) MethodHandles.dropArguments(java.lang.invoke.MethodHandles.dropArguments) Slice(io.airlift.slice.Slice) FAIL_ON_NULL(io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL) RETURN_NULL_ON_NULL(io.trino.spi.function.ScalarFunctionAdapter.NullAdaptationPolicy.RETURN_NULL_ON_NULL) MethodHandles.explicitCastArguments(java.lang.invoke.MethodHandles.explicitCastArguments) MethodHandles.insertArguments(java.lang.invoke.MethodHandles.insertArguments) Type(io.trino.spi.type.Type) FUNCTION(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.FUNCTION) MethodHandles.publicLookup(java.lang.invoke.MethodHandles.publicLookup) MethodHandles.identity(java.lang.invoke.MethodHandles.identity) MethodHandles.lookup(java.lang.invoke.MethodHandles.lookup) UNDEFINED_VALUE_FOR_NULL(io.trino.spi.function.ScalarFunctionAdapter.NullAdaptationPolicy.UNDEFINED_VALUE_FOR_NULL) MethodHandles.filterReturnValue(java.lang.invoke.MethodHandles.filterReturnValue) NULL_FLAG(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NULL_FLAG) Block(io.trino.spi.block.Block) Objects.requireNonNull(java.util.Objects.requireNonNull) InvocationArgumentConvention(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention) NULLABLE_RETURN(io.trino.spi.function.InvocationConvention.InvocationReturnConvention.NULLABLE_RETURN) MethodType.methodType(java.lang.invoke.MethodType.methodType) MethodHandles.constant(java.lang.invoke.MethodHandles.constant) BOXED_NULLABLE(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE) MethodHandles.permuteArguments(java.lang.invoke.MethodHandles.permuteArguments) TrinoException(io.trino.spi.TrinoException) InvocationReturnConvention(io.trino.spi.function.InvocationConvention.InvocationReturnConvention) Objects(java.util.Objects) List(java.util.List) MethodType(java.lang.invoke.MethodType) THROW_ON_NULL(io.trino.spi.function.ScalarFunctionAdapter.NullAdaptationPolicy.THROW_ON_NULL) MethodHandles.collectArguments(java.lang.invoke.MethodHandles.collectArguments) MethodHandles.filterArguments(java.lang.invoke.MethodHandles.filterArguments) StandardErrorCode(io.trino.spi.StandardErrorCode) ErrorCodeSupplier(io.trino.spi.ErrorCodeSupplier) NEVER_NULL(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL) UNSUPPORTED(io.trino.spi.function.ScalarFunctionAdapter.NullAdaptationPolicy.UNSUPPORTED) MethodHandles.throwException(java.lang.invoke.MethodHandles.throwException) MethodType(java.lang.invoke.MethodType) MethodHandle(java.lang.invoke.MethodHandle)

Example 2 with BOXED_NULLABLE

use of io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE in project trino by trinodb.

the class AbstractGreatestLeast method specialize.

@Override
public ScalarFunctionImplementation specialize(BoundSignature boundSignature, FunctionDependencies functionDependencies) {
    Type type = boundSignature.getReturnType();
    checkArgument(type.isOrderable(), "Type must be orderable");
    MethodHandle compareMethod = getMinMaxCompare(functionDependencies, type, simpleConvention(FAIL_ON_NULL, NEVER_NULL, NEVER_NULL), min);
    List<Class<?>> javaTypes = IntStream.range(0, boundSignature.getArity()).mapToObj(i -> wrap(type.getJavaType())).collect(toImmutableList());
    Class<?> clazz = generate(javaTypes, compareMethod);
    MethodHandle methodHandle = methodHandle(clazz, getFunctionMetadata().getSignature().getName(), javaTypes.toArray(new Class<?>[0]));
    return new ChoicesScalarFunctionImplementation(boundSignature, NULLABLE_RETURN, nCopies(javaTypes.size(), BOXED_NULLABLE), methodHandle);
}
Also used : FunctionDependencies(io.trino.metadata.FunctionDependencies) SCALAR(io.trino.metadata.FunctionKind.SCALAR) FAIL_ON_NULL(io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL) BytecodeBlock(io.airlift.bytecode.BytecodeBlock) FunctionNullability(io.trino.metadata.FunctionNullability) Scope(io.airlift.bytecode.Scope) DynamicClassLoader(io.airlift.bytecode.DynamicClassLoader) InvocationConvention.simpleConvention(io.trino.spi.function.InvocationConvention.simpleConvention) Access.a(io.airlift.bytecode.Access.a) BOOTSTRAP_METHOD(io.trino.sql.gen.Bootstrap.BOOTSTRAP_METHOD) Parameter.arg(io.airlift.bytecode.Parameter.arg) NOT_SUPPORTED(io.trino.spi.StandardErrorCode.NOT_SUPPORTED) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) CompilerUtils.makeClassName(io.trino.util.CompilerUtils.makeClassName) MinMaxCompare.getMinMaxCompare(io.trino.util.MinMaxCompare.getMinMaxCompare) BytecodeExpressions.constantNull(io.airlift.bytecode.expression.BytecodeExpressions.constantNull) FunctionMetadata(io.trino.metadata.FunctionMetadata) TypeSignature(io.trino.spi.type.TypeSignature) MethodDefinition(io.airlift.bytecode.MethodDefinition) FunctionDependencyDeclaration(io.trino.metadata.FunctionDependencyDeclaration) Collections.nCopies(java.util.Collections.nCopies) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) BytecodeExpression(io.airlift.bytecode.expression.BytecodeExpression) Collectors.joining(java.util.stream.Collectors.joining) CompilerUtils.defineClass(io.trino.util.CompilerUtils.defineClass) Signature.orderableTypeParameter(io.trino.metadata.Signature.orderableTypeParameter) List(java.util.List) PRIVATE(io.airlift.bytecode.Access.PRIVATE) BytecodeExpressions.or(io.airlift.bytecode.expression.BytecodeExpressions.or) Failures.checkCondition(io.trino.util.Failures.checkCondition) NEVER_NULL(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL) ClassDefinition(io.airlift.bytecode.ClassDefinition) IntStream(java.util.stream.IntStream) ParameterizedType.type(io.airlift.bytecode.ParameterizedType.type) Variable(io.airlift.bytecode.Variable) MethodHandle(java.lang.invoke.MethodHandle) Type(io.trino.spi.type.Type) Parameter(io.airlift.bytecode.Parameter) ImmutableList(com.google.common.collect.ImmutableList) LabelNode(io.airlift.bytecode.instruction.LabelNode) Signature(io.trino.metadata.Signature) NULLABLE_RETURN(io.trino.spi.function.InvocationConvention.InvocationReturnConvention.NULLABLE_RETURN) FINAL(io.airlift.bytecode.Access.FINAL) STATIC(io.airlift.bytecode.Access.STATIC) MethodType.methodType(java.lang.invoke.MethodType.methodType) BytecodeExpressions.invokeDynamic(io.airlift.bytecode.expression.BytecodeExpressions.invokeDynamic) BOXED_NULLABLE(io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE) SqlScalarFunction(io.trino.metadata.SqlScalarFunction) IfStatement(io.airlift.bytecode.control.IfStatement) CallSiteBinder(io.trino.sql.gen.CallSiteBinder) PUBLIC(io.airlift.bytecode.Access.PUBLIC) MinMaxCompare.getMinMaxCompareFunctionDependencies(io.trino.util.MinMaxCompare.getMinMaxCompareFunctionDependencies) BoundSignature(io.trino.metadata.BoundSignature) BytecodeExpressions.isNull(io.airlift.bytecode.expression.BytecodeExpressions.isNull) Primitives.wrap(com.google.common.primitives.Primitives.wrap) Reflection.methodHandle(io.trino.util.Reflection.methodHandle) Type(io.trino.spi.type.Type) MethodType.methodType(java.lang.invoke.MethodType.methodType) CompilerUtils.defineClass(io.trino.util.CompilerUtils.defineClass) MethodHandle(java.lang.invoke.MethodHandle)

Aggregations

BOXED_NULLABLE (io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE)2 NEVER_NULL (io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL)2 FAIL_ON_NULL (io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL)2 NULLABLE_RETURN (io.trino.spi.function.InvocationConvention.InvocationReturnConvention.NULLABLE_RETURN)2 Preconditions.checkArgument (com.google.common.base.Preconditions.checkArgument)1 ImmutableList (com.google.common.collect.ImmutableList)1 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)1 Primitives.wrap (com.google.common.primitives.Primitives.wrap)1 FINAL (io.airlift.bytecode.Access.FINAL)1 PRIVATE (io.airlift.bytecode.Access.PRIVATE)1 PUBLIC (io.airlift.bytecode.Access.PUBLIC)1 STATIC (io.airlift.bytecode.Access.STATIC)1 Access.a (io.airlift.bytecode.Access.a)1 BytecodeBlock (io.airlift.bytecode.BytecodeBlock)1 ClassDefinition (io.airlift.bytecode.ClassDefinition)1 DynamicClassLoader (io.airlift.bytecode.DynamicClassLoader)1 MethodDefinition (io.airlift.bytecode.MethodDefinition)1 Parameter (io.airlift.bytecode.Parameter)1 Parameter.arg (io.airlift.bytecode.Parameter.arg)1 ParameterizedType.type (io.airlift.bytecode.ParameterizedType.type)1