/* * 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.sql; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; import com.facebook.presto.spi.ConnectorSession; import com.google.common.base.Defaults; import com.google.common.base.Throwables; import java.lang.invoke.MethodHandle; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; public class FunctionInvoker { private final FunctionRegistry registry; public FunctionInvoker(FunctionRegistry registry) { this.registry = requireNonNull(registry, "registry is null"); } public Object invoke(Signature function, ConnectorSession session, Object... arguments) { return invoke(function, session, Arrays.asList(arguments)); } /** * Arguments must be the native container type for the corresponding SQL types. * * Returns a value in the native container type corresponding to the declared SQL return type */ public Object invoke(Signature function, ConnectorSession session, List<Object> arguments) { ScalarFunctionImplementation implementation = registry.getScalarFunctionImplementation(function); MethodHandle method = implementation.getMethodHandle(); List<Object> actualArguments = new ArrayList<>(arguments.size() + 1); Iterator<Object> iterator = arguments.iterator(); for (int i = 0; i < method.type().parameterCount(); i++) { Class<?> parameterType = method.type().parameterType(i); if (parameterType == ConnectorSession.class) { actualArguments.add(session); } else { checkArgument(iterator.hasNext(), "Not enough arguments provided for method: %s", method.type()); Object argument = iterator.next(); if (implementation.getNullFlags().get(i)) { boolean isNull = argument == null; if (isNull) { argument = Defaults.defaultValue(parameterType); } actualArguments.add(argument); actualArguments.add(isNull); // Skip the next method parameter which is marked @IsNull i++; } else { actualArguments.add(argument); } } } checkArgument(!iterator.hasNext(), "Too many arguments provided for method: %s", method.type()); try { return method.invokeWithArguments(actualArguments); } catch (Throwable throwable) { throw Throwables.propagate(throwable); } } }