| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.druid.math.expr; |
| |
| import com.google.common.collect.ImmutableSet; |
| import org.apache.druid.common.config.NullHandling; |
| import org.apache.druid.java.util.common.DateTimes; |
| import org.apache.druid.java.util.common.HumanReadableBytes; |
| import org.apache.druid.java.util.common.IAE; |
| import org.apache.druid.java.util.common.RE; |
| import org.apache.druid.java.util.common.StringUtils; |
| import org.apache.druid.java.util.common.UOE; |
| import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor; |
| import org.apache.druid.math.expr.vector.ExprVectorProcessor; |
| import org.apache.druid.math.expr.vector.VectorMathProcessors; |
| import org.apache.druid.math.expr.vector.VectorProcessors; |
| import org.apache.druid.math.expr.vector.VectorStringProcessors; |
| import org.joda.time.DateTime; |
| import org.joda.time.DateTimeZone; |
| import org.joda.time.format.DateTimeFormat; |
| |
| import javax.annotation.Nullable; |
| import java.math.BigDecimal; |
| import java.math.RoundingMode; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.function.BinaryOperator; |
| import java.util.function.DoubleBinaryOperator; |
| import java.util.function.LongBinaryOperator; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| /** |
| * Base interface describing the mechanism used to evaluate a {@link FunctionExpr}. All {@link Function} implementations |
| * are immutable. |
| * |
| * Do NOT remove "unused" members in this class. They are used by generated Antlr |
| */ |
| @SuppressWarnings("unused") |
| public interface Function |
| { |
| /** |
| * Name of the function. |
| */ |
| String name(); |
| |
| /** |
| * Evaluate the function, given a list of arguments and a set of bindings to provide values for {@link IdentifierExpr}. |
| */ |
| ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings); |
| |
| /** |
| * Given a list of arguments to this {@link Function}, get the set of arguments that must evaluate to a scalar value |
| */ |
| default Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return ImmutableSet.copyOf(args); |
| } |
| |
| /** |
| * Given a list of arguments to this {@link Function}, get the set of arguments that must evaluate to an array |
| * value |
| */ |
| default Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return Collections.emptySet(); |
| } |
| |
| /** |
| * Returns true if a function expects any array arguments |
| */ |
| default boolean hasArrayInputs() |
| { |
| return false; |
| } |
| |
| /** |
| * Returns true if function produces an array. All {@link Function} implementations are expected to |
| * exclusively produce either scalar or array values. |
| */ |
| default boolean hasArrayOutput() |
| { |
| return false; |
| } |
| |
| /** |
| * Validate function arguments |
| */ |
| void validateArguments(List<Expr> args); |
| |
| /** |
| * Compute the output type of this function for a given set of argument expression inputs. |
| * |
| * @see Expr#getOutputType |
| */ |
| @Nullable |
| ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args); |
| |
| /** |
| * Check if a function can be 'vectorized', for a given set of {@link Expr} inputs. If this method returns true, |
| * {@link #asVectorProcessor} is expected to produce a {@link ExprVectorProcessor} which can evaluate values in |
| * batches to use with vectorized query engines. |
| * |
| * @see Expr#canVectorize(Expr.InputBindingInspector) |
| * @see ApplyFunction#canVectorize(Expr.InputBindingInspector, Expr, List) |
| */ |
| default boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return false; |
| } |
| |
| /** |
| * Builds a 'vectorized' function expression processor, that can build vectorized processors for its input values |
| * using {@link Expr#buildVectorized}, for use in vectorized query engines. |
| * |
| * @see Expr#buildVectorized(Expr.VectorInputBindingInspector) |
| * @see ApplyFunction#asVectorProcessor(Expr.VectorInputBindingInspector, Expr, List) |
| */ |
| default <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| throw new UOE("%s is not vectorized", name()); |
| } |
| |
| /** |
| * Base class for a single variable input {@link Function} implementation |
| */ |
| abstract class UnivariateFunction implements Function |
| { |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| Expr expr = args.get(0); |
| return eval(expr.eval(bindings)); |
| } |
| |
| protected abstract ExprEval eval(ExprEval param); |
| } |
| |
| /** |
| * Base class for a 2 variable input {@link Function} implementation |
| */ |
| abstract class BivariateFunction implements Function |
| { |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2) { |
| throw new IAE("Function[%s] needs 2 arguments", name()); |
| } |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| Expr expr1 = args.get(0); |
| Expr expr2 = args.get(1); |
| return eval(expr1.eval(bindings), expr2.eval(bindings)); |
| } |
| |
| protected abstract ExprEval eval(ExprEval x, ExprEval y); |
| } |
| |
| /** |
| * Base class for a single variable input mathematical {@link Function}, with specialized 'eval' implementations that |
| * that operate on primitive number types |
| */ |
| abstract class UnivariateMathFunction extends UnivariateFunction |
| { |
| @Override |
| protected final ExprEval eval(ExprEval param) |
| { |
| if (NullHandling.sqlCompatible() && param.isNumericNull()) { |
| return ExprEval.of(null); |
| } |
| if (param.type() == ExprType.LONG) { |
| return eval(param.asLong()); |
| } else if (param.type() == ExprType.DOUBLE) { |
| return eval(param.asDouble()); |
| } |
| return ExprEval.of(null); |
| } |
| |
| protected ExprEval eval(long param) |
| { |
| return eval((double) param); |
| } |
| |
| protected ExprEval eval(double param) |
| { |
| return eval((long) param); |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return args.get(0).getOutputType(inspector); |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return inspector.areNumeric(args) && inspector.canVectorize(args); |
| } |
| } |
| |
| /** |
| * Many math functions always output a {@link Double} primitive, regardless of input type. |
| */ |
| abstract class DoubleUnivariateMathFunction extends UnivariateMathFunction |
| { |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.DOUBLE; |
| } |
| } |
| |
| /** |
| * Base class for a 2 variable input mathematical {@link Function}, with specialized 'eval' implementations that |
| * operate on primitive number types |
| */ |
| abstract class BivariateMathFunction extends BivariateFunction |
| { |
| @Override |
| protected final ExprEval eval(ExprEval x, ExprEval y) |
| { |
| // match the logic of BinaryEvalOpExprBase.eval, except there is no string handling so both strings is also null |
| if (NullHandling.sqlCompatible() && (x.value() == null || y.value() == null)) { |
| return ExprEval.of(null); |
| } |
| |
| ExprType type = ExprTypeConversion.autoDetect(x, y); |
| switch (type) { |
| case STRING: |
| return ExprEval.of(null); |
| case LONG: |
| return eval(x.asLong(), y.asLong()); |
| case DOUBLE: |
| default: |
| return eval(x.asDouble(), y.asDouble()); |
| } |
| } |
| |
| protected ExprEval eval(long x, long y) |
| { |
| return eval((double) x, (double) y); |
| } |
| |
| protected ExprEval eval(double x, double y) |
| { |
| return eval((long) x, (long) y); |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprTypeConversion.function( |
| args.get(0).getOutputType(inspector), |
| args.get(1).getOutputType(inspector) |
| ); |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return inspector.areNumeric(args) && inspector.canVectorize(args); |
| } |
| } |
| |
| /** |
| * Many math functions always output a {@link Double} primitive, regardless of input type. |
| */ |
| abstract class DoubleBivariateMathFunction extends BivariateMathFunction |
| { |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.DOUBLE; |
| } |
| } |
| |
| abstract class BivariateBitwiseMathFunction extends BivariateFunction |
| { |
| @Override |
| protected final ExprEval eval(ExprEval x, ExprEval y) |
| { |
| // this is a copy of the logic of BivariateMathFunction for string handling, which itself is a |
| // remix of BinaryEvalOpExprBase.eval modified so that string inputs are always null outputs |
| if (NullHandling.sqlCompatible() && (x.value() == null || y.value() == null)) { |
| return ExprEval.of(null); |
| } |
| |
| ExprType type = ExprTypeConversion.autoDetect(x, y); |
| if (type == ExprType.STRING) { |
| return ExprEval.of(null); |
| } |
| return eval(x.asLong(), y.asLong()); |
| } |
| |
| protected abstract ExprEval eval(long x, long y); |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return inspector.areNumeric(args) && inspector.canVectorize(args); |
| } |
| } |
| |
| /** |
| * Base class for a 2 variable input {@link Function} whose first argument is a {@link ExprType#STRING} and second |
| * argument is {@link ExprType#LONG} |
| */ |
| abstract class StringLongFunction extends BivariateFunction |
| { |
| @Override |
| protected final ExprEval eval(ExprEval x, ExprEval y) |
| { |
| if (x.type() != ExprType.STRING || y.type() != ExprType.LONG) { |
| throw new IAE( |
| "Function[%s] needs a string as first argument and an integer as second argument", |
| name() |
| ); |
| } |
| return eval(x.asString(), y.asInt()); |
| } |
| |
| protected abstract ExprEval eval(@Nullable String x, int y); |
| } |
| |
| /** |
| * {@link Function} that takes 1 array operand and 1 scalar operand |
| */ |
| abstract class ArrayScalarFunction implements Function |
| { |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2) { |
| throw new IAE("Function[%s] needs 2 argument", name()); |
| } |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return ImmutableSet.of(getScalarArgument(args)); |
| } |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return ImmutableSet.of(getArrayArgument(args)); |
| } |
| |
| @Override |
| public boolean hasArrayInputs() |
| { |
| return true; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval arrayExpr = getArrayArgument(args).eval(bindings); |
| final ExprEval scalarExpr = getScalarArgument(args).eval(bindings); |
| if (arrayExpr.asArray() == null) { |
| return ExprEval.of(null); |
| } |
| return doApply(arrayExpr, scalarExpr); |
| } |
| |
| Expr getScalarArgument(List<Expr> args) |
| { |
| return args.get(1); |
| } |
| |
| Expr getArrayArgument(List<Expr> args) |
| { |
| return args.get(0); |
| } |
| |
| abstract ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr); |
| } |
| |
| /** |
| * {@link Function} that takes 2 array operands |
| */ |
| abstract class ArraysFunction implements Function |
| { |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2) { |
| throw new IAE("Function[%s] needs 2 arguments", name()); |
| } |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return ImmutableSet.copyOf(args); |
| } |
| |
| @Override |
| public boolean hasArrayInputs() |
| { |
| return true; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval arrayExpr1 = args.get(0).eval(bindings); |
| final ExprEval arrayExpr2 = args.get(1).eval(bindings); |
| |
| if (arrayExpr1.asArray() == null) { |
| return arrayExpr1; |
| } |
| if (arrayExpr2.asArray() == null) { |
| return arrayExpr2; |
| } |
| |
| return doApply(arrayExpr1, arrayExpr2); |
| } |
| |
| abstract ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr); |
| } |
| |
| /** |
| * Scaffolding for a 2 argument {@link Function} which accepts one array and one scalar input and adds the scalar |
| * input to the array in some way. |
| */ |
| abstract class ArrayAddElementFunction extends ArrayScalarFunction |
| { |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType arrayType = getArrayArgument(args).getOutputType(inspector); |
| return Optional.ofNullable(ExprType.asArrayType(arrayType)).orElse(arrayType); |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| switch (arrayExpr.type()) { |
| case STRING: |
| case STRING_ARRAY: |
| return ExprEval.ofStringArray(add(arrayExpr.asStringArray(), scalarExpr.asString()).toArray(String[]::new)); |
| case LONG: |
| case LONG_ARRAY: |
| return ExprEval.ofLongArray( |
| add( |
| arrayExpr.asLongArray(), |
| scalarExpr.isNumericNull() ? null : scalarExpr.asLong() |
| ).toArray(Long[]::new) |
| ); |
| case DOUBLE: |
| case DOUBLE_ARRAY: |
| return ExprEval.ofDoubleArray( |
| add( |
| arrayExpr.asDoubleArray(), |
| scalarExpr.isNumericNull() ? null : scalarExpr.asDouble() |
| ).toArray(Double[]::new) |
| ); |
| } |
| |
| throw new RE("Unable to add to unknown array type %s", arrayExpr.type()); |
| } |
| |
| abstract <T> Stream<T> add(T[] array, @Nullable T val); |
| } |
| |
| /** |
| * Base scaffolding for functions which accept 2 array arguments and combine them in some way |
| */ |
| abstract class ArraysMergeFunction extends ArraysFunction |
| { |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return ImmutableSet.copyOf(args); |
| } |
| |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType arrayType = args.get(0).getOutputType(inspector); |
| return Optional.ofNullable(ExprType.asArrayType(arrayType)).orElse(arrayType); |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr) |
| { |
| final Object[] array1 = lhsExpr.asArray(); |
| final Object[] array2 = rhsExpr.asArray(); |
| |
| if (array1 == null) { |
| return ExprEval.of(null); |
| } |
| if (array2 == null) { |
| return lhsExpr; |
| } |
| |
| switch (lhsExpr.type()) { |
| case STRING: |
| case STRING_ARRAY: |
| return ExprEval.ofStringArray( |
| merge(lhsExpr.asStringArray(), rhsExpr.asStringArray()).toArray(String[]::new) |
| ); |
| case LONG: |
| case LONG_ARRAY: |
| return ExprEval.ofLongArray( |
| merge(lhsExpr.asLongArray(), rhsExpr.asLongArray()).toArray(Long[]::new) |
| ); |
| case DOUBLE: |
| case DOUBLE_ARRAY: |
| return ExprEval.ofDoubleArray( |
| merge(lhsExpr.asDoubleArray(), rhsExpr.asDoubleArray()).toArray(Double[]::new) |
| ); |
| } |
| throw new RE("Unable to concatenate to unknown type %s", lhsExpr.type()); |
| } |
| |
| abstract <T> Stream<T> merge(T[] array1, T[] array2); |
| } |
| |
| abstract class ReduceFunction implements Function |
| { |
| private final DoubleBinaryOperator doubleReducer; |
| private final LongBinaryOperator longReducer; |
| private final BinaryOperator<String> stringReducer; |
| |
| ReduceFunction( |
| DoubleBinaryOperator doubleReducer, |
| LongBinaryOperator longReducer, |
| BinaryOperator<String> stringReducer |
| ) |
| { |
| this.doubleReducer = doubleReducer; |
| this.longReducer = longReducer; |
| this.stringReducer = stringReducer; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| // anything goes |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType outputType = ExprType.LONG; |
| for (Expr expr : args) { |
| outputType = ExprTypeConversion.function(outputType, expr.getOutputType(inspector)); |
| } |
| return outputType; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| if (args.isEmpty()) { |
| return ExprEval.of(null); |
| } |
| |
| // evaluate arguments and collect output type |
| List<ExprEval<?>> evals = new ArrayList<>(); |
| ExprType outputType = ExprType.LONG; |
| |
| for (Expr expr : args) { |
| ExprEval<?> exprEval = expr.eval(bindings); |
| ExprType exprType = exprEval.type(); |
| |
| if (isValidType(exprType)) { |
| outputType = ExprTypeConversion.function(outputType, exprType); |
| } |
| |
| if (exprEval.value() != null) { |
| evals.add(exprEval); |
| } |
| } |
| |
| if (evals.isEmpty()) { |
| // The GREATEST/LEAST functions are not in the SQL standard. Emulate the behavior of postgres (return null if |
| // all expressions are null, otherwise skip null values) since it is used as a base for a wide number of |
| // databases. This also matches the behavior the long/double greatest/least post aggregators. Some other |
| // databases (e.g., MySQL) return null if any expression is null. |
| // https://www.postgresql.org/docs/9.5/functions-conditional.html |
| // https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#function_least |
| return ExprEval.of(null); |
| } |
| |
| switch (outputType) { |
| case DOUBLE: |
| //noinspection OptionalGetWithoutIsPresent (empty list handled earlier) |
| return ExprEval.of(evals.stream().mapToDouble(ExprEval::asDouble).reduce(doubleReducer).getAsDouble()); |
| case LONG: |
| //noinspection OptionalGetWithoutIsPresent (empty list handled earlier) |
| return ExprEval.of(evals.stream().mapToLong(ExprEval::asLong).reduce(longReducer).getAsLong()); |
| default: |
| //noinspection OptionalGetWithoutIsPresent (empty list handled earlier) |
| return ExprEval.of(evals.stream().map(ExprEval::asString).reduce(stringReducer).get()); |
| } |
| } |
| |
| private boolean isValidType(ExprType exprType) |
| { |
| switch (exprType) { |
| case DOUBLE: |
| case LONG: |
| case STRING: |
| return true; |
| default: |
| throw new IAE("Function[%s] does not accept %s types", name(), exprType); |
| } |
| } |
| } |
| |
| // ------------------------------ implementations ------------------------------ |
| |
| class ParseLong implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "parse_long"; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1 && args.size() != 2) { |
| throw new IAE("Function[%s] needs 1 or 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final int radix = args.size() == 1 ? 10 : args.get(1).eval(bindings).asInt(); |
| |
| final String input = NullHandling.nullToEmptyIfNeeded(args.get(0).eval(bindings).asString()); |
| if (input == null) { |
| return ExprEval.ofLong(null); |
| } |
| |
| final long retVal; |
| try { |
| if (radix == 16 && (input.startsWith("0x") || input.startsWith("0X"))) { |
| // Strip leading 0x from hex strings. |
| retVal = Long.parseLong(input.substring(2), radix); |
| } else { |
| retVal = Long.parseLong(input, radix); |
| } |
| } |
| catch (NumberFormatException e) { |
| return ExprEval.ofLong(null); |
| } |
| |
| return ExprEval.of(retVal); |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return (args.size() == 1 || (args.get(1).isLiteral() && args.get(1).getLiteralValue() instanceof Number)) && |
| inspector.canVectorize(args); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| if (args.size() == 1 || args.get(1).isLiteral()) { |
| final int radix = args.size() == 1 ? 10 : ((Number) args.get(1).getLiteralValue()).intValue(); |
| return VectorProcessors.parseLong(inspector, args.get(0), radix); |
| } |
| // only single argument and 2 argument where the radix is constant is currently implemented |
| // the canVectorize check should prevent this from happening, but explode just in case |
| throw Exprs.cannotVectorize(this); |
| } |
| } |
| |
| class Pi implements Function |
| { |
| private static final double PI = Math.PI; |
| |
| @Override |
| public String name() |
| { |
| return "pi"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| return ExprEval.of(PI); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() > 0) { |
| throw new IAE("Function[%s] needs 0 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.DOUBLE; |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return true; |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorProcessors.constantDouble(PI, inspector.getMaxVectorSize()); |
| } |
| } |
| |
| class Abs extends UnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "abs"; |
| } |
| |
| @Override |
| protected ExprEval eval(long param) |
| { |
| return ExprEval.of(Math.abs(param)); |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.abs(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.abs(inspector, args.get(0)); |
| } |
| } |
| |
| class Acos extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "acos"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.acos(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.acos(inspector, args.get(0)); |
| } |
| } |
| |
| class Asin extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "asin"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.asin(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.asin(inspector, args.get(0)); |
| } |
| } |
| |
| class Atan extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "atan"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.atan(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.atan(inspector, args.get(0)); |
| } |
| } |
| |
| class BitwiseComplement extends UnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseComplement"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| protected ExprEval eval(long param) |
| { |
| return ExprEval.of(~param); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseComplement(inspector, args.get(0)); |
| } |
| } |
| |
| class BitwiseConvertLongBitsToDouble extends UnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseConvertLongBitsToDouble"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType type = args.get(0).getOutputType(inspector); |
| if (type == null) { |
| return null; |
| } |
| return ExprType.DOUBLE; |
| } |
| |
| @Override |
| protected ExprEval eval(long param) |
| { |
| return ExprEval.of(Double.longBitsToDouble(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseConvertLongBitsToDouble(inspector, args.get(0)); |
| } |
| } |
| |
| class BitwiseConvertDoubleToLongBits extends UnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseConvertDoubleToLongBits"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType type = args.get(0).getOutputType(inspector); |
| if (type == null) { |
| return null; |
| } |
| return ExprType.LONG; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Double.doubleToLongBits(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseConvertDoubleToLongBits(inspector, args.get(0)); |
| } |
| } |
| |
| class BitwiseAnd extends BivariateBitwiseMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseAnd"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(x & y); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseAnd(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class BitwiseOr extends BivariateBitwiseMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseOr"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(x | y); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseOr(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class BitwiseShiftLeft extends BivariateBitwiseMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseShiftLeft"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(x << y); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseShiftLeft(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class BitwiseShiftRight extends BivariateBitwiseMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseShiftRight"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(x >> y); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseShiftRight(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class BitwiseXor extends BivariateBitwiseMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "bitwiseXor"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(x ^ y); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.bitwiseXor(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Cbrt extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "cbrt"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.cbrt(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.cbrt(inspector, args.get(0)); |
| } |
| } |
| |
| class Ceil extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "ceil"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.ceil(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.ceil(inspector, args.get(0)); |
| } |
| } |
| |
| class Cos extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "cos"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.cos(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.cos(inspector, args.get(0)); |
| } |
| } |
| |
| class Cosh extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "cosh"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.cosh(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.cosh(inspector, args.get(0)); |
| } |
| } |
| |
| class Cot extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "cot"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.cos(param) / Math.sin(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.cot(inspector, args.get(0)); |
| } |
| } |
| |
| class Div extends BivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "div"; |
| } |
| |
| @Override |
| protected ExprEval eval(final long x, final long y) |
| { |
| return ExprEval.of(x / y); |
| } |
| |
| @Override |
| protected ExprEval eval(final double x, final double y) |
| { |
| return ExprEval.of((long) (x / y)); |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprTypeConversion.integerMathFunction( |
| args.get(0).getOutputType(inspector), |
| args.get(1).getOutputType(inspector) |
| ); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.longDivide(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Exp extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "exp"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.exp(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.exp(inspector, args.get(0)); |
| } |
| } |
| |
| class Expm1 extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "expm1"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.expm1(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.expm1(inspector, args.get(0)); |
| } |
| } |
| |
| class Floor extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "floor"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.floor(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.floor(inspector, args.get(0)); |
| } |
| } |
| |
| class GetExponent extends UnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "getExponent"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.getExponent(param)); |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.getExponent(inspector, args.get(0)); |
| } |
| } |
| |
| class Log extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "log"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.log(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.log(inspector, args.get(0)); |
| } |
| } |
| |
| class Log10 extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "log10"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.log10(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.log10(inspector, args.get(0)); |
| } |
| } |
| |
| class Log1p extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "log1p"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.log1p(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.log1p(inspector, args.get(0)); |
| } |
| } |
| |
| class NextUp extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "nextUp"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.nextUp(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.nextUp(inspector, args.get(0)); |
| } |
| } |
| |
| class Rint extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "rint"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.rint(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.rint(inspector, args.get(0)); |
| } |
| } |
| |
| class Round implements Function |
| { |
| //CHECKSTYLE.OFF: Regexp |
| private static final BigDecimal MAX_FINITE_VALUE = BigDecimal.valueOf(Double.MAX_VALUE); |
| private static final BigDecimal MIN_FINITE_VALUE = BigDecimal.valueOf(-1 * Double.MAX_VALUE); |
| //CHECKSTYLE.ON: Regexp |
| |
| @Override |
| public String name() |
| { |
| return "round"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| ExprEval value1 = args.get(0).eval(bindings); |
| |
| if (NullHandling.sqlCompatible() && value1.isNumericNull()) { |
| return ExprEval.of(null); |
| } |
| |
| if (value1.type() != ExprType.LONG && value1.type() != ExprType.DOUBLE) { |
| throw new IAE( |
| "The first argument to the function[%s] should be integer or double type but got the type: %s", |
| name(), |
| value1.type() |
| ); |
| } |
| |
| if (args.size() == 1) { |
| return eval(value1); |
| } else { |
| ExprEval value2 = args.get(1).eval(bindings); |
| if (value2.type() != ExprType.LONG) { |
| throw new IAE( |
| "The second argument to the function[%s] should be integer type but got the type: %s", |
| name(), |
| value2.type() |
| ); |
| } |
| return eval(value1, value2.asInt()); |
| } |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1 && args.size() != 2) { |
| throw new IAE("Function[%s] needs 1 or 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return args.get(0).getOutputType(inspector); |
| } |
| |
| private ExprEval eval(ExprEval param) |
| { |
| return eval(param, 0); |
| } |
| |
| private ExprEval eval(ExprEval param, int scale) |
| { |
| if (param.type() == ExprType.LONG) { |
| return ExprEval.of(BigDecimal.valueOf(param.asLong()).setScale(scale, RoundingMode.HALF_UP).longValue()); |
| } else if (param.type() == ExprType.DOUBLE) { |
| BigDecimal decimal = safeGetFromDouble(param.asDouble()); |
| return ExprEval.of(decimal.setScale(scale, RoundingMode.HALF_UP).doubleValue()); |
| } else { |
| return ExprEval.of(null); |
| } |
| } |
| |
| /** |
| * Converts non-finite doubles to BigDecimal values instead of throwing a NumberFormatException. |
| */ |
| private static BigDecimal safeGetFromDouble(double val) |
| { |
| if (Double.isNaN(val)) { |
| return BigDecimal.ZERO; |
| } else if (val == Double.POSITIVE_INFINITY) { |
| return MAX_FINITE_VALUE; |
| } else if (val == Double.NEGATIVE_INFINITY) { |
| return MIN_FINITE_VALUE; |
| } |
| return BigDecimal.valueOf(val); |
| } |
| } |
| |
| class Signum extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "signum"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.signum(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.signum(inspector, args.get(0)); |
| } |
| } |
| |
| class Sin extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "sin"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.sin(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.sin(inspector, args.get(0)); |
| } |
| } |
| |
| class Sinh extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "sinh"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.sinh(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.sinh(inspector, args.get(0)); |
| } |
| } |
| |
| class Sqrt extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "sqrt"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.sqrt(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.sqrt(inspector, args.get(0)); |
| } |
| } |
| |
| class Tan extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "tan"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.tan(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.tan(inspector, args.get(0)); |
| } |
| } |
| |
| class Tanh extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "tanh"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.tanh(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.tanh(inspector, args.get(0)); |
| } |
| } |
| |
| class ToDegrees extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "toDegrees"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.toDegrees(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.toDegrees(inspector, args.get(0)); |
| } |
| } |
| |
| class ToRadians extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "toRadians"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.toRadians(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.toRadians(inspector, args.get(0)); |
| } |
| } |
| |
| class Ulp extends DoubleUnivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "ulp"; |
| } |
| |
| @Override |
| protected ExprEval eval(double param) |
| { |
| return ExprEval.of(Math.ulp(param)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.ulp(inspector, args.get(0)); |
| } |
| } |
| |
| class Atan2 extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "atan2"; |
| } |
| |
| @Override |
| protected ExprEval eval(double y, double x) |
| { |
| return ExprEval.of(Math.atan2(y, x)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.atan2(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class CopySign extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "copySign"; |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.copySign(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.copySign(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Hypot extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "hypot"; |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.hypot(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.hypot(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Remainder extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "remainder"; |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.IEEEremainder(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.remainder(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Max extends BivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "max"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(Math.max(x, y)); |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.max(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.max(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Min extends BivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "min"; |
| } |
| |
| @Override |
| protected ExprEval eval(long x, long y) |
| { |
| return ExprEval.of(Math.min(x, y)); |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.min(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.min(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class NextAfter extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "nextAfter"; |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.nextAfter(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.nextAfter(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Pow extends DoubleBivariateMathFunction |
| { |
| @Override |
| public String name() |
| { |
| return "pow"; |
| } |
| |
| @Override |
| protected ExprEval eval(double x, double y) |
| { |
| return ExprEval.of(Math.pow(x, y)); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.doublePower(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class Scalb extends BivariateFunction |
| { |
| @Override |
| public String name() |
| { |
| return "scalb"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.DOUBLE; |
| } |
| |
| @Override |
| protected ExprEval eval(ExprEval x, ExprEval y) |
| { |
| if (NullHandling.sqlCompatible() && (x.value() == null || y.value() == null)) { |
| return ExprEval.of(null); |
| } |
| |
| ExprType type = ExprTypeConversion.autoDetect(x, y); |
| switch (type) { |
| case STRING: |
| return ExprEval.of(null); |
| default: |
| return ExprEval.of(Math.scalb(x.asDouble(), y.asInt())); |
| } |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return inspector.areNumeric(args) && inspector.canVectorize(args); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return VectorMathProcessors.scalb(inspector, args.get(0), args.get(1)); |
| } |
| } |
| |
| class CastFunc extends BivariateFunction |
| { |
| @Override |
| public String name() |
| { |
| return "cast"; |
| } |
| |
| @Override |
| protected ExprEval eval(ExprEval x, ExprEval y) |
| { |
| if (NullHandling.sqlCompatible() && x.value() == null) { |
| return ExprEval.of(null); |
| } |
| ExprType castTo; |
| try { |
| castTo = ExprType.valueOf(StringUtils.toUpperCase(y.asString())); |
| } |
| catch (IllegalArgumentException e) { |
| throw new IAE("invalid type '%s'", y.asString()); |
| } |
| return x.castTo(castTo); |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| if (args.get(1).isLiteral()) { |
| ExprType castTo = ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); |
| switch (castTo) { |
| case LONG_ARRAY: |
| case DOUBLE_ARRAY: |
| case STRING_ARRAY: |
| return Collections.emptySet(); |
| default: |
| return ImmutableSet.of(args.get(0)); |
| } |
| } |
| // unknown cast, can't safely assume either way |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| if (args.get(1).isLiteral()) { |
| ExprType castTo = ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); |
| switch (castTo) { |
| case LONG: |
| case DOUBLE: |
| case STRING: |
| return Collections.emptySet(); |
| default: |
| return ImmutableSet.of(args.get(0)); |
| } |
| } |
| // unknown cast, can't safely assume either way |
| return Collections.emptySet(); |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| // can only know cast output type if cast to argument is constant |
| if (args.get(1).isLiteral()) { |
| return ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())); |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return args.get(0).canVectorize(inspector) && args.get(1).isLiteral(); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor(Expr.VectorInputBindingInspector inspector, List<Expr> args) |
| { |
| return CastToTypeVectorProcessor.cast( |
| args.get(0).buildVectorized(inspector), |
| ExprType.valueOf(StringUtils.toUpperCase(args.get(1).getLiteralValue().toString())) |
| ); |
| } |
| } |
| |
| class GreatestFunc extends ReduceFunction |
| { |
| public static final String NAME = "greatest"; |
| |
| public GreatestFunc() |
| { |
| super( |
| Math::max, |
| Math::max, |
| BinaryOperator.maxBy(Comparator.naturalOrder()) |
| ); |
| } |
| |
| @Override |
| public String name() |
| { |
| return NAME; |
| } |
| } |
| |
| class LeastFunc extends ReduceFunction |
| { |
| public static final String NAME = "least"; |
| |
| public LeastFunc() |
| { |
| super( |
| Math::min, |
| Math::min, |
| BinaryOperator.minBy(Comparator.naturalOrder()) |
| ); |
| } |
| |
| @Override |
| public String name() |
| { |
| return NAME; |
| } |
| } |
| |
| class ConditionFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "if"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| ExprEval x = args.get(0).eval(bindings); |
| return x.asBoolean() ? args.get(1).eval(bindings) : args.get(2).eval(bindings); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprTypeConversion.conditional(inspector, args.subList(1, 3)); |
| } |
| } |
| |
| /** |
| * "Searched CASE" function, similar to {@code CASE WHEN boolean_expr THEN result [ELSE else_result] END} in SQL. |
| */ |
| class CaseSearchedFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "case_searched"; |
| } |
| |
| @Override |
| public ExprEval apply(final List<Expr> args, final Expr.ObjectBinding bindings) |
| { |
| for (int i = 0; i < args.size(); i += 2) { |
| if (i == args.size() - 1) { |
| // ELSE else_result. |
| return args.get(i).eval(bindings); |
| } else if (args.get(i).eval(bindings).asBoolean()) { |
| // Matching WHEN boolean_expr THEN result |
| return args.get(i + 1).eval(bindings); |
| } |
| } |
| |
| return ExprEval.of(null); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() < 2) { |
| throw new IAE("Function[%s] must have at least 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| List<Expr> results = new ArrayList<>(); |
| for (int i = 1; i < args.size(); i += 2) { |
| results.add(args.get(i)); |
| } |
| // add else |
| results.add(args.get(args.size() - 1)); |
| return ExprTypeConversion.conditional(inspector, results); |
| } |
| } |
| |
| /** |
| * "Simple CASE" function, similar to {@code CASE expr WHEN value THEN result [ELSE else_result] END} in SQL. |
| */ |
| class CaseSimpleFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "case_simple"; |
| } |
| |
| @Override |
| public ExprEval apply(final List<Expr> args, final Expr.ObjectBinding bindings) |
| { |
| for (int i = 1; i < args.size(); i += 2) { |
| if (i == args.size() - 1) { |
| // ELSE else_result. |
| return args.get(i).eval(bindings); |
| } else if (new BinEqExpr("==", args.get(0), args.get(i)).eval(bindings).asBoolean()) { |
| // Matching WHEN value THEN result |
| return args.get(i + 1).eval(bindings); |
| } |
| } |
| |
| return ExprEval.of(null); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() < 3) { |
| throw new IAE("Function[%s] must have at least 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| List<Expr> results = new ArrayList<>(); |
| for (int i = 2; i < args.size(); i += 2) { |
| results.add(args.get(i)); |
| } |
| // add else |
| results.add(args.get(args.size() - 1)); |
| return ExprTypeConversion.conditional(inspector, results); |
| } |
| } |
| |
| class NvlFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "nvl"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval eval = args.get(0).eval(bindings); |
| return eval.value() == null ? args.get(1).eval(bindings) : eval; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2) { |
| throw new IAE("Function[%s] needs 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprTypeConversion.conditional(inspector, args); |
| } |
| } |
| |
| class IsNullFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "isnull"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval expr = args.get(0).eval(bindings); |
| return ExprEval.ofLongBoolean(expr.value() == null); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| } |
| |
| class IsNotNullFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "notnull"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval expr = args.get(0).eval(bindings); |
| return ExprEval.ofLongBoolean(expr.value() != null); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| } |
| |
| class ConcatFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "concat"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| if (args.size() == 0) { |
| return ExprEval.of(null); |
| } else { |
| // Pass first argument in to the constructor to provide StringBuilder a little extra sizing hint. |
| String first = NullHandling.nullToEmptyIfNeeded(args.get(0).eval(bindings).asString()); |
| if (first == null) { |
| // Result of concatenation is null if any of the Values is null. |
| // e.g. 'select CONCAT(null, "abc") as c;' will return null as per Standard SQL spec. |
| return ExprEval.of(null); |
| } |
| final StringBuilder builder = new StringBuilder(first); |
| for (int i = 1; i < args.size(); i++) { |
| final String s = NullHandling.nullToEmptyIfNeeded(args.get(i).eval(bindings).asString()); |
| if (s == null) { |
| // Result of concatenation is null if any of the Values is null. |
| // e.g. 'select CONCAT(null, "abc") as c;' will return null as per Standard SQL spec. |
| return ExprEval.of(null); |
| } else { |
| builder.append(s); |
| } |
| } |
| return ExprEval.of(builder.toString()); |
| } |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| // anything goes |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| public boolean canVectorize(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return inspector.areScalar(args) && inspector.canVectorize(args); |
| } |
| |
| @Override |
| public <T> ExprVectorProcessor<T> asVectorProcessor( |
| Expr.VectorInputBindingInspector inspector, |
| List<Expr> args |
| ) |
| { |
| return VectorStringProcessors.concat(inspector, args); |
| } |
| } |
| |
| class StrlenFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "strlen"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String arg = args.get(0).eval(bindings).asString(); |
| return arg == null ? ExprEval.ofLong(NullHandling.defaultLongValue()) : ExprEval.of(arg.length()); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| } |
| |
| class StringFormatFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "format"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String formatString = NullHandling.nullToEmptyIfNeeded(args.get(0).eval(bindings).asString()); |
| |
| if (formatString == null) { |
| return ExprEval.of(null); |
| } |
| |
| final Object[] formatArgs = new Object[args.size() - 1]; |
| for (int i = 1; i < args.size(); i++) { |
| formatArgs[i - 1] = args.get(i).eval(bindings).value(); |
| } |
| |
| return ExprEval.of(StringUtils.nonStrictFormat(formatString, formatArgs)); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() < 1) { |
| throw new IAE("Function[%s] needs 1 or more arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class StrposFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "strpos"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String haystack = NullHandling.nullToEmptyIfNeeded(args.get(0).eval(bindings).asString()); |
| final String needle = NullHandling.nullToEmptyIfNeeded(args.get(1).eval(bindings).asString()); |
| |
| if (haystack == null || needle == null) { |
| return ExprEval.of(null); |
| } |
| |
| final int fromIndex; |
| |
| if (args.size() >= 3) { |
| fromIndex = args.get(2).eval(bindings).asInt(); |
| } else { |
| fromIndex = 0; |
| } |
| |
| return ExprEval.of(haystack.indexOf(needle, fromIndex)); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() < 2 || args.size() > 3) { |
| throw new IAE("Function[%s] needs 2 or 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| } |
| |
| class SubstringFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "substring"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String arg = args.get(0).eval(bindings).asString(); |
| |
| if (arg == null) { |
| return ExprEval.of(null); |
| } |
| |
| // Behaves like SubstringDimExtractionFn, not SQL SUBSTRING |
| final int index = args.get(1).eval(bindings).asInt(); |
| final int length = args.get(2).eval(bindings).asInt(); |
| |
| if (index < arg.length()) { |
| if (length >= 0) { |
| return ExprEval.of(arg.substring(index, Math.min(index + length, arg.length()))); |
| } else { |
| return ExprEval.of(arg.substring(index)); |
| } |
| } else { |
| // If starting index of substring is greater then the length of string, the result will be a zero length string. |
| // e.g. 'select substring("abc", 4,5) as c;' will return an empty string |
| return ExprEval.of(NullHandling.defaultStringValue()); |
| } |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class RightFunc extends StringLongFunction |
| { |
| @Override |
| public String name() |
| { |
| return "right"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| protected ExprEval eval(@Nullable String x, int y) |
| { |
| if (y < 0) { |
| throw new IAE( |
| "Function[%s] needs a postive integer as second argument", |
| name() |
| ); |
| } |
| if (x == null) { |
| return ExprEval.of(null); |
| } |
| int len = x.length(); |
| return ExprEval.of(y < len ? x.substring(len - y) : x); |
| } |
| } |
| |
| class LeftFunc extends StringLongFunction |
| { |
| @Override |
| public String name() |
| { |
| return "left"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| protected ExprEval eval(@Nullable String x, int y) |
| { |
| if (y < 0) { |
| throw new IAE( |
| "Function[%s] needs a postive integer as second argument", |
| name() |
| ); |
| } |
| if (x == null) { |
| return ExprEval.of(null); |
| } |
| return ExprEval.of(y < x.length() ? x.substring(0, y) : x); |
| } |
| } |
| |
| class ReplaceFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "replace"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String arg = args.get(0).eval(bindings).asString(); |
| final String pattern = NullHandling.nullToEmptyIfNeeded(args.get(1).eval(bindings).asString()); |
| final String replacement = NullHandling.nullToEmptyIfNeeded(args.get(2).eval(bindings).asString()); |
| if (arg == null) { |
| return ExprEval.of(NullHandling.defaultStringValue()); |
| } |
| return ExprEval.of(StringUtils.replace(arg, pattern, replacement)); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class LowerFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "lower"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String arg = args.get(0).eval(bindings).asString(); |
| if (arg == null) { |
| return ExprEval.of(NullHandling.defaultStringValue()); |
| } |
| return ExprEval.of(StringUtils.toLowerCase(arg)); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class UpperFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "upper"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final String arg = args.get(0).eval(bindings).asString(); |
| if (arg == null) { |
| return ExprEval.of(NullHandling.defaultStringValue()); |
| } |
| return ExprEval.of(StringUtils.toUpperCase(arg)); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class ReverseFunc extends UnivariateFunction |
| { |
| @Override |
| public String name() |
| { |
| return "reverse"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| protected ExprEval eval(ExprEval param) |
| { |
| if (param.type() != ExprType.STRING) { |
| throw new IAE( |
| "Function[%s] needs a string argument", |
| name() |
| ); |
| } |
| final String arg = param.asString(); |
| return ExprEval.of(arg == null ? NullHandling.defaultStringValue() : new StringBuilder(arg).reverse().toString()); |
| } |
| } |
| |
| class RepeatFunc extends StringLongFunction |
| { |
| @Override |
| public String name() |
| { |
| return "repeat"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| protected ExprEval eval(String x, int y) |
| { |
| if (x == null) { |
| return ExprEval.of(null); |
| } |
| return ExprEval.of(y < 1 ? NullHandling.defaultStringValue() : StringUtils.repeat(x, y)); |
| } |
| } |
| |
| class LpadFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "lpad"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| String base = args.get(0).eval(bindings).asString(); |
| int len = args.get(1).eval(bindings).asInt(); |
| String pad = args.get(2).eval(bindings).asString(); |
| |
| if (base == null || pad == null) { |
| return ExprEval.of(null); |
| } else { |
| return ExprEval.of(len == 0 ? NullHandling.defaultStringValue() : StringUtils.lpad(base, len, pad)); |
| } |
| |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class RpadFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "rpad"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| String base = args.get(0).eval(bindings).asString(); |
| int len = args.get(1).eval(bindings).asInt(); |
| String pad = args.get(2).eval(bindings).asString(); |
| |
| if (base == null || pad == null) { |
| return ExprEval.of(null); |
| } else { |
| return ExprEval.of(len == 0 ? NullHandling.defaultStringValue() : StringUtils.rpad(base, len, pad)); |
| } |
| |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class TimestampFromEpochFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "timestamp"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| ExprEval value = args.get(0).eval(bindings); |
| if (value.type() != ExprType.STRING) { |
| throw new IAE("first argument should be string type but got %s type", value.type()); |
| } |
| |
| DateTimes.UtcFormatter formatter = DateTimes.ISO_DATE_OPTIONAL_TIME; |
| if (args.size() > 1) { |
| ExprEval format = args.get(1).eval(bindings); |
| if (format.type() != ExprType.STRING) { |
| throw new IAE("second argument should be string type but got %s type", format.type()); |
| } |
| formatter = DateTimes.wrapFormatter(DateTimeFormat.forPattern(format.asString())); |
| } |
| DateTime date; |
| try { |
| date = formatter.parse(value.asString()); |
| } |
| catch (IllegalArgumentException e) { |
| throw new IAE(e, "invalid value %s", value.asString()); |
| } |
| return toValue(date); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1 && args.size() != 2) { |
| throw new IAE("Function[%s] needs 1 or 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| protected ExprEval toValue(DateTime date) |
| { |
| return ExprEval.of(date.getMillis()); |
| } |
| } |
| |
| class UnixTimestampFunc extends TimestampFromEpochFunc |
| { |
| @Override |
| public String name() |
| { |
| return "unix_timestamp"; |
| } |
| |
| @Override |
| protected final ExprEval toValue(DateTime date) |
| { |
| return ExprEval.of(date.getMillis() / 1000); |
| } |
| } |
| |
| class SubMonthFunc implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "subtract_months"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| Long left = args.get(0).eval(bindings).asLong(); |
| Long right = args.get(1).eval(bindings).asLong(); |
| DateTimeZone timeZone = DateTimes.inferTzFromString(args.get(2).eval(bindings).asString()); |
| |
| if (left == null || right == null) { |
| return ExprEval.of(null); |
| } else { |
| return ExprEval.of(DateTimes.subMonths(right, left, timeZone)); |
| } |
| |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 3) { |
| throw new IAE("Function[%s] needs 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| } |
| |
| class ArrayConstructorFunction implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "array"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| // this is copied from 'BaseMapFunction.applyMap', need to find a better way to consolidate, or construct arrays, |
| // or.. something... |
| final int length = args.size(); |
| String[] stringsOut = null; |
| Long[] longsOut = null; |
| Double[] doublesOut = null; |
| |
| ExprType elementType = null; |
| for (int i = 0; i < length; i++) { |
| ExprEval<?> evaluated = args.get(i).eval(bindings); |
| if (elementType == null) { |
| elementType = evaluated.type(); |
| switch (elementType) { |
| case STRING: |
| stringsOut = new String[length]; |
| break; |
| case LONG: |
| longsOut = new Long[length]; |
| break; |
| case DOUBLE: |
| doublesOut = new Double[length]; |
| break; |
| default: |
| throw new RE("Unhandled array constructor element type [%s]", elementType); |
| } |
| } |
| |
| setArrayOutputElement(stringsOut, longsOut, doublesOut, elementType, i, evaluated); |
| } |
| |
| // There should be always at least one argument and thus elementType is never null. |
| // See validateArguments(). |
| //noinspection ConstantConditions |
| switch (elementType) { |
| case STRING: |
| return ExprEval.ofStringArray(stringsOut); |
| case LONG: |
| return ExprEval.ofLongArray(longsOut); |
| case DOUBLE: |
| return ExprEval.ofDoubleArray(doublesOut); |
| default: |
| throw new RE("Unhandled array constructor element type [%s]", elementType); |
| } |
| } |
| |
| static void setArrayOutputElement( |
| String[] stringsOut, |
| Long[] longsOut, |
| Double[] doublesOut, |
| ExprType elementType, |
| int i, |
| ExprEval evaluated |
| ) |
| { |
| switch (elementType) { |
| case STRING: |
| stringsOut[i] = evaluated.asString(); |
| break; |
| case LONG: |
| longsOut[i] = evaluated.isNumericNull() ? null : evaluated.asLong(); |
| break; |
| case DOUBLE: |
| doublesOut[i] = evaluated.isNumericNull() ? null : evaluated.asDouble(); |
| break; |
| } |
| } |
| |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return ImmutableSet.copyOf(args); |
| } |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.isEmpty()) { |
| throw new IAE("Function[%s] needs at least 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| ExprType type = ExprType.LONG; |
| for (Expr arg : args) { |
| type = ExprTypeConversion.function(type, arg.getOutputType(inspector)); |
| } |
| return ExprType.asArrayType(type); |
| } |
| } |
| |
| class ArrayLengthFunction implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "array_length"; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval expr = args.get(0).eval(bindings); |
| final Object[] array = expr.asArray(); |
| if (array == null) { |
| return ExprEval.of(null); |
| } |
| |
| return ExprEval.ofLong(array.length); |
| } |
| |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| return ImmutableSet.of(args.get(0)); |
| } |
| |
| @Override |
| public boolean hasArrayInputs() |
| { |
| return true; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 1) { |
| throw new IAE("Function[%s] needs 1 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return Collections.emptySet(); |
| } |
| } |
| |
| class StringToArrayFunction implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "string_to_array"; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2) { |
| throw new IAE("Function[%s] needs 2 argument", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING_ARRAY; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval expr = args.get(0).eval(bindings); |
| final String arrayString = expr.asString(); |
| if (arrayString == null) { |
| return ExprEval.of(null); |
| } |
| |
| final String split = args.get(1).eval(bindings).asString(); |
| return ExprEval.ofStringArray(arrayString.split(split != null ? split : "")); |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| return ImmutableSet.copyOf(args); |
| } |
| |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| } |
| |
| class ArrayToStringFunction extends ArrayScalarFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_to_string"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| final String join = scalarExpr.asString(); |
| final Object[] raw = arrayExpr.asArray(); |
| if (raw == null || raw.length == 1 && raw[0] == null) { |
| return ExprEval.of(null); |
| } |
| return ExprEval.of( |
| Arrays.stream(raw).map(String::valueOf).collect(Collectors.joining(join != null ? join : "")) |
| ); |
| } |
| } |
| |
| class ArrayOffsetFunction extends ArrayScalarFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_offset"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.elementType(args.get(0).getOutputType(inspector)); |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| final Object[] array = arrayExpr.asArray(); |
| final int position = scalarExpr.asInt(); |
| |
| if (array.length > position) { |
| return ExprEval.bestEffortOf(array[position]); |
| } |
| return ExprEval.of(null); |
| } |
| } |
| |
| class ArrayOrdinalFunction extends ArrayScalarFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_ordinal"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.elementType(args.get(0).getOutputType(inspector)); |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| final Object[] array = arrayExpr.asArray(); |
| final int position = scalarExpr.asInt() - 1; |
| |
| if (array.length > position) { |
| return ExprEval.bestEffortOf(array[position]); |
| } |
| return ExprEval.of(null); |
| } |
| } |
| |
| class ArrayOffsetOfFunction extends ArrayScalarFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_offset_of"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| final Object[] array = arrayExpr.asArray(); |
| |
| switch (scalarExpr.type()) { |
| case STRING: |
| case LONG: |
| case DOUBLE: |
| int index = -1; |
| for (int i = 0; i < array.length; i++) { |
| if (Objects.equals(array[i], scalarExpr.value())) { |
| index = i; |
| break; |
| } |
| } |
| return index < 0 ? ExprEval.ofLong(NullHandling.replaceWithDefault() ? -1 : null) : ExprEval.ofLong(index); |
| default: |
| throw new IAE("Function[%s] 2nd argument must be a a scalar type", name()); |
| } |
| } |
| } |
| |
| class ArrayOrdinalOfFunction extends ArrayScalarFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_ordinal_of"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval arrayExpr, ExprEval scalarExpr) |
| { |
| final Object[] array = arrayExpr.asArray(); |
| switch (scalarExpr.type()) { |
| case STRING: |
| case LONG: |
| case DOUBLE: |
| int index = -1; |
| for (int i = 0; i < array.length; i++) { |
| if (Objects.equals(array[i], scalarExpr.value())) { |
| index = i; |
| break; |
| } |
| } |
| return index < 0 ? ExprEval.ofLong(NullHandling.replaceWithDefault() ? -1 : null) : ExprEval.ofLong(index + 1); |
| default: |
| throw new IAE("Function[%s] 2nd argument must be a a scalar type", name()); |
| } |
| } |
| } |
| |
| class ArrayAppendFunction extends ArrayAddElementFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_append"; |
| } |
| |
| @Override |
| <T> Stream<T> add(T[] array, @Nullable T val) |
| { |
| List<T> l = new ArrayList<>(Arrays.asList(array)); |
| l.add(val); |
| return l.stream(); |
| } |
| } |
| |
| class ArrayPrependFunction extends ArrayAddElementFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_prepend"; |
| } |
| |
| @Override |
| Expr getScalarArgument(List<Expr> args) |
| { |
| return args.get(0); |
| } |
| |
| @Override |
| Expr getArrayArgument(List<Expr> args) |
| { |
| return args.get(1); |
| } |
| |
| @Override |
| <T> Stream<T> add(T[] array, @Nullable T val) |
| { |
| List<T> l = new ArrayList<>(Arrays.asList(array)); |
| l.add(0, val); |
| return l.stream(); |
| } |
| } |
| |
| class ArrayConcatFunction extends ArraysMergeFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_concat"; |
| } |
| |
| @Override |
| <T> Stream<T> merge(T[] array1, T[] array2) |
| { |
| List<T> l = new ArrayList<>(Arrays.asList(array1)); |
| l.addAll(Arrays.asList(array2)); |
| return l.stream(); |
| } |
| } |
| |
| class ArraySetAddFunction extends ArrayAddElementFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_set_add"; |
| } |
| |
| @Override |
| <T> Stream<T> add(T[] array, @Nullable T val) |
| { |
| Set<T> l = new HashSet<>(Arrays.asList(array)); |
| l.add(val); |
| return l.stream(); |
| } |
| } |
| |
| class ArraySetAddAllFunction extends ArraysMergeFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_set_add_all"; |
| } |
| |
| @Override |
| <T> Stream<T> merge(T[] array1, T[] array2) |
| { |
| Set<T> l = new HashSet<>(Arrays.asList(array1)); |
| l.addAll(Arrays.asList(array2)); |
| return l.stream(); |
| } |
| } |
| |
| class ArrayContainsFunction extends ArraysFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_contains"; |
| } |
| |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr) |
| { |
| final Object[] array1 = lhsExpr.asArray(); |
| final Object[] array2 = rhsExpr.asArray(); |
| return ExprEval.ofLongBoolean(Arrays.asList(array1).containsAll(Arrays.asList(array2))); |
| } |
| } |
| |
| class ArrayOverlapFunction extends ArraysFunction |
| { |
| @Override |
| public String name() |
| { |
| return "array_overlap"; |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return ExprType.LONG; |
| } |
| |
| @Override |
| ExprEval doApply(ExprEval lhsExpr, ExprEval rhsExpr) |
| { |
| final Object[] array1 = lhsExpr.asArray(); |
| final List<Object> array2 = Arrays.asList(rhsExpr.asArray()); |
| boolean any = false; |
| for (Object check : array1) { |
| any |= array2.contains(check); |
| } |
| return ExprEval.ofLongBoolean(any); |
| } |
| } |
| |
| class ArraySliceFunction implements Function |
| { |
| @Override |
| public String name() |
| { |
| return "array_slice"; |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() != 2 && args.size() != 3) { |
| throw new IAE("Function[%s] needs 2 or 3 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inspector, List<Expr> args) |
| { |
| return args.get(0).getOutputType(inspector); |
| } |
| |
| @Override |
| public Set<Expr> getScalarInputs(List<Expr> args) |
| { |
| if (args.size() == 3) { |
| return ImmutableSet.of(args.get(1), args.get(2)); |
| } else { |
| return ImmutableSet.of(args.get(1)); |
| } |
| } |
| |
| @Override |
| public Set<Expr> getArrayInputs(List<Expr> args) |
| { |
| return ImmutableSet.of(args.get(0)); |
| } |
| |
| @Override |
| public boolean hasArrayInputs() |
| { |
| return true; |
| } |
| |
| @Override |
| public boolean hasArrayOutput() |
| { |
| return true; |
| } |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval expr = args.get(0).eval(bindings); |
| final Object[] array = expr.asArray(); |
| if (array == null) { |
| return ExprEval.of(null); |
| } |
| |
| final int start = args.get(1).eval(bindings).asInt(); |
| int end = array.length; |
| if (args.size() == 3) { |
| end = args.get(2).eval(bindings).asInt(); |
| } |
| |
| if (start < 0 || start > array.length || start > end) { |
| // Arrays.copyOfRange will throw exception in these cases |
| return ExprEval.of(null); |
| } |
| |
| switch (expr.type()) { |
| case STRING: |
| case STRING_ARRAY: |
| return ExprEval.ofStringArray(Arrays.copyOfRange(expr.asStringArray(), start, end)); |
| case LONG: |
| case LONG_ARRAY: |
| return ExprEval.ofLongArray(Arrays.copyOfRange(expr.asLongArray(), start, end)); |
| case DOUBLE: |
| case DOUBLE_ARRAY: |
| return ExprEval.ofDoubleArray(Arrays.copyOfRange(expr.asDoubleArray(), start, end)); |
| } |
| throw new RE("Unable to slice to unknown type %s", expr.type()); |
| } |
| } |
| |
| abstract class SizeFormatFunc implements Function |
| { |
| protected abstract HumanReadableBytes.UnitSystem getUnitSystem(); |
| |
| @Override |
| public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings) |
| { |
| final ExprEval valueParam = args.get(0).eval(bindings); |
| if (NullHandling.sqlCompatible() && valueParam.isNumericNull()) { |
| return ExprEval.of(null); |
| } |
| |
| /** |
| * only LONG and DOUBLE are allowed |
| * For a DOUBLE, it will be cast to LONG before format |
| */ |
| if (valueParam.value() != null && valueParam.type() != ExprType.LONG && valueParam.type() != ExprType.DOUBLE) { |
| throw new IAE("Function[%s] needs a number as its first argument", name()); |
| } |
| |
| /** |
| * By default, precision is 2 |
| */ |
| long precision = 2; |
| if (args.size() > 1) { |
| ExprEval precisionParam = args.get(1).eval(bindings); |
| if (precisionParam.type() != ExprType.LONG) { |
| throw new IAE("Function[%s] needs an integer as its second argument", name()); |
| } |
| precision = precisionParam.asLong(); |
| if (precision < 0 || precision > 3) { |
| throw new IAE("Given precision[%d] of Function[%s] must be in the range of [0,3]", precision, name()); |
| } |
| } |
| |
| return ExprEval.of(HumanReadableBytes.format(valueParam.asLong(), precision, this.getUnitSystem())); |
| } |
| |
| @Override |
| public void validateArguments(List<Expr> args) |
| { |
| if (args.size() < 1 || args.size() > 2) { |
| throw new IAE("Function[%s] needs 1 or 2 arguments", name()); |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ExprType getOutputType(Expr.InputBindingInspector inputTypes, List<Expr> args) |
| { |
| return ExprType.STRING; |
| } |
| } |
| |
| class HumanReadableDecimalByteFormatFunc extends SizeFormatFunc |
| { |
| @Override |
| public String name() |
| { |
| return "human_readable_decimal_byte_format"; |
| } |
| |
| @Override |
| protected HumanReadableBytes.UnitSystem getUnitSystem() |
| { |
| return HumanReadableBytes.UnitSystem.DECIMAL_BYTE; |
| } |
| } |
| |
| class HumanReadableBinaryByteFormatFunc extends SizeFormatFunc |
| { |
| @Override |
| public String name() |
| { |
| return "human_readable_binary_byte_format"; |
| } |
| |
| @Override |
| protected HumanReadableBytes.UnitSystem getUnitSystem() |
| { |
| return HumanReadableBytes.UnitSystem.BINARY_BYTE; |
| } |
| } |
| |
| class HumanReadableDecimalFormatFunc extends SizeFormatFunc |
| { |
| @Override |
| public String name() |
| { |
| return "human_readable_decimal_format"; |
| } |
| |
| @Override |
| protected HumanReadableBytes.UnitSystem getUnitSystem() |
| { |
| return HumanReadableBytes.UnitSystem.DECIMAL; |
| } |
| } |
| } |