com.facebook.presto.operator.scalar.Least.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.presto.operator.scalar.Least.java

Source

/*
 * Licensed 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 com.facebook.presto.operator.scalar;

import com.facebook.presto.byteCode.Block;
import com.facebook.presto.byteCode.ClassDefinition;
import com.facebook.presto.byteCode.Scope;
import com.facebook.presto.byteCode.DynamicClassLoader;
import com.facebook.presto.byteCode.MethodDefinition;
import com.facebook.presto.byteCode.Parameter;
import com.facebook.presto.byteCode.Variable;
import com.facebook.presto.byteCode.control.IfStatement;
import com.facebook.presto.metadata.FunctionInfo;
import com.facebook.presto.metadata.FunctionRegistry;
import com.facebook.presto.metadata.ParametricScalar;
import com.facebook.presto.metadata.Signature;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.block.BlockBuilder;
import com.facebook.presto.spi.block.BlockBuilderStatus;
import com.facebook.presto.spi.type.StandardTypes;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.TypeManager;
import com.facebook.presto.sql.gen.CallSiteBinder;
import com.facebook.presto.sql.gen.CompilerOperations;
import com.facebook.presto.sql.gen.CompilerUtils;
import com.facebook.presto.util.ImmutableCollectors;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import io.airlift.slice.Slice;

import java.lang.invoke.MethodHandle;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.facebook.presto.byteCode.Access.FINAL;
import static com.facebook.presto.byteCode.Access.PRIVATE;
import static com.facebook.presto.byteCode.Access.PUBLIC;
import static com.facebook.presto.byteCode.Access.STATIC;
import static com.facebook.presto.byteCode.Access.a;
import static com.facebook.presto.byteCode.Parameter.arg;
import static com.facebook.presto.byteCode.ParameterizedType.type;
import static com.facebook.presto.metadata.Signature.internalFunction;
import static com.facebook.presto.metadata.Signature.orderableTypeParameter;
import static com.facebook.presto.spi.StandardErrorCode.INTERNAL_ERROR;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
import static com.facebook.presto.sql.gen.CompilerUtils.defineClass;
import static com.facebook.presto.sql.gen.SqlTypeByteCodeExpression.constantType;
import static com.facebook.presto.util.Reflection.methodHandle;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;

public final class Least extends ParametricScalar {
    public static final Least LEAST = new Least();
    private static final Signature SIGNATURE = new Signature("least", ImmutableList.of(orderableTypeParameter("E")),
            "E", ImmutableList.of("E"), true, false);

    @Override
    public Signature getSignature() {
        return SIGNATURE;
    }

    @Override
    public boolean isHidden() {
        return false;
    }

    @Override
    public boolean isDeterministic() {
        return true;
    }

    @Override
    public String getDescription() {
        return "get the smallest of the given values";
    }

    @Override
    public FunctionInfo specialize(Map<String, Type> types, int arity, TypeManager typeManager,
            FunctionRegistry functionRegistry) {
        Type type = types.get("E");
        checkArgument(type.isOrderable(), "Type must be orderable");

        ImmutableList.Builder<Class<?>> builder = ImmutableList.builder();
        for (int i = 0; i < arity; i++) {
            builder.add(type.getJavaType());
        }
        ImmutableList<Class<?>> stackTypes = builder.build();

        Class<?> clazz = generateLeast(stackTypes, type);
        MethodHandle methodHandle = methodHandle(clazz, "least",
                stackTypes.toArray(new Class<?>[stackTypes.size()]));
        List<Boolean> nullableParameters = ImmutableList.copyOf(Collections.nCopies(stackTypes.size(), false));

        Signature specializedSignature = internalFunction(SIGNATURE.getName(), type.getTypeSignature(),
                Collections.nCopies(arity, type.getTypeSignature()));
        return new FunctionInfo(specializedSignature, getDescription(), isHidden(), methodHandle, isDeterministic(),
                false, nullableParameters);
    }

    public static void checkNotNaN(double value) {
        if (Double.isNaN(value)) {
            throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid argument to least(): NaN");
        }
    }

    private static Class<?> generateLeast(List<Class<?>> nativeContainerTypes, Type type) {
        List<String> nativeContainerTypeNames = nativeContainerTypes.stream().map(Class::getSimpleName)
                .collect(ImmutableCollectors.toImmutableList());

        ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL),
                CompilerUtils.makeClassName(Joiner.on("").join(nativeContainerTypeNames) + "Least"),
                type(Object.class));

        definition.declareDefaultConstructor(a(PRIVATE));

        ImmutableList.Builder<Parameter> parameters = ImmutableList.builder();
        for (int i = 0; i < nativeContainerTypes.size(); i++) {
            Class<?> nativeContainerType = nativeContainerTypes.get(i);
            parameters.add(arg("arg" + i, nativeContainerType));
        }

        MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "least",
                type(nativeContainerTypes.get(0)), parameters.build());
        Scope scope = method.getScope();
        Block body = method.getBody();

        Variable typeVariable = scope.declareVariable(Type.class, "typeVariable");
        CallSiteBinder binder = new CallSiteBinder();
        body.comment("typeVariable = type;").append(constantType(binder, type)).putVariable(typeVariable);

        for (int i = 0; i < nativeContainerTypes.size(); i++) {
            Class<?> nativeContainerType = nativeContainerTypes.get(i);
            Variable currentBlock = scope.declareVariable(com.facebook.presto.spi.block.Block.class, "block" + i);
            Variable blockBuilder = scope.declareVariable(BlockBuilder.class, "blockBuilder" + i);
            Block buildBlock = new Block()
                    .comment("blockBuilder%d = typeVariable.createBlockBuilder(new BlockBuilderStatus(), 1, 32);",
                            i)
                    .getVariable(typeVariable).newObject(BlockBuilderStatus.class).dup()
                    .invokeConstructor(BlockBuilderStatus.class).push(1).push(32)
                    .invokeInterface(Type.class, "createBlockBuilder", BlockBuilder.class, BlockBuilderStatus.class,
                            int.class, int.class)
                    .putVariable(blockBuilder);

            String writeMethodName;
            if (nativeContainerType == long.class) {
                writeMethodName = "writeLong";
            } else if (nativeContainerType == boolean.class) {
                writeMethodName = "writeBoolean";
            } else if (nativeContainerType == double.class) {
                writeMethodName = "writeDouble";
            } else if (nativeContainerType == Slice.class) {
                writeMethodName = "writeSlice";
            } else {
                throw new PrestoException(INTERNAL_ERROR,
                        format("Unexpected type %s", nativeContainerType.getName()));
            }

            if (type.getTypeSignature().getBase().equals(StandardTypes.DOUBLE)) {
                buildBlock.append(scope.getVariable("arg" + i)).invokeStatic(Least.class, "checkNotNaN", void.class,
                        double.class);
            }

            Block writeBlock = new Block().comment("typeVariable.%s(blockBuilder%d, arg%d);", writeMethodName, i, i)
                    .getVariable(typeVariable).getVariable(blockBuilder).append(scope.getVariable("arg" + i))
                    .invokeInterface(Type.class, writeMethodName, void.class, BlockBuilder.class,
                            nativeContainerType);

            buildBlock.append(writeBlock);

            Block storeBlock = new Block().comment("block%d = blockBuilder%d.build();", i, i)
                    .getVariable(blockBuilder)
                    .invokeInterface(BlockBuilder.class, "build", com.facebook.presto.spi.block.Block.class)
                    .putVariable(currentBlock);
            buildBlock.append(storeBlock);
            body.append(buildBlock);
        }

        Variable leastVariable = scope.declareVariable(nativeContainerTypes.get(0), "least");
        Variable leastBlockVariable = scope.declareVariable(com.facebook.presto.spi.block.Block.class,
                "leastBlock");

        body.comment("least = arg0; leastBlock = block0;").append(scope.getVariable("arg0"))
                .putVariable(leastVariable).append(scope.getVariable("block0")).putVariable(leastBlockVariable);

        for (int i = 1; i < nativeContainerTypes.size(); i++) {
            IfStatement ifStatement = new IfStatement("if (type.compareTo(leastBlock, 0, block" + i + ", 0) < 0)");

            ifStatement.condition().getVariable(typeVariable).getVariable(leastBlockVariable).push(0)
                    .append(scope.getVariable("block" + i)).push(0)
                    .invokeInterface(Type.class, "compareTo", int.class, com.facebook.presto.spi.block.Block.class,
                            int.class, com.facebook.presto.spi.block.Block.class, int.class)
                    .push(0)
                    .invokeStatic(CompilerOperations.class, "lessThan", boolean.class, int.class, int.class);

            ifStatement.ifFalse().append(scope.getVariable("arg" + i)).putVariable(leastVariable)
                    .append(scope.getVariable("block" + i)).putVariable(leastBlockVariable);

            body.append(ifStatement);
        }

        body.comment("return least;").getVariable(leastVariable).ret(nativeContainerTypes.get(0));

        return defineClass(definition, Object.class, binder.getBindings(),
                new DynamicClassLoader(Least.class.getClassLoader()));
    }
}